Skip to content

common

Value objects and helpers shared across the library, belonging to no single model package.

  • FluidState — A fluid at a thermodynamic state point: fluid name, pressure, temperature, and density. What a feed system hands the injector, so neither package has to import the other.
  • DryMassProperties — Mass, center of gravity, and inertia tensor of a device's dry structure.
  • Array manipulation helpers and a @timing decorator for profiling.
  • require() — Imports an optional dependency, raising MissingOptionalDependencyError naming the packaging extra that ships it.

machwave.common.fluid_state

FluidState dataclass

A fluid at a thermodynamic state point.

Attributes:

Name Type Description
fluid_name str

Name of the fluid in the CoolProp database.

pressure float

Pressure [Pa].

temperature float

Temperature [K].

density float

Density [kg/m^3].

Source code in machwave/common/fluid_state.py
@dataclasses.dataclass(frozen=True, kw_only=True, slots=True)
class FluidState:
    """
    A fluid at a thermodynamic state point.

    Attributes:
        fluid_name: Name of the fluid in the CoolProp database.
        pressure: Pressure [Pa].
        temperature: Temperature [K].
        density: Density [kg/m^3].
    """

    fluid_name: str
    pressure: float
    temperature: float
    density: float

machwave.common.mass_properties

DryMassProperties dataclass

Properties common to devices that contain dry mass.

Attributes:

Name Type Description
dry_mass float

Dry mass scalar [kg].

center_of_gravity_coordinate tuple[float, float, float]

Dry mass center of gravity position measured from the nozzle exit (x, y, z) [m]. Positive x points toward the bulkhead.

moment_of_inertia tuple[float, float, float]

Dry mass principal moments of inertia (I_11, I_22, I_33) [kg-m^2], evaluated at the dry mass center of gravity. Assumes the thrust chamber is aligned with the x-axis.

Source code in machwave/common/mass_properties.py
@dataclasses.dataclass(frozen=True, kw_only=True)
class DryMassProperties:
    """
    Properties common to devices that contain dry mass.

    Attributes:
        dry_mass: Dry mass scalar [kg].
        center_of_gravity_coordinate: Dry mass center of gravity position measured from
            the nozzle exit `(x, y, z)` [m]. Positive x points toward the bulkhead.
        moment_of_inertia: Dry mass principal moments of inertia `(I_11, I_22, I_33)`
            [kg-m^2], evaluated at the dry mass center of gravity. Assumes the thrust
            chamber is aligned with the x-axis.
    """

    dry_mass: float
    center_of_gravity_coordinate: tuple[float, float, float]
    moment_of_inertia: tuple[float, float, float]

    def __post_init__(self) -> None:
        """
        Validate and normalize the dry mass properties.

        Raises:
            ValueError: If the dry mass is non-positive or non-finite, either triple is
                malformed, or a moment of inertia is negative or non-finite.
        """
        if not math.isfinite(self.dry_mass) or self.dry_mass <= 0.0:
            raise ValueError(
                f"dry_mass must be a positive finite number, got {self.dry_mass}"
            )

        cog = _as_3d_vector(
            "center_of_gravity_coordinate", self.center_of_gravity_coordinate
        )
        moment_of_inertia = _as_3d_vector("moment_of_inertia", self.moment_of_inertia)

        if any(not math.isfinite(c) or c < 0.0 for c in moment_of_inertia):
            raise ValueError(
                "moment_of_inertia components must be non-negative finite numbers, got "
                f"{moment_of_inertia}"
            )

        object.__setattr__(self, "center_of_gravity_coordinate", cog)
        object.__setattr__(self, "moment_of_inertia", moment_of_inertia)

__post_init__()

Validate and normalize the dry mass properties.

Raises:

Type Description
ValueError

If the dry mass is non-positive or non-finite, either triple is malformed, or a moment of inertia is negative or non-finite.

Source code in machwave/common/mass_properties.py
def __post_init__(self) -> None:
    """
    Validate and normalize the dry mass properties.

    Raises:
        ValueError: If the dry mass is non-positive or non-finite, either triple is
            malformed, or a moment of inertia is negative or non-finite.
    """
    if not math.isfinite(self.dry_mass) or self.dry_mass <= 0.0:
        raise ValueError(
            f"dry_mass must be a positive finite number, got {self.dry_mass}"
        )

    cog = _as_3d_vector(
        "center_of_gravity_coordinate", self.center_of_gravity_coordinate
    )
    moment_of_inertia = _as_3d_vector("moment_of_inertia", self.moment_of_inertia)

    if any(not math.isfinite(c) or c < 0.0 for c in moment_of_inertia):
        raise ValueError(
            "moment_of_inertia components must be non-negative finite numbers, got "
            f"{moment_of_inertia}"
        )

    object.__setattr__(self, "center_of_gravity_coordinate", cog)
    object.__setattr__(self, "moment_of_inertia", moment_of_inertia)

machwave.common.arrays

replace_array_values(arr, to_replace, value)

Replaces values in a NumPy array with another value.

Parameters:

Name Type Description Default
arr NDArray[number]

The array in which to replace values.

required
to_replace int | float

The value to be replaced.

required
value int | float

The value to replace with.

required

Returns:

Type Description
NDArray[number]

A new array with the values replaced.

Source code in machwave/common/arrays.py
def replace_array_values(
    arr: npt.NDArray[np.number],
    to_replace: int | float,
    value: int | float,
) -> npt.NDArray[np.number]:
    """
    Replaces values in a NumPy array with another value.

    Args:
        arr: The array in which to replace values.
        to_replace: The value to be replaced.
        value: The value to replace with.

    Returns:
        A new array with the values replaced.
    """
    arr = arr.copy()
    arr[arr == to_replace] = value
    return arr

