Skip to content

cfdtwin.Project

cfdtwin.Project

A CFDTwin project — a folder on disk, plus optional Fluent connection.

Construct via Project.create(path, name) or Project.open(path)Project(path) is reserved for internal use.

create classmethod

create(project_path: str | Path, name: str) -> 'Project'

Create a new project folder. Errors if the folder already contains a project_info.json.

open classmethod

open(project_path: str | Path) -> 'Project'

Open an existing project folder. Errors if no project_info.json is found.

set_case_file

set_case_file(case_file_path: str | Path) -> None

Store the .cas / .cas.h5 file reference path. Doesn't touch Fluent.

set_inputs

set_inputs(inputs: dict) -> None

Declare inputs (no Fluent connection required).

Three accepted entry shapes — choose one per input:

# 1. BC range tuple (lo, hi). bc_type and parameter_path resolve at run time.
project.set_inputs({"inlet|velocity": (0.1, 1.0)})

# 2. BC rich dict — fully declarative, no Fluent needed at any stage.
project.set_inputs({
    "inlet|velocity": {
        "range": (0.1, 1.0),
        "bc_type": "velocity-inlet",
        "parameter_path": "vmag.value",
    },
})

# 3. Fluent input parameter (named expression with input_parameter=True).
# Key is the expression name; pass category="Input Parameter" and
# the unit string. Easiest path: feed back from list_available_inputs().
project.set_inputs({
    "inlet_vel": {
        "range": (0.2, 0.8),
        "category": "Input Parameter",
        "unit": "m/s",
    },
})

BC keys use "|" (e.g. "inlet|velocity"). Input-parameter keys are just the expression name.

list_available_inputs

list_available_inputs() -> list[dict]

Discover BCs + input parameters from the connected Fluent session.

Requires a live Fluent connection (call connect_fluent() first). Each returned dict has at least name, type, category; Input Parameter entries also carry unit, current_value, and definition. The dicts can be fed back into set_inputs after adding a range::

project.connect_fluent()
for item in project.list_available_inputs():
    print(item['name'], item['category'])

set_outputs

set_outputs(outputs: list[dict]) -> None

Declare outputs to extract from each simulation.

Each entry is a dict:

project.set_outputs([
    {"name": "outlet_temp", "category": "Report Definition"},
    {"name": "mid_plane",  "category": "Surface",
     "field_variables": ["temperature", "velocity-magnitude"]},
    {"name": "fluid",      "category": "Cell Zone",
     "field_variables": ["temperature"]},
])

category must be one of: "Report Definition", "Surface", "Cell Zone". field_variables is required for Surface and Cell Zone, ignored for Report Definition (the report's value is the only output).

generate_doe

generate_doe(n: int | None = None, method: str = 'lhs', points_per_param: int | None = None, seed: int = 42, append: bool = False) -> int

Generate DOE samples within the ranges from set_inputs(...).

Parameters

n : int, optional Sample count. Required for method='lhs', ignored for 'factorial'. method : str, default 'lhs' 'lhs' (Latin hypercube) or 'factorial' (full grid). points_per_param : int, optional Required for method='factorial'. Total samples = n_per_param ** n_inputs. seed : int, default 42 Random seed for LHS reproducibility. append : bool, default False If True, add to existing samples; otherwise replace.

Returns

int Total number of samples now stored.

connect_fluent

connect_fluent(precision: str | None = None, processor_count: int | None = None, dimension: int | None = None, use_gui: bool | None = None) -> None

Launch Fluent and load the project's case file.

Any kwarg passed here overrides the matching field from the project's stored solver_settings (or the built-in defaults if none stored). Pass precision="single" when the case file was saved in single precision — Fluent will refuse to read a single-precision case in a double-precision session.

Holds the solver handle on the Project; subsequent calls re-use it.

run_simulations

run_simulations(iterations: int | None = None, reinitialize: bool = True, verbose: bool = False, on_progress: Callable | None = None, on_iteration: Callable | None = None) -> SimulationResult

Run all incomplete DOE simulations. Auto-connects Fluent if needed.

Parameters

iterations None (default) honors the iteration count baked into the case file. Pass an int to override for this batch. reinitialize Re-run hybrid initialization before each sim. Usually what you want. verbose If True, prints one "sim X/N " line per simulation as it starts and finishes. Ignored if on_progress is supplied — your callback takes over. on_progress callback(idx, total, sim_id, status) — fires when each sim starts, finishes, or fails. on_iteration callback(sim_id, iter_num, total_iters) — fires per Fluent iteration. Use sparingly; very chatty for long sim batches.

train

train(model_name: str, outputs=None, epochs: int = 500, test_size: float = 0.2, random_seed: int = 42, exclude_range: tuple[int, int] | None = None, excluded_ids: list[int] | None = None, on_progress: Callable | None = None, on_epoch: Callable | None = None, verbose: int = 1) -> TrainingResult

Train surrogate models for the requested outputs.

Parameters

model_name : str Folder name under project/models/. Auto-suffixed (_2, _3, ...) on collision; the final name is in result.model_name. Letters/digits/_/- only. outputs : None | list[str] | dict, default None None = train every (location, field_variable) in the project, with defaults.

**list of str** = train just those model_keys
(``"<location>_<field>"``), with defaults::

    outputs=["mid_plane_temperature", "outlet_temp_value"]

**dict** = per-output overrides::

    outputs={
        "mid_plane_temperature": {
            "pod": {"modes": 20},          # OR {"variance": 0.99}
            "nn":  {"learning_rate": 5e-4, "hidden_layers": [128, 64]},
        },
        "outlet_temp_value": {
            "nn": {"preset": "1d"},        # switch base preset
        },
    }

epochs, test_size, random_seed, exclude_range : training defaults excluded_ids : list[int], optional Arbitrary 1-indexed sim_ids to withhold from training. Combined with exclude_range (a sample is dropped if either matches).

predict

predict(model_name: str, params) -> PredictionResult

Run a trained model on input parameters.

Parameters

model_name : str Name of the model directory under project/models/. params : dict | list[dict] dict — single sample::

    project.predict("my_run", {"inlet|velocity": 0.5})

**list of dict** — batch (one prediction per dict)::

    project.predict("my_run", [
        {"inlet|velocity": 0.5},
        {"inlet|velocity": 0.7},
    ])

list_models

list_models() -> list[str]

Return sorted list of model folder names under project/models/.

model_info

model_info(model_name: str) -> ModelInfo

Return the trained-model metadata. Raises KeyError if missing.

For models that bundle multiple sub-models (legacy layout — one folder held both temperature and velocity), this returns metadata for the first metadata.json found. Use a more specific model_name to target the per-field model.

delete_model

delete_model(model_name: str) -> None

Delete a trained model directory. Raises KeyError if not found.

export_model

export_model(model_name: str, dest_path: str | Path) -> Path

Copy a trained model directory to dest_path / model_name.

Returns the destination path. Existing folder at the destination is replaced (after checking that source exists).