Source code for fieldview.data

"""Dataset loading, access, and query helpers for FieldView.

The :mod:`fieldview.data` namespace covers the full dataset lifecycle: loading
supported file formats, selecting or discovering the current dataset, probing
values, accessing registered scalar/vector/position arrays, working with
transient data, and retrieving session or window metadata tied to the active
scene.

Typical usage:
    ds = fv.data.load_plot3d("grid.g", "sol.q")
"""

from __future__ import annotations

import json
import importlib
import os
from array import array
from dataclasses import dataclass
from enum import Enum
from typing import (
    NamedTuple,
    Protocol,
    SupportsFloat,
    SupportsIndex,
    SupportsInt,
    cast,
)
from collections.abc import Callable, Iterable, Iterator, Mapping

from . import constant
from . import _core as _core_module
from ._core import Dataset as _CoreDataset
from ._core_utils import _core_call
from .exceptions import (
    CoreError,
    InvalidArgumentError,
    InvalidDatasetError,
    _send_fv_error_msg,
)
from .utils import Range, _coerce_pathlike_str, _coerce_xyz_triplet

DatasetArraySnapshotView = _core_module.DatasetArraySnapshotView

__all__: list[str] = [
    "DuplicationOptions",
    "Dataset",
    "get_current",
    "set_current",
    "load_plot3d",
    "load_overflow2",
    "load_fvuns",
    "load_vtk_structured",
    "load_acusolve_direct",
    "load_acusolve_fvuns",
    "load_ansys_cfx_fvuns",
    "load_fluent_cff_direct",
    "load_append_sampled_data",
    "load_pw_common",
    "load_cfdpp_fvuns",
    "load_cgns_structured",
    "load_cgns_unstructured",
    "load_cgns_unstructured_hybrid",
    "load_cobalt_fvuns",
    "load_converge_fvuns",
    "load_ensight",
    "load_flow3d_animation",
    "load_flow3d_restart",
    "load_fluent_fvuns",
    "load_fluent_unstructured",
    "load_fluent_cas_dat_direct",
    "load_fluent_direct",
    "load_havoc",
    "load_lsdyna_d3plot",
    "load_lsdyna_state",
    "load_nparc_wind",
    "load_openfoam_direct",
    "load_openfoam_fvuns",
    "load_sc_tetra",
    "load_sc_flow",
    "load_scryu",
    "load_sc_stream",
    "load_starccm_fvuns",
    "load_starcd_fvuns",
    "load_stl",
    "load_surface_sampled_data",
    "load_tecplot_360",
    "load_ultrafluidx_direct",
    "load_vtk_unstructured_hybrid",
    "load_wind_structured",
    "load_wind_unstructured",
    "load_xdb_import",
    "reader_aliases",
    "dataset",
    "Vec3",
    "ScalarProbe",
    "VectorProbe",
    "ProbeResult",
    "DatasetArraySnapshotView",
    "ScalarArrayRef",
    "ScalarRegistry",
    "probe",
    "probe_ijk",
    "TransientInfo",
    "IntegrationResult",
    "DatasetBounds",
    "WindowInfo",
    "WindowList",
    "WindowSplitResult",
    "CameraPose",
    "CameraExactState",
    "ViewState",
    "CameraState",
    "VectorArrayRef",
    "VectorRegistry",
    "PerspectiveState",
    "SessionState",
    "get_session_state",
]


_current_dataset: Dataset | None = None
_dataset_registry: dict[int, "Dataset"] = {}

_IntLike = SupportsInt | SupportsIndex | str | bytes
_FloatLike = SupportsFloat | SupportsIndex | str | bytes
_PathLikeStr = str | os.PathLike[str]


class _NumpyArrayLike(Protocol):
    ndim: int
    shape: tuple[int, ...]

    def copy(self) -> object: ...

    def setflags(self, write: bool = ...) -> None: ...


class _NumpyModule(Protocol):
    float32: object

    def asarray(self, values: object, dtype: object = ...) -> _NumpyArrayLike: ...

    def ascontiguousarray(self, values: object, dtype: object = ...) -> object: ...


class _ReaderLoader(Protocol):
    def load(self, **kwargs: object) -> Dataset: ...


class _ReaderWithServerConfig(Protocol):
    def server_config(self, server_config: object) -> None: ...


def _load_numpy() -> _NumpyModule | None:
    try:
        module = importlib.import_module("numpy")
        return cast(_NumpyModule, cast(object, module))
    except ImportError:
        return None


def _core_getattr(obj: object, name: str) -> object:
    return cast(object, _core_call(getattr, obj, name))


def _core_setattr(obj: object, name: str, value: object) -> None:
    _core_call(setattr, obj, name, value)


def _core_getstr(obj: object, name: str) -> str:
    return str(_core_getattr(obj, name))


def _core_getbool(obj: object, name: str) -> bool:
    return bool(_core_getattr(obj, name))


def _core_getint(obj: object, name: str) -> int:
    return int(cast(_IntLike, _core_getattr(obj, name)))


def _core_getfloat(obj: object, name: str) -> float:
    return float(cast(_FloatLike, _core_getattr(obj, name)))


def _to_int(value: object) -> int:
    return int(cast(_IntLike, value))


def _to_float(value: object) -> float:
    return float(cast(_FloatLike, value))


class DuplicationOptions:
    """Options for dataset duplication operations.

    Args:
        operation: One of "none", "mirror", "rotate", or "translate".
        mirror_axes: constant.Axes to mirror, e.g. ``constant.Axes.XY``.
        rotate_axis: constant.Axis to rotate around, e.g. ``constant.Axis.Z``.
        rotate_copies: Number of copies for rotate. Default 2.
        rotate_sweep: Sweep angle for rotate, in degrees. Default 360.0.
        translate_axes: constant.Axes to translate, e.g. ``constant.Axes.XYZ``.
        translate_copies: Tuple of (x, y, z) copies.
        translate_deltas: Tuple of (dx, dy, dz) offsets.
    """

    __slots__ = (
        "operation",
        "mirror_axes",
        "rotate_axis",
        "rotate_copies",
        "rotate_sweep",
        "translate_axes",
        "translate_copies_x",
        "translate_copies_y",
        "translate_copies_z",
        "translate_delta_x",
        "translate_delta_y",
        "translate_delta_z",
    )

    def __init__(
        self,
        operation: str | constant.DuplicateOperation = constant.DuplicateOperation.NONE,
        mirror_axes: str | constant.Axes | None = None,
        rotate_axis: str | constant.Axis | None = None,
        rotate_copies: int | None = None,
        rotate_sweep: float | None = None,
        translate_axes: str | constant.Axes | None = None,
        translate_copies_x: int | None = None,
        translate_copies_y: int | None = None,
        translate_copies_z: int | None = None,
        translate_delta_x: float | None = None,
        translate_delta_y: float | None = None,
        translate_delta_z: float | None = None,
    ):
        if isinstance(operation, constant.DuplicateOperation):
            operation = operation.value
        self.operation = operation
        self.mirror_axes = (
            mirror_axes.value if isinstance(mirror_axes, constant.Axes) else mirror_axes
        )
        self.rotate_axis = (
            rotate_axis.value if isinstance(rotate_axis, constant.Axis) else rotate_axis
        )
        self.rotate_copies = rotate_copies
        self.rotate_sweep = rotate_sweep
        self.translate_axes = (
            translate_axes.value
            if isinstance(translate_axes, constant.Axes)
            else translate_axes
        )
        self.translate_copies_x = translate_copies_x
        self.translate_copies_y = translate_copies_y
        self.translate_copies_z = translate_copies_z
        self.translate_delta_x = translate_delta_x
        self.translate_delta_y = translate_delta_y
        self.translate_delta_z = translate_delta_z

    def to_dict(self) -> dict[str, object]:
        """Return a dict payload for the duplication command."""
        data: dict[str, object] = {"operation": self.operation}
        if self.mirror_axes is not None:
            data["mirror_axes"] = self.mirror_axes
        if self.rotate_axis is not None:
            data["rotate_axis"] = self.rotate_axis
        if self.rotate_copies is not None:
            data["rotate_copies"] = self.rotate_copies
        if self.rotate_sweep is not None:
            data["rotate_sweep"] = self.rotate_sweep
        if self.translate_axes is not None:
            data["translate_axes"] = self.translate_axes
        if self.translate_copies_x is not None:
            data["translate_copies_x"] = self.translate_copies_x
        if self.translate_copies_y is not None:
            data["translate_copies_y"] = self.translate_copies_y
        if self.translate_copies_z is not None:
            data["translate_copies_z"] = self.translate_copies_z
        if self.translate_delta_x is not None:
            data["translate_delta_x"] = self.translate_delta_x
        if self.translate_delta_y is not None:
            data["translate_delta_y"] = self.translate_delta_y
        if self.translate_delta_z is not None:
            data["translate_delta_z"] = self.translate_delta_z
        return data


class Vec3(NamedTuple):
    """Three-component point or vector."""

    x: float
    y: float
    z: float


class ScalarProbe(NamedTuple):
    """Function name and scalar value pair."""

    func: str | None
    value: float | None


class VectorProbe(NamedTuple):
    """Function name and vector value pair."""

    func: str | None
    value: Vec3 | None