machwave.common.decorators

timing(f)

Decorator to print the execution time of a function.

Parameters:

Name Type Description Default
f F

The function to be timed.

required

Returns:

Type Description
F

The wrapped function with added timing functionality.

Source code in machwave/common/decorators.py
def timing(f: F) -> F:
    """
    Decorator to print the execution time of a function.

    Args:
        f: The function to be timed.

    Returns:
        The wrapped function with added timing functionality.
    """

    @functools.wraps(f)
    def wrapper(*args: typing.Any, **kwargs: typing.Any) -> typing.Any:
        start_time = time.time()
        result = f(*args, **kwargs)
        end_time = time.time()
        print(f"\nExecution time: {end_time - start_time:.4f} seconds")
        return result

    return typing.cast(F, wrapper)

machwave.common.objects

get_object_dict(obj)

Extract the attribute dictionary from an object.

Parameters:

Name Type Description Default
obj Any

Any Python object to inspect.

required

Returns:

Type Description
dict[str, Any]

A dictionary mapping attribute names to their values, or an empty dictionary

dict[str, Any]

if the object has no dict.

Examples:

>>> class Example:
...     def __init__(self):
...         self.x = 1
...         self.y = 2
>>> get_object_dict(Example())
{'x': 1, 'y': 2}
Source code in machwave/common/objects.py
def get_object_dict(obj: typing.Any) -> dict[str, typing.Any]:
    """
    Extract the attribute dictionary from an object.

    Args:
        obj: Any Python object to inspect.

    Returns:
        A dictionary mapping attribute names to their values, or an empty dictionary
        if the object has no __dict__.

    Examples:
        >>> class Example:
        ...     def __init__(self):
        ...         self.x = 1
        ...         self.y = 2
        >>> get_object_dict(Example())
        {'x': 1, 'y': 2}
    """
    try:
        return vars(obj)
    except TypeError:
        return {}

machwave.common.extras

Resolution of the optional dependencies behind Machwave's packaging extras.

Modules reached only once a feature is used guard their own import at module level and re-raise MissingOptionalDependencyError; that keeps the third-party names real and fully typed. require is for modules on the import path, which must stay importable without their dependency. It returns the module untyped, so prefer the module-level guard wherever both work.

MissingOptionalDependencyError

Bases: ImportError

Raised when a feature needs an optional dependency that is not installed.

Source code in machwave/common/extras.py
class MissingOptionalDependencyError(ImportError):
    """Raised when a feature needs an optional dependency that is not installed."""

    def __init__(
        self, module_name: str, extra: str, feature: str | None = None
    ) -> None:
        """
        Build the error from the missing dependency and the extra that ships it.

        Args:
            module_name: Import name of the package, e.g. `skimage.measure`.
            extra: Packaging extra that installs it.
            feature: Plural noun phrase naming the feature, overriding the default
                wording for `extra`. Use it when one extra gates more than one
                feature.
        """
        top_level = module_name.partition(".")[0]
        distribution = _DISTRIBUTION_BY_MODULE.get(top_level, top_level)
        feature = feature or _FEATURE_BY_EXTRA[extra]

        self.extra = extra

        super().__init__(
            f"{feature} require the `{distribution}` package. "
            f"Install it with `pip install machwave[{extra}]`."
        )

__init__(module_name, extra, feature=None)

Build the error from the missing dependency and the extra that ships it.

Parameters:

Name Type Description Default
module_name str

Import name of the package, e.g. skimage.measure.

required
extra str

Packaging extra that installs it.

required
feature str | None

Plural noun phrase naming the feature, overriding the default wording for extra. Use it when one extra gates more than one feature.

None
Source code in machwave/common/extras.py
def __init__(
    self, module_name: str, extra: str, feature: str | None = None
) -> None:
    """
    Build the error from the missing dependency and the extra that ships it.

    Args:
        module_name: Import name of the package, e.g. `skimage.measure`.
        extra: Packaging extra that installs it.
        feature: Plural noun phrase naming the feature, overriding the default
            wording for `extra`. Use it when one extra gates more than one
            feature.
    """
    top_level = module_name.partition(".")[0]
    distribution = _DISTRIBUTION_BY_MODULE.get(top_level, top_level)
    feature = feature or _FEATURE_BY_EXTRA[extra]

    self.extra = extra

    super().__init__(
        f"{feature} require the `{distribution}` package. "
        f"Install it with `pip install machwave[{extra}]`."
    )

require(module_name, extra, feature=None)

Import an optional dependency, naming the extra that ships it on failure.

Parameters:

Name Type Description Default
module_name str

Import name of the package, e.g. skimage.measure.

required
extra str

Packaging extra that installs it.

required
feature str | None

Plural noun phrase naming the feature, overriding the default wording for extra.

None

Returns:

Type Description
ModuleType

The imported module.

Raises:

Type Description
MissingOptionalDependencyError

If the package is not installed.

Source code in machwave/common/extras.py
def require(
    module_name: str, extra: str, feature: str | None = None
) -> types.ModuleType:
    """
    Import an optional dependency, naming the extra that ships it on failure.

    Args:
        module_name: Import name of the package, e.g. `skimage.measure`.
        extra: Packaging extra that installs it.
        feature: Plural noun phrase naming the feature, overriding the default
            wording for `extra`.

    Returns:
        The imported module.

    Raises:
        MissingOptionalDependencyError: If the package is not installed.
    """
    try:
        return importlib.import_module(module_name)
    except ImportError as e:
        raise MissingOptionalDependencyError(module_name, extra, feature) from e