[docs] @dataclass(frozen=True) class ProbeResult: """Probe result for probed scalar/iso/threshold/vector functions. ``hit`` reports whether the probe resolved to a dataset location. When ``hit`` is ``False``, function names may still be populated while sampled values remain ``None``. For IJK misses, ``point`` may also be ``None`` because no displayed-space location was resolved. """ hit: bool point: Vec3 | None region: int | None grid_index: int | None grid: int | None ijk: tuple[float, float, float] | None scalar: ScalarProbe iso: ScalarProbe threshold: ScalarProbe vector: VectorProbe
[docs] @dataclass(frozen=True) class IntegrationResult: """Surface integration result. Instances are returned by ``surface.integrate()`` and the coord/iso ``integrate_partial_surface()`` methods. Common fields: - ``integral_type`` identifies the host-side integration mode. - ``scalar_function`` is the scalar function that was integrated. - ``area`` is the surface integral ∫ f dA of the active scalar. - ``sum`` is the total integral of the active scalar over the surface. - ``average`` is the area-weighted mean, equal to ``sum / area`` when the host provides it. When the target surface has normals and an active vector function, ``has_surface_normals`` is ``True`` and the normal/vector flux fields such as ``sum_nx`` and ``sum_v_dot_n`` may also be populated. Example: .. code-block:: python >>> import os >>> import fieldview as fv >>> data_dir = os.path.join(fv.home, "examples", "f18") >>> ds = fv.data.load_plot3d( ... os.path.join(data_dir, "f18i9b_g_bin"), ... os.path.join(data_dir, "f18i9b_q_bin"), ... ) >>> surf = fv.vis.create_boundary(ds) >>> surf.scalar_func = "Pressure [PLOT3D]" >>> result = surf.integrate() >>> result.area, result.sum, result.average """ integral_type: str scalar_function: str | None area: float sum: float average: float | None surface: str | None = None has_surface_normals: bool = False vector_function: str | None = None sum_nx: float | None = None sum_ny: float | None = None sum_nz: float | None = None sum_v_dot_n: float | None = None
def _coerce_scalar_values_buffer(values: object) -> object: np = _load_numpy() try: if np is not None: arr = np.asarray(values, dtype=np.float32) if arr.ndim != 1: raise InvalidArgumentError("Scalar values must be one-dimensional.") return np.ascontiguousarray(arr, dtype=np.float32) if isinstance(values, array): if values.typecode == "f": return values return array("f", values) return array( "f", [float(cast(_FloatLike, item)) for item in cast(Iterable[object], values)], ) except InvalidArgumentError: raise except (TypeError, ValueError) as exc: raise InvalidArgumentError( "Scalar values must be a one-dimensional array-like of floats." ) from exc def _coerce_vector_values_buffer(values: object) -> object: np = _load_numpy() try: if np is not None: arr = np.asarray(values, dtype=np.float32) if arr.ndim != 2 or arr.shape[1] != 3: raise InvalidArgumentError("Vector values must have shape (n, 3).") return np.ascontiguousarray(arr, dtype=np.float32) rows: list[tuple[float, float, float]] = [] for row in cast(Iterable[object], values): if not isinstance(row, (list, tuple)): raise InvalidArgumentError("Vector values must have shape (n, 3).") components = tuple(cast(tuple[object, ...], row)) if len(components) != 3: raise InvalidArgumentError("Vector values must have shape (n, 3).") rows.append( ( _to_float(components[0]), _to_float(components[1]), _to_float(components[2]), ) ) if not rows: raise InvalidArgumentError("Vector values must have shape (n, 3).") flat = array("f") for row in rows: flat.extend(row) return flat except InvalidArgumentError: raise except (TypeError, ValueError) as exc: raise InvalidArgumentError( "Vector values must be an array-like of shape (n, 3)." ) from exc def _extract_created_function_id(payload: Mapping[str, object], kind: str) -> int: function_id_value = payload.get("function_id") if function_id_value is None: raise CoreError( f"FieldView did not return 'function_id' when creating the {kind} function." ) try: function_id = int(cast(_IntLike, function_id_value)) except (TypeError, ValueError) as exc: raise CoreError( f"FieldView returned a non-integer 'function_id' for the created {kind} function." ) from exc if function_id < 0: raise CoreError( f"FieldView returned an invalid 'function_id' for the created {kind} function." ) return function_id
[docs] class ScalarArrayRef: """Reference to a named scalar function within a dataset.""" __slots__ = ("_dataset", "_name", "_function_id") def __init__(self, dataset: "Dataset", name: str, function_id: int): self._dataset = dataset self._name = name self._function_id = int(function_id) @property def name(self) -> str: return self._name @property def function_id(self) -> int: return self._function_id
[docs] def snapshot(self, *, grid: int = 1) -> DatasetArraySnapshotView: """Return a read-only snapshot view for this scalar on ``grid``. If you convert the snapshot with :func:`numpy.asarray`, the resulting NumPy view keeps the snapshot backing alive even if the local ``snap`` variable is deleted. """ self._dataset._ensure_valid() return _core_call( self._dataset._core._scalar_array_snapshot, self._function_id, int(grid) )
[docs] def to_numpy(self, *, grid: int = 1, copy: bool = False) -> object: """Return this scalar snapshot as a NumPy array. Args: grid: 1-based grid number. copy: When ``False`` (default), return a read-only NumPy view over the snapshot buffer when possible. When ``True``, return an independent writable NumPy copy. Notes: With ``copy=False``, the returned NumPy array keeps the snapshot backing alive, so the array remains valid even if the local ``snap`` variable goes out of scope. """ np = _load_numpy() if np is None: raise CoreError( "NumPy is not installed; use snapshot() for a buffer-compatible view." ) snap = self.snapshot(grid=grid) arr = np.asarray(snap) if copy: return arr.copy() arr.setflags(write=False) return arr
def __repr__(self) -> str: return f"ScalarArrayRef(name={self._name!r}, function_id={self._function_id})"
[docs] class VectorArrayRef: """Reference to a named vector function within a dataset.""" __slots__ = ("_dataset", "_name", "_function_id") def __init__(self, dataset: "Dataset", name: str, function_id: int): self._dataset = dataset self._name = name self._function_id = int(function_id) @property def name(self) -> str: return self._name @property def function_id(self) -> int: return self._function_id
[docs] def snapshot(self, *, grid: int = 1) -> DatasetArraySnapshotView: """Return a read-only snapshot view for this vector on ``grid``. If you convert the snapshot with :func:`numpy.asarray`, the resulting NumPy view keeps the snapshot backing alive even if the local ``snap`` variable is deleted. """ self._dataset._ensure_valid() return _core_call( self._dataset._core._vector_array_snapshot, self._function_id, int(grid) )
[docs] def to_numpy(self, *, grid: int = 1, copy: bool = False) -> object: """Return this vector snapshot as a NumPy array. Args: grid: 1-based grid number. copy: When ``False`` (default), return a read-only NumPy view over the snapshot buffer when possible. When ``True``, return an independent writable NumPy copy. Notes: With ``copy=False``, the returned NumPy array keeps the snapshot backing alive, so the array remains valid even if the local ``snap`` variable goes out of scope. """ np = _load_numpy() if np is None: raise CoreError( "NumPy is not installed; use snapshot() for a buffer-compatible view." ) snap = self.snapshot(grid=grid) arr = np.asarray(snap) if copy: return arr.copy() arr.setflags(write=False) return arr
def __repr__(self) -> str: return f"VectorArrayRef(name={self._name!r}, function_id={self._function_id})"
[docs] class ScalarRegistry: """Container-like access to dataset scalar functions.""" __slots__ = ("_dataset",) def __init__(self, dataset: "Dataset"): self._dataset = dataset def __contains__(self, name: object) -> bool: return isinstance(name, str) and name in self._dataset._scalar_function_ids def __getitem__(self, name: str) -> ScalarArrayRef: self._dataset._ensure_valid() function_id = self._dataset._scalar_function_ids.get(name) if function_id is None: raise KeyError(name) return ScalarArrayRef(self._dataset, name, function_id) def __iter__(self) -> Iterator[str]: self._dataset._ensure_valid() return iter(self._dataset._scalar_functions) def __len__(self) -> int: self._dataset._ensure_valid() return len(self._dataset._scalar_functions)
[docs] def snapshot(self, name: str, *, grid: int = 1) -> DatasetArraySnapshotView: """Return a read-only snapshot view for the named scalar function.""" return self[name].snapshot(grid=grid)
[docs] def to_numpy(self, name: str, *, grid: int = 1, copy: bool = False) -> object: """Return the named scalar function as a NumPy array. Args: name: Scalar function name. grid: 1-based grid number. copy: When ``False`` (default), return a read-only NumPy view over the snapshot buffer when possible. When ``True``, return an independent writable NumPy copy. """ return self[name].to_numpy(grid=grid, copy=copy)
[docs] def create(self, name: str, values: object, *, grid: int = 1) -> ScalarArrayRef: """Create or replace a named scalar function from array-like values.""" if not isinstance(name, str) or not name: raise InvalidArgumentError("Scalar name must be a non-empty string.") buffer_values = _coerce_scalar_values_buffer(values) payload = _core_call( self._dataset._core._create_scalar_array, name, int(grid), buffer_values ) _refresh_function_lists(self._dataset) function_id = _extract_created_function_id(payload, "scalar") return ScalarArrayRef(self._dataset, name, function_id)
[docs] class VectorRegistry: """Container-like access to dataset vector functions.""" __slots__ = ("_dataset",) def __init__(self, dataset: "Dataset"): self._dataset = dataset def __contains__(self, name: object) -> bool: return isinstance(name, str) and name in self._dataset._vector_function_ids def __getitem__(self, name: str) -> VectorArrayRef: self._dataset._ensure_valid() function_id = self._dataset._vector_function_ids.get(name) if function_id is None: raise KeyError(name) return VectorArrayRef(self._dataset, name, function_id) def __iter__(self) -> Iterator[str]: self._dataset._ensure_valid() return iter(self._dataset._vector_functions) def __len__(self) -> int: self._dataset._ensure_valid() return len(self._dataset._vector_functions)
[docs] def snapshot(self, name: str, *, grid: int = 1) -> DatasetArraySnapshotView: """Return a read-only snapshot view for the named vector function.""" return self[name].snapshot(grid=grid)
[docs] def to_numpy(self, name: str, *, grid: int = 1, copy: bool = False) -> object: """Return the named vector function as a NumPy array. Args: name: Vector function name. grid: 1-based grid number. copy: When ``False`` (default), return a read-only NumPy view over the snapshot buffer when possible. When ``True``, return an independent writable NumPy copy. """ return self[name].to_numpy(grid=grid, copy=copy)
[docs] def create(self, name: str, values: object, *, grid: int = 1) -> VectorArrayRef: """Create or replace a named vector function from array-like values.""" if not isinstance(name, str) or not name: raise InvalidArgumentError("Vector name must be a non-empty string.") buffer_values = _coerce_vector_values_buffer(values) payload = _core_call( self._dataset._core._create_vector_array, name, int(grid), buffer_values ) _refresh_function_lists(self._dataset) function_id = _extract_created_function_id(payload, "vector") return VectorArrayRef(self._dataset, name, function_id)
[docs] class PositionRegistry: """Access to native XYZ node positions for dataset grids. This registry is grid-oriented rather than function-oriented. Reads are snapshot-only and return ``(n, 3)`` XYZ data for the requested grid. """ __slots__ = ("_dataset",) def __init__(self, dataset: "Dataset"): self._dataset = dataset
[docs] def snapshot(self, *, grid: int = 1) -> DatasetArraySnapshotView: """Return a read-only snapshot view of native XYZ positions on ``grid``. If you convert the snapshot with :func:`numpy.asarray`, the resulting NumPy view keeps the snapshot backing alive even if the local ``snap`` variable is deleted. """ self._dataset._ensure_valid() return _core_call(self._dataset._core._position_array_snapshot, int(grid))
[docs] def to_numpy(self, *, grid: int = 1, copy: bool = False) -> object: """Return native XYZ positions as a NumPy array. Args: grid: 1-based grid number. copy: When ``False`` (default), return a read-only NumPy view over the snapshot buffer when possible. When ``True``, return an independent writable NumPy copy. Returns: A NumPy array with shape ``(n, 3)`` in native XYZ order. Notes: With ``copy=False``, the returned NumPy array keeps the snapshot backing alive, so the array remains valid even if the local ``snap`` variable goes out of scope. """ np = _load_numpy() if np is None: raise CoreError( "NumPy is not installed; use snapshot() for a buffer-compatible view." ) snap = self.snapshot(grid=grid) arr = np.asarray(snap) if copy: return arr.copy() arr.setflags(write=False) return arr
[docs] @dataclass(frozen=True) class WindowInfo: """Metadata for a single graphics window.""" window: int current: bool scene_index: int parent_window: int | None label: str | None environment: str | None view_sync_enabled: bool background: str | None dataset_ids: list[int] environment_id: int | None = None background_image: str | None = None
[docs] @dataclass(frozen=True) class WindowList: """Window enumeration payload returned by :mod:`fieldview.layout`.""" current_window: int | None windows: list[WindowInfo]
[docs] @dataclass(frozen=True) class WindowSplitResult: """Result metadata for a split-window operation.""" source_window: int new_window: int mode: str orientation: str
[docs] @dataclass(frozen=True) class PerspectiveState: """Perspective state for a graphics window.""" enabled: bool angle: float
@dataclass(frozen=True) class CameraExactState: """Exact FieldView transform state for deterministic camera replay. This stores the raw world/view transform pieces that classic ``.vct`` restore uses internally. It remains available as a low-level payload type, while :class:`ViewState` is the primary deterministic replay object exposed by :mod:`fieldview.camera`. """ zoom: float scale: float rotation_angle: float rotation_axis: tuple[float, float, float] translation: tuple[float, float, float] perspective_z: float rotation_center_on: bool = False rotation_center: tuple[float, float, float] = (0.0, 0.0, 0.0) def to_payload(self) -> dict[str, object]: """Return the host JSON payload used by camera replay.""" return { "zoom": self.zoom, "scale": self.scale, "rotation_angle": self.rotation_angle, "rotation_axis": list(self.rotation_axis), "translation": list(self.translation), "perspective_z": self.perspective_z, "rotation_center_on": self.rotation_center_on, "rotation_center": list(self.rotation_center), }
[docs] @dataclass(frozen=True) class CameraPose: """Human-readable camera pose summary. This describes the visible camera tuple in terms of ``eye``, ``target``, and ``up`` plus the current perspective mode. It is useful for inspection and approximate camera manipulation, but unlike :class:`ViewState` it is not the authoritative deterministic replay format for FieldView views. """ eye: tuple[float, float, float] target: tuple[float, float, float] up: tuple[float, float, float] perspective_enabled: bool perspective_angle: float
CameraState = CameraPose
[docs] @dataclass(frozen=True) class ViewState: """Deterministic FieldView view state. This is the exact replay representation for camera/view restore. It stores the same raw transform pieces that classic ``.vct`` restore uses along with the public perspective settings needed by the host command handler. """ perspective_enabled: bool perspective_angle: float perspective_z: float rotation_angle: float rotation_axis: tuple[float, float, float] translation: tuple[float, float, float] scale: float zoom: float rotation_center_on: bool = False rotation_center: tuple[float, float, float] = (0.0, 0.0, 0.0)
[docs] @classmethod def from_exact( cls, exact_state: CameraExactState, *, perspective_enabled: bool, perspective_angle: float, ) -> ViewState: """Create a deterministic view state from the exact transform payload.""" return cls( perspective_enabled=perspective_enabled, perspective_angle=perspective_angle, perspective_z=exact_state.perspective_z, rotation_angle=exact_state.rotation_angle, rotation_axis=exact_state.rotation_axis, translation=exact_state.translation, scale=exact_state.scale, zoom=exact_state.zoom, rotation_center_on=exact_state.rotation_center_on, rotation_center=exact_state.rotation_center, )
[docs] def to_exact_state(self) -> CameraExactState: """Return the underlying exact transform payload.""" return CameraExactState( zoom=self.zoom, scale=self.scale, rotation_angle=self.rotation_angle, rotation_axis=self.rotation_axis, translation=self.translation, perspective_z=self.perspective_z, rotation_center_on=self.rotation_center_on, rotation_center=self.rotation_center, )
def to_payload(self) -> dict[str, object]: """Return the host JSON payload for deterministic replay.""" payload = self.to_exact_state().to_payload() payload["perspective_enabled"] = self.perspective_enabled payload["perspective_angle"] = self.perspective_angle return payload
[docs] @dataclass(frozen=True) class TransientInfo: """Transient dataset state. Instances are returned by :meth:`Dataset.transient_info`. Example: .. code-block:: python import os import fieldview as fv uns_file = os.path.join(fv.home, "examples", "rectangular_duct", "rect_duct_010.uns") ds = fv.data.load_fvuns(uns_file, transient=True) info = ds.transient_info() print(info.time_step_values[:3]) """ time_step: int solution_time: float total_time_steps: int has_solution_times: bool time_step_range: Range solution_time_range: Range time_step_values: tuple[int, ...] solution_time_values: tuple[float, ...]
def _coerce_enum(value: object) -> object: if isinstance(value, Enum): return cast(object, value.value) return value def _reader_factory(name: str) -> Callable[..., object]: from . import _readers return cast(Callable[..., object], getattr(_readers, name)) def _reader_load(reader: object, **kwargs: object) -> Dataset: return cast(_ReaderLoader, reader).load(**kwargs) def _load_json_dict(value: object, label: str = "JSON payload") -> dict[str, object]: payload = cast(object, json.loads(str(value))) if isinstance(payload, dict): return dict(cast(dict[str, object], payload)) raise CoreError(f"{label} must decode to a JSON object") class DuplicationMirrorConfig: __slots__ = ("_parent", "_axes") def __init__(self, parent: "DuplicationController") -> None: self._parent = parent self._axes: str | None = None @property def axes(self) -> str | None: return self._axes @axes.setter def axes(self, value: object) -> None: coerced = _coerce_enum(value) self._axes = None if coerced is None else str(coerced) self._parent._apply("mirror") class DuplicationRotateConfig: __slots__ = ("_parent", "_axis", "_copies", "_sweep") def __init__(self, parent: "DuplicationController") -> None: self._parent = parent self._axis = constant.Axis.Y.value self._copies = 2 self._sweep = 360.0 @property def axis(self) -> str: return str(self._axis) @axis.setter def axis(self, value: object) -> None: self._axis = str(_coerce_enum(value)) self._parent._apply("rotate") @property def copies(self) -> int: return self._copies @copies.setter def copies(self, value: object) -> None: self._copies = _to_int(value) self._parent._apply("rotate") @property def sweep(self) -> float: return self._sweep @sweep.setter def sweep(self, value: object) -> None: self._sweep = _to_float(value) self._parent._apply("rotate") class DuplicationTranslateConfig: __slots__ = ("_parent", "_axes", "_copies", "_deltas") def __init__(self, parent: "DuplicationController") -> None: self._parent = parent self._axes: str | None = None self._copies = (2, 2, 2) self._deltas = (0.0, 0.0, 0.0) @property def axes(self) -> str | None: return self._axes @axes.setter def axes(self, value: object) -> None: coerced = _coerce_enum(value) self._axes = None if coerced is None else str(coerced) self._parent._apply("translate") @property def copies(self) -> tuple[int, int, int]: return self._copies @copies.setter def copies(self, value: object) -> None: if not isinstance(value, (list, tuple)): raise InvalidArgumentError("translate.copies must be a 3-tuple") items = tuple(cast(tuple[object, ...], value)) if len(items) != 3: raise InvalidArgumentError("translate.copies must be a 3-tuple") self._copies = ( _to_int(items[0]), _to_int(items[1]), _to_int(items[2]), ) self._parent._apply("translate") @property def deltas(self) -> tuple[float, float, float]: return self._deltas @deltas.setter def deltas(self, value: object) -> None: if not isinstance(value, (list, tuple)): raise InvalidArgumentError("translate.deltas must be a 3-tuple") items = tuple(cast(tuple[object, ...], value)) if len(items) != 3: raise InvalidArgumentError("translate.deltas must be a 3-tuple") self._deltas = ( _to_float(items[0]), _to_float(items[1]), _to_float(items[2]), ) self._parent._apply("translate") class DuplicationController: __slots__ = ("_dataset", "mirror", "rotate", "translate") def __init__(self, dataset: "Dataset") -> None: self._dataset = dataset self.mirror = DuplicationMirrorConfig(self) self.rotate = DuplicationRotateConfig(self) self.translate = DuplicationTranslateConfig(self) def __call__(self, options: DuplicationOptions | dict[str, object]) -> object: if isinstance(options, DuplicationOptions): payload = options.to_dict() elif isinstance(options, dict): payload = options else: raise InvalidArgumentError( "duplication options must be a DuplicationOptions or dict" ) return _core_call(self._dataset._core.duplicate, payload) def clear(self) -> None: _core_call(self._dataset._core.duplicate, {"operation": "none"}) def _apply(self, operation: str) -> None: payload: dict[str, object] = {"operation": operation} if operation == "mirror": if self.mirror.axes is None: return payload["mirror_axes"] = self.mirror.axes elif operation == "rotate": payload["rotate_axis"] = self.rotate.axis payload["rotate_copies"] = self.rotate.copies payload["rotate_sweep"] = self.rotate.sweep elif operation == "translate": if self.translate.axes is None: return payload["translate_axes"] = self.translate.axes payload["translate_copies_x"] = self.translate.copies[0] payload["translate_copies_y"] = self.translate.copies[1] payload["translate_copies_z"] = self.translate.copies[2] payload["translate_delta_x"] = self.translate.deltas[0] payload["translate_delta_y"] = self.translate.deltas[1] payload["translate_delta_z"] = self.translate.deltas[2] _core_call(self._dataset._core.duplicate, payload) class TransformController: __slots__ = ( "_dataset", "_scale", "_scale_set", "_translate", "_translate_set", "_rotate1", "_rotate1_set", "_rotate2", "_rotate2_set", "_rotate3", "_rotate3_set", "_pending", ) def __init__(self, dataset: "Dataset") -> None: self._dataset = dataset self._scale = (1.0, 1.0, 1.0) self._scale_set = False self._translate = (0.0, 0.0, 0.0) self._translate_set = False self._rotate1 = (constant.Axis.Y.value, 0.0) self._rotate1_set = False self._rotate2 = (constant.Axis.Y.value, 0.0) self._rotate2_set = False self._rotate3 = (constant.Axis.Y.value, 0.0) self._rotate3_set = False self._pending = False def clear(self) -> None: defaults: dict[str, object] = { "scale_x": 1.0, "scale_y": 1.0, "scale_z": 1.0, "translate_x": 0.0, "translate_y": 0.0, "translate_z": 0.0, "rotate1_axis": constant.Axis.Y.value, "rotate1_angle": 0.0, "rotate2_axis": constant.Axis.Y.value, "rotate2_angle": 0.0, "rotate3_axis": constant.Axis.Y.value, "rotate3_angle": 0.0, } if _core_getint(self._dataset._core, "dataset_id") >= 0: _core_call(self._dataset._core.transform, defaults) self._scale = (1.0, 1.0, 1.0) self._scale_set = False self._translate = (0.0, 0.0, 0.0) self._translate_set = False self._rotate1 = (constant.Axis.Y.value, 0.0) self._rotate1_set = False self._rotate2 = (constant.Axis.Y.value, 0.0) self._rotate2_set = False self._rotate3 = (constant.Axis.Y.value, 0.0) self._rotate3_set = False self._pending = False def _set_vec3(self, name: str, value: object) -> tuple[float, float, float]: if not isinstance(value, (list, tuple)): raise InvalidArgumentError("transform.{0} must be a 3-tuple".format(name)) items = tuple(cast(tuple[object, ...], value)) if len(items) != 3: raise InvalidArgumentError("transform.{0} must be a 3-tuple".format(name)) return (_to_float(items[0]), _to_float(items[1]), _to_float(items[2])) def _set_rotation(self, name: str, value: object) -> tuple[str, float]: if not isinstance(value, (list, tuple)): raise InvalidArgumentError( "transform.{0} must be (axis, angle)".format(name) ) items = tuple(cast(tuple[object, ...], value)) if len(items) != 2: raise InvalidArgumentError( "transform.{0} must be (axis, angle)".format(name) ) axis = _coerce_enum(items[0]) if axis is None: raise InvalidArgumentError( "transform.{0} axis must be provided".format(name) ) return (str(axis), _to_float(items[1])) def _build_payload(self) -> dict[str, object]: payload: dict[str, object] = {} if self._scale_set: payload["scale_x"] = self._scale[0] payload["scale_y"] = self._scale[1] payload["scale_z"] = self._scale[2] if self._translate_set: payload["translate_x"] = self._translate[0] payload["translate_y"] = self._translate[1] payload["translate_z"] = self._translate[2] if self._rotate1_set: payload["rotate1_axis"] = self._rotate1[0] payload["rotate1_angle"] = self._rotate1[1] if self._rotate2_set: payload["rotate2_axis"] = self._rotate2[0] payload["rotate2_angle"] = self._rotate2[1] if self._rotate3_set: payload["rotate3_axis"] = self._rotate3[0] payload["rotate3_angle"] = self._rotate3[1] return payload def _apply(self) -> None: payload = self._build_payload() if not payload: return if _core_getint(self._dataset._core, "dataset_id") < 0: self._pending = True return _core_call(self._dataset._core.transform, payload) self._pending = False def apply_pending(self) -> None: if not self._pending: return if _core_getint(self._dataset._core, "dataset_id") < 0: return payload = self._build_payload() if not payload: self._pending = False return _core_call(self._dataset._core.transform, payload) self._pending = False @property def scale(self) -> tuple[float, float, float]: return self._scale @scale.setter def scale(self, value: object) -> None: self._scale = self._set_vec3("scale", value) self._scale_set = True self._apply() @property def translate(self) -> tuple[float, float, float]: return self._translate @translate.setter def translate(self, value: object) -> None: self._translate = self._set_vec3("translate", value) self._translate_set = True self._apply() @property def rotate1(self) -> tuple[str, float]: return self._rotate1 @rotate1.setter def rotate1(self, value: object) -> None: self._rotate1 = self._set_rotation("rotate1", value) self._rotate1_set = True self._apply() @property def rotate2(self) -> tuple[str, float]: return self._rotate2 @rotate2.setter def rotate2(self, value: object) -> None: self._rotate2 = self._set_rotation("rotate2", value) self._rotate2_set = True self._apply() @property def rotate3(self) -> tuple[str, float]: return self._rotate3 @rotate3.setter def rotate3(self, value: object) -> None: self._rotate3 = self._set_rotation("rotate3", value) self._rotate3_set = True self._apply()
[docs] class Dataset: """Base dataset wrapper. A Dataset instance tracks a loaded dataset in FieldView. If a new load happens with ``input_mode=REPLACE``, existing dataset objects are invalidated. """ __slots__ = ( "_core", "_duplication", "_transform", "_scalar_registry", "_vector_registry", "_position_registry", "_scalar_functions", "_vector_functions", "_boundary_types", "_scalar_function_ids", "_vector_function_ids", "_load_recipe", ) _core: _CoreDataset _duplication: DuplicationController _transform: TransformController _scalar_registry: ScalarRegistry _vector_registry: VectorRegistry _position_registry: PositionRegistry _scalar_functions: list[str] _vector_functions: list[str] _boundary_types: list[str] _scalar_function_ids: dict[str, int] _vector_function_ids: dict[str, int] _load_recipe: dict[str, object] | None def __init__(self, core: _CoreDataset | None = None) -> None: self._core = core or _CoreDataset() self._duplication = DuplicationController(self) self._transform = TransformController(self) self._scalar_registry = ScalarRegistry(self) self._vector_registry = VectorRegistry(self) self._position_registry = PositionRegistry(self) self._scalar_functions = [] self._vector_functions = [] self._boundary_types = [] self._scalar_function_ids = {} self._vector_function_ids = {} self._load_recipe = None def __getattr__(self, name: str) -> object: return _core_getattr(self._core, name) @property def dataset_id(self) -> int: """Return the host dataset identifier.""" return _core_getint(self._core, "dataset_id") @property def server(self) -> str: """Get or set the current server name.""" return _core_getstr(self._core, "server") @server.setter def server(self, value: str) -> None: """Set the current server name.""" _core_setattr(self._core, "server", value) @property def input_mode(self) -> str: """Return the dataset input mode label.""" return _core_getstr(self._core, "input_mode") @property def data_format(self) -> str: """Return the dataset format label.""" self._ensure_valid() return _core_getstr(self._core, "data_format") @property def is_unstructured(self) -> bool: """Return True when the host dataset is unstructured.""" self._ensure_valid() return self._is_unstructured_cached() def _is_unstructured_cached(self) -> bool: """Return the cached dataset topology after prior validation.""" return _core_getbool(self._core, "is_unstructured") @property def grid_file(self) -> str: """Return the grid (or combined) file path.""" self._ensure_valid() return _core_getstr(self._core, "grid_file") @property def result_file(self) -> str: """Return the results file path, if any.""" return _core_getstr(self._core, "result_file") @property def transient(self) -> bool: """Return True if the dataset is transient.""" return _core_getbool(self._core, "transient") @property def has_solution_times(self) -> bool: """Return True if solution times are available.""" return _core_getbool(self._core, "has_solution_times") @property def cur_time_step(self) -> int: """Return the current transient time step index.""" return _core_getint(self._core, "cur_time_step") @property def cur_solution_time(self) -> float: """Return the current transient solution time.""" return _core_getfloat(self._core, "cur_solution_time") @property def total_time_steps(self) -> int: """Return the total number of transient time steps.""" return _core_getint(self._core, "total_time_steps") @property def time_step_range(self) -> Range: """Return the min/max transient time-step range.""" return Range( _core_getint(self._core, "time_step_min"), _core_getint(self._core, "time_step_max"), ) @property def solution_time_range(self) -> Range: """Return the min/max transient solution-time range.""" return Range( _core_getfloat(self._core, "solution_time_min"), _core_getfloat(self._core, "solution_time_max"), ) @property def num_grids(self) -> int: """Return the number of grids in this dataset.""" self._ensure_valid() return _core_getint(self._core, "num_grids") @property def scalar_functions(self) -> list[str]: """Return scalar function names loaded for this dataset (sorted). The list is populated at load time and updated by APIs that add functions. """ self._ensure_valid() return list(self._scalar_functions) @property def scalars(self) -> ScalarRegistry: """Return scalar-function registry access for this dataset.""" self._ensure_valid() return self._scalar_registry @property def vector_functions(self) -> list[str]: """Return vector function names loaded for this dataset (sorted). The list is populated at load time and updated by APIs that add functions. """ self._ensure_valid() return list(self._vector_functions) @property def vectors(self) -> VectorRegistry: """Return vector-function registry access for this dataset.""" self._ensure_valid() return self._vector_registry @property def positions(self) -> PositionRegistry: """Return native XYZ position snapshots for this dataset.""" self._ensure_valid() return self._position_registry @property def boundary_types(self) -> list[str]: """Return boundary type names loaded for this dataset.""" self._ensure_valid() return list(self._boundary_types) def _ensure_valid(self) -> None: try: self._core._ensure_valid() except RuntimeError as exc: msg = str(exc) if _is_invalid_dataset_message(msg): raise InvalidDatasetError(msg) from exc if msg: _send_fv_error_msg(msg) raise CoreError(msg) from exc @property def xmin(self) -> float: """Return the minimum X bound.""" return _core_getfloat(self._core, "xmin") @property def xmax(self) -> float: """Return the maximum X bound.""" return _core_getfloat(self._core, "xmax") @property def ymin(self) -> float: """Return the minimum Y bound.""" return _core_getfloat(self._core, "ymin") @property def ymax(self) -> float: """Return the maximum Y bound.""" return _core_getfloat(self._core, "ymax") @property def zmin(self) -> float: """Return the minimum Z bound.""" return _core_getfloat(self._core, "zmin") @property def zmax(self) -> float: """Return the maximum Z bound.""" return _core_getfloat(self._core, "zmax") @property def rmin(self) -> float: """Return the minimum R bound.""" return _core_getfloat(self._core, "rmin") @property def rmax(self) -> float: """Return the maximum R bound.""" return _core_getfloat(self._core, "rmax") @property def tmin(self) -> float: """Return the minimum T bound.""" return _core_getfloat(self._core, "tmin") @property def tmax(self) -> float: """Return the maximum T bound.""" return _core_getfloat(self._core, "tmax") @property def visibility(self) -> bool: """Get or set dataset visibility state.""" return _core_getbool(self._core, "visibility") @visibility.setter def visibility(self, value: bool) -> None: """Set dataset visibility state.""" _core_setattr(self._core, "visibility", bool(value))
[docs] def dump(self) -> None: """Print dataset state to stdout.""" time_step_range = self.time_step_range solution_time_range = self.solution_time_range items = ( ("dataset_id", self.dataset_id), ("server", self.server), ("input_mode", self.input_mode), ("data_format", self.data_format), ("grid_file", self.grid_file), ("result_file", self.result_file), ("transient", self.transient), ("has_solution_times", self.has_solution_times), ("cur_time_step", self.cur_time_step), ("cur_solution_time", self.cur_solution_time), ("total_time_steps", self.total_time_steps), ("time_step_min", time_step_range.min), ("time_step_max", time_step_range.max), ("solution_time_min", solution_time_range.min), ("solution_time_max", solution_time_range.max), ("num_grids", self.num_grids), ("xmin", self.xmin), ("xmax", self.xmax), ("ymin", self.ymin), ("ymax", self.ymax), ("zmin", self.zmin), ("zmax", self.zmax), ("rmin", self.rmin), ("rmax", self.rmax), ("tmin", self.tmin), ("tmax", self.tmax), ("visibility", self.visibility), ) print("Dataset:") for key, value in items: print(f" {key}: {value}")
[docs] def server_config(self, server_config: str | constant.ServerConfig) -> None: """Select a server configuration. Args: server_config: ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a configuration name string (without the ``.srv`` extension) under the sconfig folder. """ if isinstance(server_config, constant.ServerConfig): server_config = server_config.value if isinstance(server_config, str): lowered = server_config.lower() if lowered == "manual": raise InvalidArgumentError("server_config 'manual' is not supported.") if lowered in { "local", "local_parallel", "localparallel", "local-parallel", }: server_config = "local_parallel" if lowered != "local" else "local" _core_call(self._core.server_config, server_config, None)
[docs] def set_transient( self, *, time_step: int | None = None, solution_time: float | None = None ) -> None: """Set the active transient time step or solution time. Args: time_step: Discrete transient time-step number to activate. solution_time: Continuous solution-time value to activate. Example: .. code-block:: python import os import fieldview as fv data_dir = os.path.join(fv.home, "examples", "rectangular_duct") ds = fv.data.load_fvuns(os.path.join(data_dir, "rect_duct_010.uns"), transient=True) ds.set_transient(time_step=25) """ if time_step is None and solution_time is None: raise InvalidArgumentError( "set_transient requires time_step or solution_time." ) _core_call(self._core.set_transient, time_step, solution_time)
[docs] def transient_info(self) -> TransientInfo: """Return transient dataset information. Example: .. code-block:: python import os import fieldview as fv data_dir = os.path.join(fv.home, "examples", "rectangular_duct") ds = fv.data.load_fvuns(os.path.join(data_dir, "rect_duct_010.uns"), transient=True) info = ds.transient_info() print(info.time_step, info.total_time_steps) print(info.time_step_values[:3]) """ self._ensure_valid() payload = _core_call(self._core.transient_info) return _parse_transient_info(payload)
[docs] def sweep_time( self, *, from_time_step: int | None = None, to_time_step: int | None = None, from_time_step_index: int | None = None, to_time_step_index: int | None = None, from_solution_time: float | None = None, to_solution_time: float | None = None, from_solution_time_index: int | None = None, to_solution_time_index: int | None = None, loop: bool = False, skip: int = 0, cycles: int = 1, delta_time: float | None = None, streaklines_filename: str | os.PathLike[str] | None = None, extracts_database_name: str | os.PathLike[str] | None = None, export_surfaces: object = None, ) -> None: """Run a transient sweep for this dataset. By default, this sweeps all available time steps from the first to the last step. ``delta_time=None`` restores the dataset's original solution times (disables any active delta-time override). Index-based modes resolve endpoints through :meth:`transient_info` before dispatching the sweep to the core. ``export_surfaces`` accepts tuple entries like ``(coord, "coord_sweep", "csv")`` or ``(coord, "coord_sweep")``. Args: from_time_step: Starting transient step value. Use together with ``to_time_step``. to_time_step: Ending transient step value. Use together with ``from_time_step``. from_time_step_index: Starting transient step index. ``0`` means the first step; negative values count from the end (``-1`` is the last step). Indices are resolved against :attr:`TransientInfo.time_step_values`. to_time_step_index: Ending transient step index. ``-1`` means the last step; negative values count from the end. from_solution_time: Starting solution time value. Use together with ``to_solution_time``. to_solution_time: Ending solution time value. Use together with ``from_solution_time``. from_solution_time_index: Starting solution time index. Indices are resolved against :attr:`TransientInfo.solution_time_values`. to_solution_time_index: Ending solution time index. Negative values count from the end. loop: Whether the host should loop the sweep continuously. skip: Number of intermediate steps to skip between displayed or exported states. cycles: Number of sweep cycles to run. delta_time: Optional delta-time override for animated pathline or streakline calculations. If omitted, any prior override is cleared and original dataset solution times are used. streaklines_filename: Optional output filename for streakline export generated during the sweep. extracts_database_name: Optional extracts database name to write during the sweep. export_surfaces: Optional iterable of surface export requests, each expressed as ``(surface, filename)`` or ``(surface, filename, format)``. Example: .. code-block:: python import os import fieldview as fv data_dir = os.path.join(fv.home, "examples", "rectangular_duct") ds = fv.data.load_fvuns(os.path.join(data_dir, "rect_duct_010.uns"), transient=True) ds.sweep_time() ds.sweep_time(from_time_step=1, to_time_step=10) ds.sweep_time(from_time_step_index=-10, to_time_step_index=-1) # last 10 frames ds.sweep_time(from_solution_time=0.0, to_solution_time=0.3, cycles=1) ds.sweep_time(from_solution_time_index=0, to_solution_time_index=-1) """ self._ensure_valid() if not self.transient: raise InvalidArgumentError("dataset is not transient") use_time_steps = from_time_step is not None or to_time_step is not None use_time_step_indices = ( from_time_step_index is not None or to_time_step_index is not None ) use_solution_times = ( from_solution_time is not None or to_solution_time is not None ) use_solution_time_indices = ( from_solution_time_index is not None or to_solution_time_index is not None ) mode_count = sum( ( use_time_steps, use_time_step_indices, use_solution_times, use_solution_time_indices, ) ) if mode_count == 0: use_time_step_indices = True from_time_step_index = 0 to_time_step_index = -1 elif mode_count > 1: raise InvalidArgumentError( "specify exactly one of time-step values, time-step indices, solution-time values, or solution-time indices" ) if use_time_steps: if from_time_step is None or to_time_step is None: raise InvalidArgumentError( "from_time_step and to_time_step must both be provided" ) if isinstance(from_time_step, bool) or not isinstance(from_time_step, int): raise InvalidArgumentError("from_time_step must be an integer") if isinstance(to_time_step, bool) or not isinstance(to_time_step, int): raise InvalidArgumentError("to_time_step must be an integer") if from_time_step > to_time_step: raise InvalidArgumentError("from_time_step must be <= to_time_step") elif use_time_step_indices: if from_time_step_index is None or to_time_step_index is None: raise InvalidArgumentError( "from_time_step_index and to_time_step_index must both be provided" ) if isinstance(from_time_step_index, bool) or not isinstance( from_time_step_index, int ): raise InvalidArgumentError("from_time_step_index must be an integer") if isinstance(to_time_step_index, bool) or not isinstance( to_time_step_index, int ): raise InvalidArgumentError("to_time_step_index must be an integer") elif use_solution_times: if from_solution_time is None or to_solution_time is None: raise InvalidArgumentError( "from_solution_time and to_solution_time must both be provided" ) if isinstance(from_solution_time, bool) or not isinstance( from_solution_time, (int, float) ): raise InvalidArgumentError("from_solution_time must be numeric") if isinstance(to_solution_time, bool) or not isinstance( to_solution_time, (int, float) ): raise InvalidArgumentError("to_solution_time must be numeric") if from_solution_time > to_solution_time: raise InvalidArgumentError( "from_solution_time must be <= to_solution_time" ) else: if from_solution_time_index is None or to_solution_time_index is None: raise InvalidArgumentError( "from_solution_time_index and to_solution_time_index must both be provided" ) if isinstance(from_solution_time_index, bool) or not isinstance( from_solution_time_index, int ): raise InvalidArgumentError( "from_solution_time_index must be an integer" ) if isinstance(to_solution_time_index, bool) or not isinstance( to_solution_time_index, int ): raise InvalidArgumentError("to_solution_time_index must be an integer") if isinstance(skip, bool) or not isinstance(skip, int): raise InvalidArgumentError("skip must be an integer") if skip < 0: raise InvalidArgumentError("skip must be >= 0") if isinstance(cycles, bool) or not isinstance(cycles, int): raise InvalidArgumentError("cycles must be an integer") if cycles <= 0: raise InvalidArgumentError("cycles must be > 0") loop_periods = _normalize_sweep_loop(loop) time_step_value_range: tuple[int, int] | None = None time_step_index_range: tuple[int, int] | None = None solution_time_value_range: tuple[float, float] | None = None solution_time_index_range: tuple[int, int] | None = None if use_time_steps: time_step_value_range = ( cast(int, from_time_step), cast(int, to_time_step), ) elif use_time_step_indices: time_step_index_range = ( cast(int, from_time_step_index), cast(int, to_time_step_index), ) elif use_solution_times: solution_time_value_range = ( float(cast(float | int, from_solution_time)), float(cast(float | int, to_solution_time)), ) else: solution_time_index_range = ( cast(int, from_solution_time_index), cast(int, to_solution_time_index), ) payload: dict[str, object] = {} if time_step_value_range is not None: payload["from_time_step"], payload["to_time_step"] = time_step_value_range elif time_step_index_range is not None: # Index modes must query transient_info() so they can resolve to # concrete core-facing transient step values before dispatch. info = self.transient_info() from_time_step_index_value, to_time_step_index_value = time_step_index_range num_time_steps = len(info.time_step_values) from_index = _normalize_sweep_index( from_time_step_index_value, label="from_time_step_index", total_steps=num_time_steps, collection_name="time steps", ) to_index = _normalize_sweep_index( to_time_step_index_value, label="to_time_step_index", total_steps=num_time_steps, collection_name="time steps", ) if from_index > to_index: raise InvalidArgumentError( "from_time_step_index must resolve to a step at or before to_time_step_index" ) payload["from_time_step"] = info.time_step_values[from_index] payload["to_time_step"] = info.time_step_values[to_index] elif solution_time_value_range is not None: ( payload["from_solution_time"], payload["to_solution_time"], ) = solution_time_value_range else: info = self.transient_info() if not info.has_solution_times: raise InvalidArgumentError("dataset has no solution times") ( from_solution_time_index_value, to_solution_time_index_value, ) = cast(tuple[int, int], solution_time_index_range) num_solution_times = len(info.solution_time_values) from_index = _normalize_sweep_index( from_solution_time_index_value, label="from_solution_time_index", total_steps=num_solution_times, collection_name="solution times", ) to_index = _normalize_sweep_index( to_solution_time_index_value, label="to_solution_time_index", total_steps=num_solution_times, collection_name="solution times", ) if from_index > to_index: raise InvalidArgumentError( "from_solution_time_index must resolve to a time at or before to_solution_time_index" ) payload["from_solution_time"] = info.solution_time_values[from_index] payload["to_solution_time"] = info.solution_time_values[to_index] payload["loop"] = loop_periods payload["skip"] = int(skip) payload["cycles"] = int(cycles) if delta_time is not None: if isinstance(delta_time, bool) or not isinstance(delta_time, (int, float)): raise InvalidArgumentError("delta_time must be numeric") payload["delta_time"] = float(delta_time) if streaklines_filename is not None: streaklines_filename = _coerce_pathlike_str( streaklines_filename, "streaklines_filename" ) if not streaklines_filename.strip(): raise InvalidArgumentError( "streaklines_filename must be a non-empty string" ) payload["streaklines_filename"] = streaklines_filename.strip() if extracts_database_name is not None: extracts_database_name = _coerce_pathlike_str( extracts_database_name, "extracts_database_name" ) if not extracts_database_name.strip(): raise InvalidArgumentError( "extracts_database_name must be a non-empty string" ) payload["extracts_database_name"] = extracts_database_name.strip() export_target_count = sum( value is not None for value in (streaklines_filename, extracts_database_name, export_surfaces) ) if export_target_count > 1: raise InvalidArgumentError( "Only one of streaklines_filename, extracts_database_name, or export_surfaces may be specified" ) normalized_exports = _normalize_sweep_export_surfaces(self, export_surfaces) if normalized_exports is not None: payload["export_surfaces"] = normalized_exports _core_call(self._core.sweep_time, payload)
@property def duplication(self) -> DuplicationController: """Access duplication controls (mirror/rotate/translate).""" return self._duplication @property def transform(self) -> TransformController: """Access transform controls (scale/translate/rotate).""" return self._transform
[docs] def duplication_clear(self) -> None: """Clear duplication state for this dataset.""" _core_call(self._core.duplicate, {"operation": "none"})
[docs] def copy(self) -> "Dataset": """Return an independent dataset copy loaded in append mode.""" self._ensure_valid() recipe = self._load_recipe if recipe is None: raise InvalidArgumentError( "copy() requires a dataset loaded with fieldview.data load_* helpers." ) return _load_dataset_from_recipe(recipe, mode=constant.InputMode.APPEND.value)
def _load_dataset_from_recipe(recipe: dict[str, object], *, mode: str) -> Dataset: kind = str(recipe.get("kind", "")) server = str(recipe.get("server", "")) options = dict(cast(dict[str, object], recipe.get("options", {}))) core = _CoreDataset() if server: core.server = server core.input_mode = mode if kind == "plot3d": _core_call( core.load_plot3d, recipe.get("grid_file", ""), recipe.get("q_file", ""), recipe.get("function_file", ""), recipe.get("function_name_file", ""), mode, options, ) elif kind == "generic": file = str(recipe.get("file", "")) results_file = str(recipe.get("results_file", "")) if results_file: filename = "" grid_file = file results = results_file else: filename = file grid_file = "" results = "" _core_call( cast(Callable[..., object], getattr(core, "load_generic")), str(recipe.get("reader_name", "")), filename, grid_file, results, mode, options, None, None, ) else: raise InvalidArgumentError("copy() is not supported for this dataset type.") clone = _reset_after_load(Dataset(core)) clone._load_recipe = dict(recipe) return clone def _apply_if_supported(reader: object, attr: str, value: object) -> None: if value is None: return if hasattr(reader, attr): setattr(reader, attr, value) def _apply_common_reader_options( reader: object, input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, grid_processing: str | constant.GridProcessing | None = None, ) -> None: if input_mode is not None: setattr(reader, "input_mode", input_mode) if grid_processing is not None: setattr(reader, "grid_processing", grid_processing) # Match FVX data input semantics: omitted server_config means direct/local-serial. cast(_ReaderWithServerConfig, reader).server_config( "" if server_config is None else server_config ) def _apply_transient_reader_options( reader: object, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, initial_time_index: int | None = None, initial_time_step: int | None = None, initial_solution_time: float | None = None, ) -> None: _apply_if_supported(reader, "transient", transient) _apply_if_supported(reader, "read_as_steady_state", read_as_steady_state) _apply_if_supported( reader, "changing_number_of_grids_over_time", changing_number_of_grids_over_time ) _apply_if_supported(reader, "initial_time_index", initial_time_index) _apply_if_supported(reader, "initial_time_step", initial_time_step) _apply_if_supported(reader, "initial_solution_time", initial_solution_time) def _apply_generic_reader_options( reader: object, input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, initial_time_index: int | None = None, initial_time_step: int | None = None, initial_solution_time: float | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, extended_variables: bool | None = None, duplicate_boundaries: bool | None = None, ) -> None: _apply_common_reader_options(reader, input_mode, server_config, grid_processing) _apply_transient_reader_options( reader, transient, read_as_steady_state, changing_number_of_grids_over_time, initial_time_index, initial_time_step, initial_solution_time, ) _apply_if_supported(reader, "boundary_only", boundary_only) _apply_if_supported(reader, "extended_variables", extended_variables) _apply_if_supported(reader, "duplicate_boundaries", duplicate_boundaries) def _load_generic_reader( factory: str | Callable[..., object], file: _PathLikeStr = "", results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, initial_time_index: int | None = None, initial_time_step: int | None = None, initial_solution_time: float | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, extended_variables: bool | None = None, duplicate_boundaries: bool | None = None, ) -> Dataset: if isinstance(factory, str): factory = _reader_factory(factory) reader = factory() _apply_generic_reader_options( reader, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, initial_time_index=initial_time_index, initial_time_step=initial_time_step, initial_solution_time=initial_solution_time, grid_processing=grid_processing, boundary_only=boundary_only, extended_variables=extended_variables, duplicate_boundaries=duplicate_boundaries, ) return _reader_load(reader, file=file, results_file=results_file) def load_generic( reader_name: str, file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, initial_time_index: int | None = None, initial_time_step: int | None = None, initial_solution_time: float | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, extended_variables: bool | None = None, duplicate_boundaries: bool | None = None, ) -> Dataset: """Load a generic reader and return a Dataset. Args: reader_name: Reader alias (e.g. "fvuns", "ensight"). file: "Grid Data", "Grids and Results Data", or "Grid or Combined Data". results_file: "Results Data" file for two-file formats. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings when supported by the reader. initial_time_index, initial_time_step, initial_solution_time: Optional initial transient selection after load, when supported. grid_processing: Optional grid-processing mode override. boundary_only: When supported, restrict loading to boundary/surface data. extended_variables: When supported, request extended-variable import. duplicate_boundaries: When supported, preserve duplicate boundary information during import. """ reader = _reader_factory("_generic")(reader_name=reader_name) _apply_generic_reader_options( reader, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, initial_time_index=initial_time_index, initial_time_step=initial_time_step, initial_solution_time=initial_solution_time, grid_processing=grid_processing, boundary_only=boundary_only, extended_variables=extended_variables, duplicate_boundaries=duplicate_boundaries, ) return _reader_load(reader, file=file, results_file=results_file)
[docs] def load_plot3d( grid_file: _PathLikeStr, q_file: _PathLikeStr = "", function_file: _PathLikeStr = "", function_name_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, merged_series: bool | None = None, auto_detect_format: bool | None = None, file_format: str | constant.Plot3dFileFormat | None = None, auto_partition: bool | None = None, ghost_cells: int | None = None, iblanks: bool | None = None, multi_grid: bool | None = None, coords: str | constant.Plot3dCoords | None = None, ) -> Dataset: """Load Plot3D data and return a Dataset. Args: grid_file: Plot3D grid file path. q_file: Optional Plot3D solution/Q file path. function_file: Optional Plot3D function file path. function_name_file: Optional Plot3D function-name file path. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. merged_series: When ``True``, treat matching Plot3D files as a merged time series. auto_detect_format, file_format: Optional Plot3D format detection or explicit format override. auto_partition: When ``True``, enable reader-side auto partitioning. ghost_cells: Number of ghost-cell layers to interpret. iblanks: When ``True``, enable iblank handling. multi_grid: When ``True``, treat the input as a multigrid dataset. coords: Optional Plot3D coordinate interpretation override. Example usage: .. code-block:: python >>> import fieldview as fv >>> grid_file = "/path/to/grid.bin" >>> q_file = "/path/to/q.bin" >>> ds = fv.data.load_plot3d(grid_file, q_file) """ reader = _reader_factory("_plot3d")() _apply_common_reader_options(reader, input_mode, server_config, grid_processing) _apply_transient_reader_options( reader, transient, read_as_steady_state, changing_number_of_grids_over_time, ) _apply_if_supported(reader, "merged_series", merged_series) _apply_if_supported(reader, "auto_detect_format", auto_detect_format) _apply_if_supported(reader, "file_format", file_format) _apply_if_supported(reader, "auto_partition", auto_partition) _apply_if_supported(reader, "ghost_cells", ghost_cells) _apply_if_supported(reader, "iblanks", iblanks) _apply_if_supported(reader, "multi_grid", multi_grid) _apply_if_supported(reader, "coords", coords) return _reader_load( reader, grid_file=grid_file, q_file=q_file, function_file=function_file, function_name_file=function_name_file, )
[docs] def load_overflow2( grid_file: _PathLikeStr, q_file: _PathLikeStr = "", function_file: _PathLikeStr = "", function_name_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, merged_series: bool | None = None, auto_detect_format: bool | None = None, file_format: str | constant.Plot3dFileFormat | None = None, auto_partition: bool | None = None, ghost_cells: int | None = None, iblanks: bool | None = None, multi_grid: bool | None = None, coords: str | constant.Plot3dCoords | None = None, ) -> Dataset: """Load Overflow-2 (Plot3D-compatible) data and return a Dataset. Args: grid_file: Plot3D-compatible grid file path. q_file: Optional solution/Q file path. function_file: Optional function file path. function_name_file: Optional function-name file path. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. merged_series: When ``True``, treat matching files as a merged time series. auto_detect_format, file_format: Optional format detection or explicit format override. auto_partition: When ``True``, enable reader-side auto partitioning. ghost_cells: Number of ghost-cell layers to interpret. iblanks: When ``True``, enable iblank handling. multi_grid: When ``True``, treat the input as a multigrid dataset. coords: Optional coordinate interpretation override. """ reader = _reader_factory("_overflow2")() _apply_common_reader_options(reader, input_mode, server_config, grid_processing) _apply_transient_reader_options( reader, transient, read_as_steady_state, changing_number_of_grids_over_time, ) _apply_if_supported(reader, "merged_series", merged_series) _apply_if_supported(reader, "auto_detect_format", auto_detect_format) _apply_if_supported(reader, "file_format", file_format) _apply_if_supported(reader, "auto_partition", auto_partition) _apply_if_supported(reader, "ghost_cells", ghost_cells) _apply_if_supported(reader, "iblanks", iblanks) _apply_if_supported(reader, "multi_grid", multi_grid) _apply_if_supported(reader, "coords", coords) return _reader_load( reader, grid_file=grid_file, q_file=q_file, function_file=function_file, function_name_file=function_name_file, )
[docs] def load_fvuns( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load FV-UNS data and return a Dataset. Args: file: Primary FV-UNS file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. Example usage: .. code-block:: python >>> import fieldview as fv >>> file = "/path/to/model.fvuns" >>> results_file = "/path/to/results.fvuns" >>> ds = fv.data.load_fvuns(file=file, results_file=results_file) """ return _load_generic_reader( "_fvuns", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_vtk_structured( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, ) -> Dataset: """Load VTK structured data and return a Dataset. Args: file: Primary VTK structured file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. """ return _load_generic_reader( "_vtk_structured", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, )
[docs] def load_acusolve_direct( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, extended_variables: bool | None = None, duplicate_boundaries: bool | None = None, ) -> Dataset: """Load AcuSolve direct data and return a Dataset. Args: file: Primary AcuSolve input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. extended_variables: When ``True``, request extended-variable import. duplicate_boundaries: When ``True``, preserve duplicate boundary information during import. """ return _load_generic_reader( "_acusolve_direct", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, grid_processing=grid_processing, boundary_only=boundary_only, extended_variables=extended_variables, duplicate_boundaries=duplicate_boundaries, )
[docs] def load_acusolve_fvuns( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load AcuSolve FV-UNS data and return a Dataset. Args: file: Primary AcuSolve FV-UNS file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_acusolve_fvuns", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_ansys_cfx_fvuns( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load ANSYS-CFX FV-UNS data and return a Dataset. Args: file: Primary ANSYS-CFX FV-UNS file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_ansys_cfx_fvuns", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_fluent_cff_direct( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, ) -> Dataset: """Load Fluent CFF direct data and return a Dataset. Args: file: Primary Fluent CFF input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. """ return _load_generic_reader( "_fluent_cff_direct", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, )
[docs] def load_append_sampled_data(file: _PathLikeStr) -> Dataset: """Load Append Sampled Data and return a Dataset. Args: file: Input file path for the sampled-data dataset. """ return _load_generic_reader( "_append_sampled_data", file=file, )
[docs] def load_pw_common(file: _PathLikeStr) -> Dataset: """Load pw common data and return a Dataset. Args: file: Input file path for the pw common dataset. """ return _load_generic_reader( "_pw_common", file=file, )
[docs] def load_cfdpp_fvuns( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load CFD++ FV-UNS data and return a Dataset. Args: file: Primary CFD++ FV-UNS file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_cfdpp_fvuns", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_cgns_structured( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, grid_processing: str | constant.GridProcessing | None = None, ) -> Dataset: """Load CGNS structured data and return a Dataset. Args: file: Primary CGNS structured file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. grid_processing: Optional grid-processing mode override. """ return _load_generic_reader( "_cgns_structured", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, grid_processing=grid_processing, )
[docs] def load_cgns_unstructured( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load CGNS unstructured data and return a Dataset. Args: file: Primary CGNS unstructured file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_cgns_unstructured", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_cgns_unstructured_hybrid( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load CGNS unstructured hybrid data and return a Dataset. Args: file: Primary CGNS hybrid file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_cgns_unstructured_hybrid", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_cobalt_fvuns( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load COBALT FV-UNS data and return a Dataset. Args: file: Primary COBALT FV-UNS file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_cobalt_fvuns", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_converge_fvuns( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load CONVERGE FV-UNS data and return a Dataset. Args: file: Primary CONVERGE FV-UNS file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_converge_fvuns", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_ensight( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load Ensight data and return a Dataset. Args: file: Primary Ensight input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_ensight", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_flow3d_animation( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, initial_time_index: int | None = None, initial_time_step: int | None = None, initial_solution_time: float | None = None, ) -> Dataset: """Load FLOW-3D animation data and return a Dataset. Args: file: Primary FLOW-3D animation file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. initial_time_index, initial_time_step, initial_solution_time: Optional initial transient selection after load. """ return _load_generic_reader( "_flow3d_animation", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, initial_time_index=initial_time_index, initial_time_step=initial_time_step, initial_solution_time=initial_solution_time, grid_processing=grid_processing, )
[docs] def load_flow3d_restart( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, initial_time_index: int | None = None, initial_time_step: int | None = None, initial_solution_time: float | None = None, ) -> Dataset: """Load FLOW-3D restart data and return a Dataset. Args: file: Primary FLOW-3D restart file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. initial_time_index, initial_time_step, initial_solution_time: Optional initial transient selection after load. """ return _load_generic_reader( "_flow3d_restart", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, initial_time_index=initial_time_index, initial_time_step=initial_time_step, initial_solution_time=initial_solution_time, grid_processing=grid_processing, )
[docs] def load_fluent_fvuns( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load FLUENT FV-UNS data and return a Dataset. Args: file: Primary FLUENT FV-UNS file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_fluent_fvuns", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_fluent_unstructured( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, ) -> Dataset: """Load Fluent unstructured data and return a Dataset. Args: file: Primary Fluent unstructured file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. """ return _load_generic_reader( "_fluent_unstructured", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, )
[docs] def load_fluent_cas_dat_direct( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, initial_time_index: int | None = None, initial_time_step: int | None = None, initial_solution_time: float | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load FLUENT cas/dat direct data and return a Dataset. Args: file: Primary FLUENT cas/dat input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. initial_time_index, initial_time_step, initial_solution_time: Optional initial transient selection after load. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_fluent_cas_dat_direct", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, initial_time_index=initial_time_index, initial_time_step=initial_time_step, initial_solution_time=initial_solution_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_fluent_direct( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, ) -> Dataset: """Load Fluent direct data and return a Dataset. Args: file: Primary Fluent direct input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. """ return _load_generic_reader( "_fluent_direct", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, )
[docs] def load_havoc( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load HAVOC data and return a Dataset. Args: file: Primary HAVOC input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_havoc", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_lsdyna_d3plot( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, initial_time_index: int | None = None, initial_time_step: int | None = None, initial_solution_time: float | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load LS-DYNA d3plot data and return a Dataset. Args: file: Primary LS-DYNA d3plot file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. initial_time_index, initial_time_step, initial_solution_time: Optional initial transient selection after load. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_lsdyna_d3plot", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, initial_time_index=initial_time_index, initial_time_step=initial_time_step, initial_solution_time=initial_solution_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_lsdyna_state( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load LS-DYNA state data and return a Dataset. Args: file: Primary LS-DYNA state file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_lsdyna_state", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_nparc_wind( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, ) -> Dataset: """Load NPARC/WIND data and return a Dataset. Args: file: Primary NPARC/WIND input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. """ return _load_generic_reader( "_nparc_wind", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, )
[docs] def load_openfoam_direct( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, initial_time_index: int | None = None, initial_time_step: int | None = None, initial_solution_time: float | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load OpenFOAM direct data and return a Dataset. Args: file: Primary OpenFOAM direct input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. initial_time_index, initial_time_step, initial_solution_time: Optional initial transient selection after load. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_openfoam_direct", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, initial_time_index=initial_time_index, initial_time_step=initial_time_step, initial_solution_time=initial_solution_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_openfoam_fvuns( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, initial_time_index: int | None = None, initial_time_step: int | None = None, initial_solution_time: float | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load OpenFOAM FV-UNS data and return a Dataset. Args: file: Primary OpenFOAM FV-UNS file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. initial_time_index, initial_time_step, initial_solution_time: Optional initial transient selection after load. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_openfoam_fvuns", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, initial_time_index=initial_time_index, initial_time_step=initial_time_step, initial_solution_time=initial_solution_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_sc_tetra( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load SC/Tetra data and return a Dataset. Args: file: Primary SC/Tetra input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_sc_tetra", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_sc_flow( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load SCFLOW data and return a Dataset. Args: file: Primary SCFLOW input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_sc_flow", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_scryu( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load SCRYU data and return a Dataset. Args: file: Primary SCRYU input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_scryu", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_sc_stream( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load SCSTREAM data and return a Dataset. Args: file: Primary SCSTREAM input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_sc_stream", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_starccm_fvuns( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load STAR-CCM+ FV-UNS data and return a Dataset. Args: file: Primary STAR-CCM+ FV-UNS file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_starccm_fvuns", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_starcd_fvuns( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load STAR-CD FV-UNS data and return a Dataset. Args: file: Primary STAR-CD FV-UNS file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_starcd_fvuns", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_stl( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, initial_time_index: int | None = None, initial_time_step: int | None = None, initial_solution_time: float | None = None, ) -> Dataset: """Load STL data and return a Dataset. Args: file: Primary STL input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. initial_time_index, initial_time_step, initial_solution_time: Optional initial transient selection after load. """ return _load_generic_reader( "_stl", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, initial_time_index=initial_time_index, initial_time_step=initial_time_step, initial_solution_time=initial_solution_time, grid_processing=grid_processing, )
[docs] def load_surface_sampled_data( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load surface sampled data and return a Dataset. Args: file: Primary surface-sampled-data input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_surface_sampled_data", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_tecplot_360( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load Tecplot 360 data and return a Dataset. Args: file: Primary Tecplot 360 input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_tecplot_360", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_ultrafluidx_direct( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, initial_time_index: int | None = None, initial_time_step: int | None = None, initial_solution_time: float | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load ultraFluidX data and return a Dataset. Args: file: Primary ultraFluidX input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. initial_time_index, initial_time_step, initial_solution_time: Optional initial transient selection after load. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_ultrafluidx_direct", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, initial_time_index=initial_time_index, initial_time_step=initial_time_step, initial_solution_time=initial_solution_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_vtk_unstructured_hybrid( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load VTK unstructured/hybrid data and return a Dataset. Args: file: Primary VTK unstructured or hybrid input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_vtk_unstructured_hybrid", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_wind_structured( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, ) -> Dataset: """Load WIND structured data and return a Dataset. Args: file: Primary WIND structured input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. """ return _load_generic_reader( "_wind_structured", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, )
[docs] def load_wind_unstructured( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, boundary_only: bool | None = None, ) -> Dataset: """Load WIND unstructured data and return a Dataset. Args: file: Primary WIND unstructured input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. boundary_only: When ``True``, load only boundary/surface data. """ return _load_generic_reader( "_wind_unstructured", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, grid_processing=grid_processing, boundary_only=boundary_only, )
[docs] def load_xdb_import( file: _PathLikeStr, results_file: _PathLikeStr = "", input_mode: str | constant.InputMode | None = None, server_config: str | constant.ServerConfig | None = None, transient: bool | None = None, read_as_steady_state: bool | None = None, changing_number_of_grids_over_time: bool | None = None, grid_processing: str | constant.GridProcessing | None = None, initial_time_index: int | None = None, initial_time_step: int | None = None, initial_solution_time: float | None = None, ) -> Dataset: """Load XDB import data and return a Dataset. Args: file: Primary XDB import input file path. results_file: Optional companion results file. input_mode: Optional dataset load mode. Use ``constant.InputMode.REPLACE`` or ``constant.InputMode.APPEND``. server_config: Optional server configuration. Use ``constant.ServerConfig.LOCAL``, ``constant.ServerConfig.LOCAL_PARALLEL``, or a named ``.srv`` configuration. transient, read_as_steady_state, changing_number_of_grids_over_time: Optional transient-reader settings. grid_processing: Optional grid-processing mode override. initial_time_index, initial_time_step, initial_solution_time: Optional initial transient selection after load. """ return _load_generic_reader( "_xdb_import", file=file, results_file=results_file, input_mode=input_mode, server_config=server_config, transient=transient, read_as_steady_state=read_as_steady_state, changing_number_of_grids_over_time=changing_number_of_grids_over_time, initial_time_index=initial_time_index, initial_time_step=initial_time_step, initial_solution_time=initial_solution_time, grid_processing=grid_processing, )
def _reset_after_load(dataset: Dataset) -> Dataset: global _current_dataset dataset.duplication_clear() dataset.transform.clear() _refresh_function_lists(dataset) _dataset_registry[dataset.dataset_id] = dataset _current_dataset = dataset return dataset def _get_or_attach_dataset(dataset_id: int) -> Dataset: """Return a live dataset wrapper for an existing host dataset id. Replace-style host reloads invalidate previously attached wrappers for the same dataset slot. Append-style reads leave existing dataset wrappers valid and allocate new dataset ids for the appended data. """ dataset_id = int(dataset_id) dataset_ref = _dataset_registry.get(dataset_id) if dataset_ref is not None: try: dataset_ref._core._ensure_valid() except RuntimeError as exc: if _is_invalid_dataset_message(str(exc)): _dataset_registry.pop(dataset_id, None) else: msg = str(exc).strip() raise CoreError( msg or "Unexpected FieldView error while validating cached dataset." ) from exc else: return dataset_ref attach_existing = getattr(_core_module, "dataset_attach_existing", None) if not callable(attach_existing): raise InvalidDatasetError( "FieldView host does not provide live dataset attach support. " + "Load it via fieldview.data.load_*." ) try: core_dataset = _core_call(attach_existing, dataset_id) except CoreError as exc: raise InvalidDatasetError(str(exc)) from exc dataset_ref = Dataset(core=cast(_CoreDataset, core_dataset)) _refresh_function_lists(dataset_ref) _dataset_registry[dataset_ref.dataset_id] = dataset_ref return dataset_ref def _refresh_function_lists(dataset: Dataset) -> None: scalar_payload = _load_json_dict( _core_call(dataset._core._list_functions_json, "scalar"), "scalar function list", ) vector_payload = _load_json_dict( _core_call(dataset._core._list_functions_json, "vector"), "vector function list", ) dataset._scalar_functions, dataset._scalar_function_ids = _parse_function_list( scalar_payload ) dataset._vector_functions, dataset._vector_function_ids = _parse_function_list( vector_payload ) dataset._boundary_types = [] list_boundary_types = getattr(dataset._core, "_list_boundary_types_json", None) if list_boundary_types is None: return boundary_payload = _load_json_dict( _core_call(cast(Callable[..., object], list_boundary_types)), "boundary type list", ) dataset._boundary_types = _parse_boundary_type_list(boundary_payload) def _is_invalid_dataset_message(message: str) -> bool: lower_msg = message.lower() return "dataset is invalid" in lower_msg or "replaced by another load" in lower_msg def _refresh_current_dataset_function_lists() -> None: """Refresh cached function lists for the current dataset wrapper when available.""" try: dataset = get_current() except InvalidDatasetError: return _refresh_function_lists(dataset) def _parse_function_list( payload: dict[str, object], ) -> tuple[list[str], dict[str, int]]: names: list[str] = [] mapping: dict[str, int] = {} functions = payload.get("functions", []) if not isinstance(functions, list): return names, mapping for entry in cast(list[object], functions): if not isinstance(entry, dict): continue entry_map = cast(dict[str, object], entry) name = entry_map.get("name") func_id = entry_map.get("id") if not isinstance(name, str) or not name: continue try: func_id_int = _to_int(func_id) except (TypeError, ValueError): continue names.append(name) mapping.setdefault(name, func_id_int) return names, mapping def _parse_boundary_type_list(payload: dict[str, object]) -> list[str]: candidates = payload.get("boundary_types") if candidates is None: candidates = payload.get("types") names: list[str] = [] if not isinstance(candidates, list): return names for entry in cast(list[object], candidates): if isinstance(entry, str): name = entry.strip() elif isinstance(entry, dict): name = str(cast(dict[str, object], entry).get("name", "")).strip() else: continue if name: names.append(name) return names def _resolve_dataset_or_current(dataset: Dataset | None) -> Dataset: if dataset is None: dataset = get_current() if not isinstance(dataset, Dataset): raise InvalidArgumentError("dataset must be a Dataset instance") if dataset.dataset_id < 0: raise InvalidDatasetError("dataset is invalid (not loaded)") dataset._ensure_valid() return dataset def _validate_ijk_point(value: object) -> tuple[int, int, int]: if not isinstance(value, (list, tuple)): raise InvalidArgumentError("ijk_point must be a 3-integer sequence") components = tuple(cast(tuple[object, ...], value)) if len(components) != 3: raise InvalidArgumentError("ijk_point must be a 3-integer sequence") result: list[int] = [] for index, component in enumerate(components, start=1): if isinstance(component, bool) or not isinstance(component, int): raise InvalidArgumentError(f"ijk_point[{index}] must be an integer") result.append(int(component)) return result[0], result[1], result[2] def _validate_grid_index(value: object) -> int: if isinstance(value, bool) or not isinstance(value, int): raise InvalidArgumentError("grid_index must be an integer") if value < 0: raise InvalidArgumentError("grid_index must be >= 0") return int(value) def _validate_grid(value: object) -> int: if isinstance(value, bool) or not isinstance(value, int): raise InvalidArgumentError("grid must be an integer") if value < 1: raise InvalidArgumentError("grid must be >= 1") return int(value) def _lookup_probe_function_id( dataset: Dataset, value: object, attr: str, label: str ) -> int: if not isinstance(value, str): raise InvalidArgumentError(f"{label}_func must be a string") needle = value.strip() if not needle: raise InvalidArgumentError(f"{label}_func cannot be empty") mapping = getattr(dataset, attr, None) if not isinstance(mapping, dict): raise InvalidArgumentError( f"{label}_func could not be resolved for the dataset" ) mapping = cast(dict[str, int], mapping) needle_lower = needle.lower() for func_name, func_id in mapping.items(): if func_name.lower() == needle_lower: return func_id raise InvalidArgumentError(f"{label}_func not found: {needle}") def _resolve_probe_function_overrides( dataset: Dataset, *, scalar_func: object = None, vector_func: object = None, iso_func: object = None, threshold_func: object = None, ) -> tuple[int | None, int | None, int | None, int | None]: scalar_func_id = None vector_func_id = None iso_func_id = None threshold_func_id = None if scalar_func is not None: scalar_func_id = _lookup_probe_function_id( dataset, scalar_func, "_scalar_function_ids", "scalar" ) if vector_func is not None: vector_func_id = _lookup_probe_function_id( dataset, vector_func, "_vector_function_ids", "vector" ) if iso_func is not None: # FieldView's geometric/iso register uses dataset scalar-function IDs. iso_func_id = _lookup_probe_function_id( dataset, iso_func, "_scalar_function_ids", "iso" ) if threshold_func is not None: # Threshold selection also comes from the dataset scalar-function registry. threshold_func_id = _lookup_probe_function_id( dataset, threshold_func, "_scalar_function_ids", "threshold" ) return scalar_func_id, vector_func_id, iso_func_id, threshold_func_id def _validate_tolerance(value: object) -> float: if value is None: return 1.0e-5 if isinstance(value, bool) or not isinstance(value, (int, float)): raise InvalidArgumentError("tolerance must be a number") return float(value) def _get_surface_target(surface: object) -> tuple[str, int]: prefix = getattr(surface, "_core_prefix", "") phigs_obj = getattr(surface, "_phigs_obj", None) if getattr(surface, "_is_deleted", False): raise InvalidArgumentError("surface was deleted") if not isinstance(prefix, str) or not isinstance(phigs_obj, int): raise InvalidArgumentError("surface must be a FieldView surface object") return prefix, int(phigs_obj) def _as_vec3(value: object, label: str) -> Vec3: if not isinstance(value, (list, tuple)): raise CoreError(f"Probe response field '{label}' must be a 3-item sequence") components = tuple(cast(tuple[object, ...], value)) if len(components) != 3: raise CoreError(f"Probe response field '{label}' must be a 3-item sequence") try: return Vec3( _to_float(components[0]), _to_float(components[1]), _to_float(components[2]), ) except (TypeError, ValueError) as exc: raise CoreError( f"Probe response field '{label}' must contain numeric values" ) from exc def _as_optional_scalar_probe(value: object, label: str) -> ScalarProbe: if not isinstance(value, dict): raise CoreError(f"Probe response field '{label}' must be an object") value_map = cast(dict[str, object], value) func = value_map.get("func") if func is not None and not isinstance(func, str): raise CoreError(f"Probe response field '{label}.func' must be a string or null") raw_value = value_map.get("value") if raw_value is None: probe_value = None elif isinstance(raw_value, bool) or not isinstance(raw_value, (int, float)): raise CoreError(f"Probe response field '{label}.value' must be numeric or null") else: probe_value = float(raw_value) return ScalarProbe(func, probe_value) def _as_optional_vector_probe(value: object, label: str) -> VectorProbe: if not isinstance(value, dict): raise CoreError(f"Probe response field '{label}' must be an object") value_map = cast(dict[str, object], value) func = value_map.get("func") if func is not None and not isinstance(func, str): raise CoreError(f"Probe response field '{label}.func' must be a string or null") raw_value = value_map.get("value") vector_value = None if raw_value is None else _as_vec3(raw_value, f"{label}.value") return VectorProbe(func, vector_value) def _parse_probe_result(payload: dict[str, object]) -> ProbeResult: hit = payload.get("hit") if not isinstance(hit, bool): raise CoreError("Probe response field 'hit' must be boolean") raw_point = payload.get("point") point = None if raw_point is None else _as_vec3(raw_point, "point") if hit and point is None: raise CoreError("Probe response field 'point' must be present when hit is true") region = payload.get("region") if region is not None: if isinstance(region, bool) or not isinstance(region, int): raise CoreError("Probe response field 'region' must be an integer or null") region = int(region) grid_index = payload.get("grid_index") if grid_index is not None: if isinstance(grid_index, bool) or not isinstance(grid_index, int): raise CoreError( "Probe response field 'grid_index' must be an integer or null" ) grid_index = int(grid_index) grid = payload.get("grid") if grid is not None: if isinstance(grid, bool) or not isinstance(grid, int): raise CoreError("Probe response field 'grid' must be an integer or null") grid = int(grid) raw_ijk = payload.get("ijk") ijk = None if raw_ijk is not None: if not isinstance(raw_ijk, (list, tuple)): raise CoreError( "Probe response field 'ijk' must be a 3-item sequence or null" ) components = tuple(cast(tuple[object, ...], raw_ijk)) if len(components) != 3: raise CoreError( "Probe response field 'ijk' must be a 3-item sequence or null" ) try: ijk = ( _to_float(components[0]), _to_float(components[1]), _to_float(components[2]), ) except (TypeError, ValueError) as exc: raise CoreError( "Probe response field 'ijk' must contain numeric values" ) from exc return ProbeResult( hit=bool(hit), point=point, region=region, grid_index=grid_index, grid=grid, ijk=ijk, scalar=_as_optional_scalar_probe(payload.get("scalar"), "scalar"), iso=_as_optional_scalar_probe(payload.get("iso"), "iso"), threshold=_as_optional_scalar_probe(payload.get("threshold"), "threshold"), vector=_as_optional_vector_probe(payload.get("vector"), "vector"), ) def _parse_integration_result(payload: dict[str, object]) -> IntegrationResult: if not isinstance(payload, dict): raise CoreError("Integration response must be an object") integral_type = payload.get("integral_type") if integral_type is not None and not isinstance(integral_type, str): raise CoreError("Integration response field 'integral_type' must be a string") scalar_function = payload.get("scalar_function") if scalar_function is not None and not isinstance(scalar_function, str): raise CoreError( "Integration response field 'scalar_function' must be a string or null" ) surface = payload.get("surface") if surface is not None and not isinstance(surface, str): raise CoreError("Integration response field 'surface' must be a string or null") area = payload.get("area") total = payload.get("sum") if isinstance(area, bool) or not isinstance(area, (int, float)): raise CoreError("Integration response field 'area' must be numeric") if isinstance(total, bool) or not isinstance(total, (int, float)): raise CoreError("Integration response field 'sum' must be numeric") average_value = payload.get("average") if average_value is None: average = None elif isinstance(average_value, bool) or not isinstance(average_value, (int, float)): raise CoreError("Integration response field 'average' must be numeric or null") else: average = float(average_value) has_surface_normals = payload.get("has_surface_normals", False) if not isinstance(has_surface_normals, bool): raise CoreError( "Integration response field 'has_surface_normals' must be boolean" ) vector_function = payload.get("vector_function") if vector_function is not None and not isinstance(vector_function, str): raise CoreError( "Integration response field 'vector_function' must be a string or null" ) def optional_float(key: str) -> float | None: value = payload.get(key) if value is None: return None if isinstance(value, bool) or not isinstance(value, (int, float)): raise CoreError( f"Integration response field '{key}' must be numeric or null" ) return float(value) return IntegrationResult( integral_type=str(integral_type or ""), scalar_function=scalar_function, area=float(area), sum=float(total), average=average, surface=surface, has_surface_normals=has_surface_normals, vector_function=vector_function, sum_nx=optional_float("sum_nx"), sum_ny=optional_float("sum_ny"), sum_nz=optional_float("sum_nz"), sum_v_dot_n=optional_float("sum_v_dot_n"), ) def _parse_transient_int_values( payload: dict[str, object], key: str ) -> tuple[int, ...]: raw_values = payload.get(key, ()) if raw_values is None: return () if not isinstance(raw_values, (list, tuple)): raise CoreError(f"Transient response field '{key}' must be a sequence") values_seq = tuple(cast(tuple[object, ...], raw_values)) values: list[int] = [] for index, value in enumerate(values_seq, start=1): if isinstance(value, bool) or not isinstance(value, (int, float)): raise CoreError( f"Transient response field '{key}[{index}]' must be numeric" ) converted = int(value) if converted != value: raise CoreError( f"Transient response field '{key}[{index}]' must be an integer" ) values.append(converted) return tuple(values) def _parse_transient_float_values( payload: dict[str, object], key: str ) -> tuple[float, ...]: raw_values = payload.get(key, ()) if raw_values is None: return () if not isinstance(raw_values, (list, tuple)): raise CoreError(f"Transient response field '{key}' must be a sequence") values_seq = tuple(cast(tuple[object, ...], raw_values)) values: list[float] = [] for index, value in enumerate(values_seq, start=1): if isinstance(value, bool) or not isinstance(value, (int, float)): raise CoreError( f"Transient response field '{key}[{index}]' must be numeric" ) values.append(float(value)) return tuple(values) def _parse_transient_info(payload: dict[str, object]) -> TransientInfo: if not isinstance(payload, dict): raise CoreError("Transient response must be an object") time_step = payload.get("cur_time_step") if isinstance(time_step, bool) or not isinstance(time_step, int): raise CoreError("Transient response field 'cur_time_step' must be an integer") solution_time = payload.get("cur_solution_time") if isinstance(solution_time, bool) or not isinstance(solution_time, (int, float)): raise CoreError("Transient response field 'cur_solution_time' must be numeric") total_time_steps = payload.get("total_time_steps") if isinstance(total_time_steps, bool) or not isinstance(total_time_steps, int): raise CoreError( "Transient response field 'total_time_steps' must be an integer" ) has_solution_times = payload.get("has_solution_times", False) if not isinstance(has_solution_times, bool): raise CoreError("Transient response field 'has_solution_times' must be boolean") time_step_min = payload.get("time_step_min") time_step_max = payload.get("time_step_max") if isinstance(time_step_min, bool) or not isinstance(time_step_min, int): raise CoreError("Transient response field 'time_step_min' must be an integer") if isinstance(time_step_max, bool) or not isinstance(time_step_max, int): raise CoreError("Transient response field 'time_step_max' must be an integer") solution_time_min = payload.get("solution_time_min") solution_time_max = payload.get("solution_time_max") if isinstance(solution_time_min, bool) or not isinstance( solution_time_min, (int, float) ): raise CoreError("Transient response field 'solution_time_min' must be numeric") if isinstance(solution_time_max, bool) or not isinstance( solution_time_max, (int, float) ): raise CoreError("Transient response field 'solution_time_max' must be numeric") return TransientInfo( time_step=int(time_step), solution_time=float(solution_time), total_time_steps=int(total_time_steps), has_solution_times=has_solution_times, time_step_range=Range(int(time_step_min), int(time_step_max)), solution_time_range=Range(float(solution_time_min), float(solution_time_max)), time_step_values=_parse_transient_int_values(payload, "time_step_values"), solution_time_values=_parse_transient_float_values( payload, "solution_time_values" ), ) def _normalize_sweep_loop(value: object) -> int: if value is False or value is None: return 1 if value is True: raise InvalidArgumentError( "loop=True is ambiguous; use an integer number of periods" ) if isinstance(value, bool) or not isinstance(value, int): raise InvalidArgumentError("loop must be False or a positive integer") if value <= 0: raise InvalidArgumentError("loop must be False or a positive integer") return int(value) def _normalize_sweep_index( value: int, *, label: str, total_steps: int, collection_name: str, ) -> int: if total_steps <= 0: raise InvalidArgumentError(f"dataset has no transient {collection_name}") index = value if index < 0: index += total_steps if index < 0 or index >= total_steps: raise InvalidArgumentError( f"{label} index {value} is out of range for {total_steps} transient {collection_name}" ) return index def _normalize_sweep_export_surfaces( dataset: "Dataset", export_surfaces: object, ) -> list[dict[str, object]] | None: if export_surfaces is None: return None if not isinstance(export_surfaces, (list, tuple)): raise InvalidArgumentError( "export_surfaces must be a non-empty list of export requests" ) export_entries = tuple(cast(tuple[object, ...], export_surfaces)) if not export_entries: raise InvalidArgumentError( "export_surfaces must be a non-empty list of export requests" ) if len(export_entries) != 1: raise InvalidArgumentError( "export_surfaces currently supports exactly one surface entry" ) normalized: list[dict[str, object]] = [] for index, entry in enumerate(export_entries, start=1): if not isinstance(entry, (list, tuple)): raise InvalidArgumentError(f"export_surfaces[{index}] must be a tuple") entry_items = tuple(cast(tuple[object, ...], entry)) surface: object basename: object export_type: object if len(entry_items) == 2: surface, basename = entry_items export_type = "text" elif len(entry_items) == 3: surface = entry_items[0] basename = entry_items[1] export_type = entry_items[2] else: raise InvalidArgumentError( f"export_surfaces[{index}] tuple entries must be (surface, basename) or (surface, basename, type)" ) prefix, phigs_obj = _get_surface_target(surface) dataset_id = getattr(surface, "_dataset_id", None) if dataset_id != dataset.dataset_id: raise InvalidArgumentError( f"export_surfaces[{index}].surface must belong to the target dataset" ) basename_path = _coerce_pathlike_str( basename, f"export_surfaces[{index}].basename" ) if not basename_path.strip(): raise InvalidArgumentError( f"export_surfaces[{index}].basename must be a non-empty string" ) if not isinstance(export_type, str): raise InvalidArgumentError( f"export_surfaces[{index}].type must be a string" ) export_type = export_type.strip().lower() if export_type not in {"text", "mat-file", "csv"}: raise InvalidArgumentError( f"export_surfaces[{index}].type must be 'text', 'mat-file', or 'csv'" ) normalized.append( { "surface_kind": prefix, "phigs_obj": phigs_obj, "basename": basename_path.strip(), "type": export_type, } ) return normalized
[docs] def probe( point: object, dataset: Dataset | None = None, *, scalar_func: object = None, vector_func: object = None, iso_func: object = None, threshold_func: object = None, ) -> ProbeResult: """Probe selected functions at a displayed-space point. Args: point: Displayed-space XYZ probe location as a 3-number sequence. dataset: Target dataset. When omitted, the current dataset is used. scalar_func: Optional scalar function name to make current before probing. When omitted, the existing current scalar is used. vector_func: Optional vector function name to make current before probing. When omitted, the existing current vector is used. iso_func: Optional iso/geometric function name to make current before probing. When omitted, the existing current iso/geometric function is used. threshold_func: Optional threshold function name to make current before probing. When omitted, the existing current threshold function is used. Returns: ProbeResult with the probed location, resolved grid metadata, and sampled scalar/vector/iso/threshold values. Notes: Any provided function override updates FieldView's current function selection before probing and leaves that selection changed after the call returns. If an override is invalid or cannot be selected, the previous current-function state is preserved. Example: .. code-block:: python import os import fieldview as fv data_dir = os.path.join(fv.home, "examples", "f18") ds = fv.data.load_plot3d( os.path.join(data_dir, "f18i9b_g_bin"), os.path.join(data_dir, "f18i9b_q_bin"), ) result = fv.data.probe( (0.5, 0.5, 0.5), dataset=ds, scalar_func="Pressure", vector_func="Velocity", ) print(result.scalar) print(result.vector) """ resolved_dataset = _resolve_dataset_or_current(dataset) x, y, z = _coerce_xyz_triplet(point, "point") scalar_func_id, vector_func_id, iso_func_id, threshold_func_id = ( _resolve_probe_function_overrides( resolved_dataset, scalar_func=scalar_func, vector_func=vector_func, iso_func=iso_func, threshold_func=threshold_func, ) ) payload = _load_json_dict( _core_call( resolved_dataset._core._probe_json, x, y, z, scalar_func_id, vector_func_id, iso_func_id, threshold_func_id, ), "probe result", ) return _parse_probe_result(payload)
[docs] def probe_ijk( ijk_point: object, grid_index: int | None = None, *, grid: int | None = None, dataset: Dataset | None = None, scalar_func: object = None, vector_func: object = None, iso_func: object = None, threshold_func: object = None, ) -> ProbeResult: """Probe selected functions at a structured-grid IJK point. Args: ijk_point: Structured-grid IJK probe location as a 3-integer sequence. grid_index: Optional 0-based actual-grid index. grid: Optional 1-based grid number, matching the convention used by other public APIs. Provide either ``grid`` or ``grid_index``. If both are provided, they must refer to the same grid. dataset: Target dataset. When omitted, the current dataset is used. scalar_func: Optional scalar function name to make current before probing. When omitted, the existing current scalar is used. vector_func: Optional vector function name to make current before probing. When omitted, the existing current vector is used. iso_func: Optional iso/geometric function name to make current before probing. When omitted, the existing current iso/geometric function is used. threshold_func: Optional threshold function name to make current before probing. When omitted, the existing current threshold function is used. Returns: ProbeResult with the resolved displayed-space point, grid metadata, and sampled scalar/vector/iso/threshold values. Notes: Any provided function override updates FieldView's current function selection before probing and leaves that selection changed after the call returns. If an override is invalid or cannot be selected, the previous current-function state is preserved. Example: .. code-block:: python import os import fieldview as fv data_dir = os.path.join(fv.home, "examples", "f18") ds = fv.data.load_plot3d( os.path.join(data_dir, "f18i9b_g_bin"), os.path.join(data_dir, "f18i9b_q_bin"), ) result = fv.data.probe_ijk((2, 3, 4), grid=1, dataset=ds, scalar_func="Pressure") print(result.point) print(result.scalar) """ resolved_dataset = _resolve_dataset_or_current(dataset) i, j, k = _validate_ijk_point(ijk_point) if grid is None and grid_index is None: raise InvalidArgumentError("probe_ijk requires grid or grid_index") resolved_grid_index = None if grid is not None: resolved_grid_index = _validate_grid(grid) - 1 if grid_index is not None: validated_grid_index = _validate_grid_index(grid_index) if ( resolved_grid_index is not None and resolved_grid_index != validated_grid_index ): raise InvalidArgumentError( "grid and grid_index must refer to the same grid" ) resolved_grid_index = validated_grid_index scalar_func_id, vector_func_id, iso_func_id, threshold_func_id = ( _resolve_probe_function_overrides( resolved_dataset, scalar_func=scalar_func, vector_func=vector_func, iso_func=iso_func, threshold_func=threshold_func, ) ) payload = _load_json_dict( _core_call( resolved_dataset._core._probe_ijk_json, i, j, k, resolved_grid_index, scalar_func_id, vector_func_id, iso_func_id, threshold_func_id, ), "probe_ijk result", ) return _parse_probe_result(payload)
def _integrate_surface(surface: object) -> IntegrationResult: """Integrate the current scalar on a single surface.""" prefix, phigs_obj = _get_surface_target(surface) funcs = { "boundary_surf": getattr(_core_module, "boundary_surf_integrate", None), "comp_surf": getattr(_core_module, "comp_surf_integrate", None), "coord_surf": getattr(_core_module, "coord_surf_integrate", None), "iso_surf": getattr(_core_module, "iso_surf_integrate", None), } func = funcs.get(prefix) if not callable(func): raise InvalidArgumentError( "surface must be a Boundary, Comp, Coord, or Iso surface" ) payload = cast(dict[str, object], _core_call(func, phigs_obj)) return _parse_integration_result(payload) def _integrate_partial_surface( surface: object, point: object, tolerance: float | None = None, ) -> IntegrationResult | None: """Integrate the connected partial region on a coord or iso surface.""" prefix, phigs_obj = _get_surface_target(surface) x, y, z = _coerce_xyz_triplet(point, "point") tol = _validate_tolerance(tolerance) funcs = { "coord_surf": getattr(_core_module, "coord_surf_integrate_partial", None), "iso_surf": getattr(_core_module, "iso_surf_integrate_partial", None), } func = funcs.get(prefix) if not callable(func): raise InvalidArgumentError("surface must be a Coord or Iso surface") payload = _core_call(func, phigs_obj, x, y, z, tol) if payload is None: return None return _parse_integration_result(cast(dict[str, object], payload)) _READER_ALIASES = { "plot3d": "plot3d", "overflow2": "plot3d", "fvuns": "unstructured", "vtk_structured": "vtk_structured", "acusolve_direct": "acusolve_direct", "acusolve_fvuns": "acusolve_unstructured", "ansys_cfx_fvuns": "ansys-cfx_unstructured", "fluent_cff_direct": "fluent_cff_cas/dat_direct", "append_sampled_data": "append_sampled_data", "pw_common": "pw common", "cfdpp_fvuns": "cfd++_unstructured", "cgns_structured": "cgns_structured", "cgns_unstructured": "cgns_unstructured", "cgns_unstructured_hybrid": "cgns_unstructured/hybrid", "cobalt_fvuns": "cobalt_unstructured", "converge_fvuns": "converge_unstructured", "ensight": "ensight", "flow3d_animation": "flow-3d animation", "flow3d_restart": "flow-3d restart", "fluent_fvuns": "fluent_unstructured", "fluent_unstructured": "fluent_unstructured", "fluent_cas_dat_direct": "fluent_cas/dat_direct", "fluent_direct": "fluent_direct", "havoc": "havoc", "lsdyna_d3plot": "lsdyna_d3plot", "lsdyna_state": "lsdyna", "nparc_wind": "nparc/wind", "openfoam_direct": "openfoam", "openfoam_fvuns": "openfoam_unstructured", "sc_tetra": "sc_tetra", "sc_flow": "sc_flow", "scryu": "scryu", "sc_stream": "sc_stream", "starccm_fvuns": "starccm_unstructured", "starcd_fvuns": "starcd_unstructured", "stl": "stl", "surface_sampled_data": "surface_sampled_data", "tecplot_360": "tecplot_360", "ultrafluidx_direct": "ufx", "vtk_unstructured_hybrid": "vtk_unstructured/hybrid", "wind_structured": "wind_structured", "wind_unstructured": "wind_unstructured", "xdb_import": "xdb_import", } def reader_aliases() -> dict[str, str]: """Return a copy of reader aliases supported by the generic reader.""" return dict(_READER_ALIASES)
[docs] @dataclass class DatasetBounds: """Axis-aligned bounding box for a dataset. Cartesian axes are always populated. ``rmin``/``rmax`` and ``tmin``/``tmax`` are non-zero only for datasets with a cylindrical coordinate system. """ xmin: float = 0.0 xmax: float = 0.0 ymin: float = 0.0 ymax: float = 0.0 zmin: float = 0.0 zmax: float = 0.0 rmin: float = 0.0 rmax: float = 0.0 tmin: float = 0.0 tmax: float = 0.0
[docs] @dataclass class SessionState: """Snapshot of the current FieldView session. Returned by :func:`get_session_state`. Contains dataset metadata, domain bounds, function lists, and all existing surface objects in a single call. When ``data_loaded`` is ``False``, ``dataset_id`` is ``None`` and all lists are empty. The returned fields are grouped roughly as follows: - Dataset identity: ``data_loaded``, ``dataset_id``, ``data_format``, ``reader_name``, ``grid_file``, ``result_file``, ``num_grids`` - Domain extents: ``bounds`` - Transient state: ``transient``, ``has_solution_times``, ``cur_time_step``, ``total_time_steps`` - Available functions: ``scalar_functions``, ``vector_functions`` - Existing scene objects: ``objects`` - Window/layout state: ``windows`` Example: .. code-block:: python >>> import os >>> import fieldview as fv >>> data_dir = os.path.join(fv.home, "examples", "f18") >>> ds = fv.data.load_plot3d( ... os.path.join(data_dir, "f18i9b_g_bin"), ... os.path.join(data_dir, "f18i9b_q_bin"), ... ) >>> state = fv.data.get_session_state() >>> state.dataset_id, state.num_grids >>> state.bounds.xmin, state.bounds.xmax >>> len(state.scalar_functions), len(state.objects.boundary_list) """ data_loaded: bool dataset_id: int | None data_format: str reader_name: str grid_file: str result_file: str num_grids: int bounds: DatasetBounds transient: bool has_solution_times: bool cur_time_step: int total_time_steps: int scalar_functions: list[str] vector_functions: list[str] objects: object # ObjectLists from fieldview._objects; typed as object to avoid circular import windows: WindowList | None = None
def _build_window_info(payload: dict[str, object]) -> WindowInfo: parent_window = payload.get("parent_window") environment_id = payload.get("environment_id") dataset_ids = [ _to_int(dataset_id) for dataset_id in cast(Iterable[object], payload.get("dataset_ids", [])) ] return WindowInfo( window=_to_int(payload.get("window", 0)), current=bool(payload.get("current", False)), scene_index=_to_int(payload.get("scene_index", -1)), parent_window=None if parent_window is None else _to_int(parent_window), label=None if payload.get("label") is None else str(payload.get("label")), environment=None if payload.get("environment") is None else str(payload.get("environment")), view_sync_enabled=bool(payload.get("view_sync_enabled", False)), background=None if payload.get("background") is None else str(payload.get("background")), dataset_ids=dataset_ids, environment_id=None if environment_id is None else _to_int(environment_id), background_image=None if payload.get("background_image") is None else str(payload.get("background_image")), ) def _build_window_list(payload: dict[str, object] | None) -> WindowList | None: if not isinstance(payload, dict): return None current_window = payload.get("current_window") if current_window is not None: current_window = _to_int(current_window) windows = [ _build_window_info(cast(dict[str, object], item)) for item in cast(Iterable[object], payload.get("windows", [])) if isinstance(item, dict) ] return WindowList(current_window=current_window, windows=windows) def _build_window_split_result(payload: dict[str, object]) -> WindowSplitResult: return WindowSplitResult( source_window=_to_int(payload.get("source_window", 0)), new_window=_to_int(payload.get("new_window", 0)), mode=str(payload.get("mode", "")), orientation=str(payload.get("orientation", "")), ) def _build_perspective_state(payload: dict[str, object]) -> PerspectiveState: return PerspectiveState( enabled=bool(payload.get("enabled", False)), angle=_to_float(payload.get("angle", 0.0)), ) def _build_camera_pose(payload: dict[str, object]) -> CameraPose: def _tuple3(key: str) -> tuple[float, float, float]: values = payload.get(key, (0.0, 0.0, 0.0)) if not isinstance(values, (list, tuple)): values = (0.0, 0.0, 0.0) values_tuple = tuple(cast(tuple[object, ...], values)) if len(values_tuple) != 3: values_tuple = (0.0, 0.0, 0.0) return ( _to_float(values_tuple[0]), _to_float(values_tuple[1]), _to_float(values_tuple[2]), ) return CameraPose( eye=_tuple3("eye"), target=_tuple3("target"), up=_tuple3("up"), perspective_enabled=bool(payload.get("perspective_enabled", False)), perspective_angle=_to_float(payload.get("perspective_angle", 0.0)), ) def _build_view_state(payload: dict[str, object]) -> ViewState: exact_state_obj = payload.get("exact_state") if not isinstance(exact_state_obj, dict): exact_state_obj = {} exact_payload = cast(dict[object, object], exact_state_obj) def _tuple3_value( value: object, default: tuple[float, float, float] ) -> tuple[float, float, float]: if not isinstance(value, (list, tuple)): value = default values_tuple = tuple(cast(tuple[object, ...], value)) if len(values_tuple) != 3: values_tuple = default return ( _to_float(values_tuple[0]), _to_float(values_tuple[1]), _to_float(values_tuple[2]), ) exact_state = CameraExactState( zoom=_to_float(exact_payload.get("zoom", 1.0)), scale=_to_float(exact_payload.get("scale", 1.0)), rotation_angle=_to_float(exact_payload.get("rotation_angle", 0.0)), rotation_axis=_tuple3_value( exact_payload.get("rotation_axis", (0.0, 0.0, 1.0)), (0.0, 0.0, 1.0), ), translation=_tuple3_value( exact_payload.get("translation", (0.0, 0.0, 0.0)), (0.0, 0.0, 0.0), ), perspective_z=_to_float(exact_payload.get("perspective_z", 0.0)), rotation_center_on=bool(exact_payload.get("rotation_center_on", False)), rotation_center=_tuple3_value( exact_payload.get("rotation_center", (0.0, 0.0, 0.0)), (0.0, 0.0, 0.0), ), ) return ViewState.from_exact( exact_state, perspective_enabled=bool(payload.get("perspective_enabled", False)), perspective_angle=_to_float(payload.get("perspective_angle", 0.0)), )
[docs] def get_session_state(include_functions: bool = True) -> SessionState: """Return a snapshot of the current FieldView session. Retrieves dataset metadata, domain bounds, function lists, existing scene objects, and window/layout state in a single dispatcher round-trip. If no dataset is loaded, ``data_loaded`` is ``False`` and most fields are empty or ``None``. As a side effect, the current dataset is attached into the Python registry so that a subsequent :func:`get_current` call returns the cached wrapper at no extra cost. This is the recommended first call for any automation script entering a live FieldView session with unknown state. The returned :class:`SessionState` is a compact snapshot with: - dataset metadata and file names - full domain bounds - transient/time-step information - scalar/vector function name lists - all current graphics objects grouped in ``state.objects`` - current window/layout information in ``state.windows`` Append-vs-replace invalidation semantics: - Replace-style reloads invalidate previously attached wrappers for the replaced dataset slot. Re-acquire wrappers after the reload completes. - Append-style reads allocate a new dataset id and leave existing wrappers valid. Example: .. code-block:: python >>> import os >>> import fieldview as fv >>> data_dir = os.path.join(fv.home, "examples", "f18") >>> ds = fv.data.load_plot3d( ... os.path.join(data_dir, "f18i9b_g_bin"), ... os.path.join(data_dir, "f18i9b_q_bin"), ... ) >>> state = fv.data.get_session_state() >>> current = fv.data.get_current() # cached after get_session_state() >>> mid_x = (state.bounds.xmin + state.bounds.xmax) / 2.0 >>> len(state.scalar_functions), len(state.objects.boundary_list) :param include_functions: When ``False``, scalar/vector function lists are omitted from the response. Useful for large datasets where the script only needs object/bounds context. """ from ._objects import ObjectLists, _build_group # local import avoids circular _get_session_state_fn = getattr(_core_module, "get_session_state", None) if not callable(_get_session_state_fn): raise CoreError("get_session_state: not supported by this FieldView build") # parse_response_dict_or_throw in C++ already validates "ok" and strips it, # so the dict we receive here has no "ok" key — errors throw before we get here. resp = cast(dict[str, object], _core_call(_get_session_state_fn, include_functions)) data_loaded = bool(resp.get("data_loaded", False)) bounds = DatasetBounds( xmin=_to_float(resp.get("xmin", 0.0)), xmax=_to_float(resp.get("xmax", 0.0)), ymin=_to_float(resp.get("ymin", 0.0)), ymax=_to_float(resp.get("ymax", 0.0)), zmin=_to_float(resp.get("zmin", 0.0)), zmax=_to_float(resp.get("zmax", 0.0)), rmin=_to_float(resp.get("rmin", 0.0)), rmax=_to_float(resp.get("rmax", 0.0)), tmin=_to_float(resp.get("tmin", 0.0)), tmax=_to_float(resp.get("tmax", 0.0)), ) objects = ObjectLists( boundary_list=_build_group(resp, "boundary_list"), comp_list=_build_group(resp, "comp_list"), coord_list=_build_group(resp, "coord_list"), iso_list=_build_group(resp, "iso_list"), streamlines_list=_build_group(resp, "streamlines_list"), particle_paths_list=_build_group(resp, "particle_paths_list") if "particle_paths_list" in resp else [], plot2d_list=_build_group(resp, "plot2d_list") if "plot2d_list" in resp else [], vortex_cores_list=_build_group(resp, "vortex_cores_list") if "vortex_cores_list" in resp else [], surface_flows_list=_build_group(resp, "surface_flows_list") if "surface_flows_list" in resp else [], separation_lines_list=_build_group(resp, "separation_lines_list") if "separation_lines_list" in resp else [], reattachment_lines_list=_build_group(resp, "reattachment_lines_list") if "reattachment_lines_list" in resp else [], annotation_text_list=_build_group(resp, "annotation_text_list"), annotation_arrow_list=_build_group(resp, "annotation_arrow_list"), ) # Side effect: attach the current dataset into the registry so get_current() # returns a cached wrapper without a second round-trip. if data_loaded: dataset_id = resp.get("dataset_id") if dataset_id is not None: try: _get_or_attach_dataset(_to_int(dataset_id)) except InvalidDatasetError: pass scalar_funcs = [ str(cast(dict[str, object], f).get("name", "")) if isinstance(f, dict) else str(f) for f in cast(Iterable[object], resp.get("scalar_functions", [])) ] vector_funcs = [ str(cast(dict[str, object], f).get("name", "")) if isinstance(f, dict) else str(f) for f in cast(Iterable[object], resp.get("vector_functions", [])) ] windows = _build_window_list(cast(dict[str, object] | None, resp.get("windows"))) dataset_id_value = resp.get("dataset_id") dataset_id = int(dataset_id_value) if isinstance(dataset_id_value, int) else None return SessionState( data_loaded=data_loaded, dataset_id=dataset_id, data_format=str(resp.get("data_format", "")), reader_name=str(resp.get("reader_name", "")), grid_file=str(resp.get("grid_file", "")), result_file=str(resp.get("result_file", "")), num_grids=_to_int(resp.get("num_grids", 0)), bounds=bounds, transient=bool(resp.get("transient", False)), has_solution_times=bool(resp.get("has_solution_times", False)), cur_time_step=_to_int(resp.get("cur_time_step", 0)), total_time_steps=_to_int(resp.get("total_time_steps", 0)), scalar_functions=scalar_funcs, vector_functions=vector_funcs, objects=objects, windows=windows, )
[docs] def get_current() -> Dataset: """Return the current dataset from the live FieldView session. If the dataset was loaded through the FieldView UI before Python attached to the session, this call reconstructs and caches a live :class:`Dataset` wrapper on demand instead of failing on a Python-registry miss. Replace-style reloads invalidate the previous wrapper for that dataset slot. Append-style reads allocate a new dataset id and leave existing wrappers valid. Example: .. code-block:: python >>> import os >>> import fieldview as fv >>> data_dir = os.path.join(fv.home, "examples", "f18") >>> ds = fv.data.load_plot3d( ... os.path.join(data_dir, "f18i9b_g_bin"), ... os.path.join(data_dir, "f18i9b_q_bin"), ... ) >>> current = fv.data.get_current() >>> current.dataset_id == ds.dataset_id """ global _current_dataset get_current_id = getattr(_core_module, "dataset_get_current_id", None) if callable(get_current_id): try: dsnum = _to_int(_core_call(get_current_id)) except CoreError as exc: raise InvalidDatasetError(str(exc)) from exc if dsnum < 0: raise InvalidDatasetError( "No current dataset is set. Load a dataset or call fieldview.data.set_current()." ) dataset_ref = _get_or_attach_dataset(dsnum) _current_dataset = dataset_ref return dataset_ref current_dataset = _current_dataset if current_dataset is None: raise InvalidDatasetError( "No current dataset is set. Load a dataset or call fieldview.data.set_current()." ) current_dataset._ensure_valid() return current_dataset
def set_current(ds: Dataset) -> None: """Set the current dataset used by create_* helpers when dataset is omitted. Args: ds: Dataset instance to make current for subsequent helper calls that omit an explicit dataset argument. """ if not isinstance(ds, Dataset): raise InvalidArgumentError("ds must be a Dataset instance") if ds.dataset_id < 0: raise InvalidDatasetError("dataset is invalid (not loaded)") ds._ensure_valid() set_current_id = getattr(_core_module, "dataset_set_current_id", None) if callable(set_current_id): _core_call(set_current_id, int(ds.dataset_id)) _dataset_registry[ds.dataset_id] = ds global _current_dataset _current_dataset = ds dataset: type[Dataset] = Dataset