cfdtwin 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. cfdtwin-0.1.0/.github/workflows/ci.yml +44 -0
  2. cfdtwin-0.1.0/.github/workflows/docs.yml +30 -0
  3. cfdtwin-0.1.0/.github/workflows/publish.yml +28 -0
  4. cfdtwin-0.1.0/.gitignore +63 -0
  5. cfdtwin-0.1.0/LICENSE +21 -0
  6. cfdtwin-0.1.0/PKG-INFO +117 -0
  7. cfdtwin-0.1.0/README.md +88 -0
  8. cfdtwin-0.1.0/cfdtwin/__init__.py +39 -0
  9. cfdtwin-0.1.0/cfdtwin/_project_manager.py +199 -0
  10. cfdtwin-0.1.0/cfdtwin/_project_system.py +340 -0
  11. cfdtwin-0.1.0/cfdtwin/doe.py +390 -0
  12. cfdtwin-0.1.0/cfdtwin/fluent.py +139 -0
  13. cfdtwin-0.1.0/cfdtwin/metrics.py +65 -0
  14. cfdtwin-0.1.0/cfdtwin/nn.py +317 -0
  15. cfdtwin-0.1.0/cfdtwin/output_parameters.py +42 -0
  16. cfdtwin-0.1.0/cfdtwin/pod.py +138 -0
  17. cfdtwin-0.1.0/cfdtwin/project.py +700 -0
  18. cfdtwin-0.1.0/cfdtwin/results.py +163 -0
  19. cfdtwin-0.1.0/cfdtwin/simulation.py +891 -0
  20. cfdtwin-0.1.0/cfdtwin/training.py +565 -0
  21. cfdtwin-0.1.0/cfdtwin/visualization.py +813 -0
  22. cfdtwin-0.1.0/docs/api/project.md +5 -0
  23. cfdtwin-0.1.0/docs/api/results.md +23 -0
  24. cfdtwin-0.1.0/docs/assets/logo_icon.png +0 -0
  25. cfdtwin-0.1.0/docs/assets/logo_wordmark.png +0 -0
  26. cfdtwin-0.1.0/docs/examples/README.md +30 -0
  27. cfdtwin-0.1.0/docs/examples/discovering_bcs.py +123 -0
  28. cfdtwin-0.1.0/docs/examples/full_workflow.py +90 -0
  29. cfdtwin-0.1.0/docs/examples/quickstart.py +48 -0
  30. cfdtwin-0.1.0/docs/examples/training_tuning.py +61 -0
  31. cfdtwin-0.1.0/docs/index.md +55 -0
  32. cfdtwin-0.1.0/docs/quickstart.md +62 -0
  33. cfdtwin-0.1.0/docs/stylesheets/extra.css +18 -0
  34. cfdtwin-0.1.0/docs/tutorials/fluent_session.md +132 -0
  35. cfdtwin-0.1.0/docs/tutorials/full_workflow.md +154 -0
  36. cfdtwin-0.1.0/docs/tutorials/training_tuning.md +132 -0
  37. cfdtwin-0.1.0/gui/__init__.py +0 -0
  38. cfdtwin-0.1.0/gui/__main__.py +3 -0
  39. cfdtwin-0.1.0/gui/app.py +144 -0
  40. cfdtwin-0.1.0/gui/assets/logo_icon.png +0 -0
  41. cfdtwin-0.1.0/gui/assets/logo_wordmark.png +0 -0
  42. cfdtwin-0.1.0/gui/dataset_manager.py +73 -0
  43. cfdtwin-0.1.0/gui/error_helpers.py +66 -0
  44. cfdtwin-0.1.0/gui/fluent_cache.py +93 -0
  45. cfdtwin-0.1.0/gui/fluent_manager.py +134 -0
  46. cfdtwin-0.1.0/gui/main_window.py +337 -0
  47. cfdtwin-0.1.0/gui/pages/__init__.py +0 -0
  48. cfdtwin-0.1.0/gui/pages/doe_page.py +365 -0
  49. cfdtwin-0.1.0/gui/pages/setup_page.py +936 -0
  50. cfdtwin-0.1.0/gui/pages/simulate_page.py +323 -0
  51. cfdtwin-0.1.0/gui/pages/train_page.py +519 -0
  52. cfdtwin-0.1.0/gui/pages/validate_page.py +1418 -0
  53. cfdtwin-0.1.0/gui/project_dialog.py +368 -0
  54. cfdtwin-0.1.0/gui/settings_dialog.py +317 -0
  55. cfdtwin-0.1.0/gui/spinner.py +109 -0
  56. cfdtwin-0.1.0/gui/theme.py +420 -0
  57. cfdtwin-0.1.0/gui/user_settings.py +92 -0
  58. cfdtwin-0.1.0/gui/workers.py +242 -0
  59. cfdtwin-0.1.0/mkdocs.yml +66 -0
  60. cfdtwin-0.1.0/pyproject.toml +59 -0
@@ -0,0 +1,44 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ branches: [master]
8
+
9
+ jobs:
10
+ unit-tests:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+ cache: pip
24
+
25
+ - name: Install Qt system libs
26
+ # pytest-qt (in our dev extra) auto-imports PySide6 at session
27
+ # start. The bare ubuntu-latest runner has no libEGL/libGL/xcb,
28
+ # so plugin load INTERNALERRORs before any test runs.
29
+ run: |
30
+ sudo apt-get update
31
+ sudo apt-get install -y libegl1 libgl1 libxkbcommon0 libdbus-1-3 \
32
+ libxcb-cursor0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 \
33
+ libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxcb-xinerama0 \
34
+ libxcb-xkb1 libfontconfig1
35
+
36
+ - name: Install package and dev dependencies
37
+ run: |
38
+ python -m pip install --upgrade pip
39
+ pip install -e ".[dev]"
40
+
41
+ - name: Run unit tests
42
+ env:
43
+ QT_QPA_PLATFORM: offscreen # headless runner
44
+ run: pytest tests/unit -v
@@ -0,0 +1,30 @@
1
+ name: Build & deploy docs
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+
7
+ permissions:
8
+ contents: write # mkdocs gh-deploy pushes to gh-pages branch
9
+
10
+ jobs:
11
+ deploy-docs:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ with:
16
+ fetch-depth: 0 # full history needed for mkdocs gh-deploy
17
+
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.12"
21
+ cache: pip
22
+
23
+ - name: Install docs dependencies
24
+ run: |
25
+ python -m pip install --upgrade pip
26
+ pip install mkdocs mkdocs-material "mkdocstrings[python]"
27
+ pip install -e .
28
+
29
+ - name: Deploy
30
+ run: mkdocs gh-deploy --force
@@ -0,0 +1,28 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"] # tagging vX.Y.Z triggers a release
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ environment:
11
+ name: pypi
12
+ url: https://pypi.org/p/cfdtwin
13
+ permissions:
14
+ id-token: write # required for PyPI Trusted Publishing
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.12"
21
+
22
+ - name: Build wheel + sdist
23
+ run: |
24
+ python -m pip install --upgrade pip build
25
+ python -m build
26
+
27
+ - name: Publish to PyPI
28
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,63 @@
1
+ # Temporary and cache files
2
+ *.trn
3
+ .claude/
4
+ *.bat
5
+ *.log
6
+ nul
7
+
8
+ # Python cache
9
+ __pycache__/
10
+ .pytest_cache/
11
+ /Field Surrogate/__pycache__/
12
+ /Field Surrogate/modules/__pycache__/
13
+ /Volume Surrogate/__pycache__/
14
+ /Volume Surrogate/modules/__pycache__/
15
+ /Scalar Surrogate/cache/
16
+
17
+ # Data and output files
18
+ # Images
19
+ *.png
20
+ # ...but keep the brand logos: needed by the GUI at runtime, by the README
21
+ # on GitHub/PyPI, and by the docs site.
22
+ !gui/assets/*.png
23
+ !docs/assets/*.png
24
+
25
+ # Model files (ignore H5 Keras models but allow NPZ inside dataset folders)
26
+ *.h5
27
+
28
+ # Dataset output folders (contains NPZ files, trained models, and visualizations)
29
+ /Field Surrogate/field_surrogate_*/
30
+ /Volume Surrogate/volume_surrogate_*/
31
+ /Volume Surrogate/test_volume_data.npz
32
+ /Volume Surrogate/test_volume_temperature_3d.png
33
+
34
+ # Documentation
35
+ /Field Surrogate/context.md
36
+ /Volume Surrogate/context.md
37
+
38
+ #python scripts
39
+ visualize_pod.py
40
+
41
+ # Project data (kept out of git)
42
+ Testing/
43
+ tests/
44
+ validations/
45
+
46
+ # Personal scratch notes (kept locally)
47
+ BACKLOG.md
48
+
49
+ # Built docs site (regenerated by GitHub Actions)
50
+ site/
51
+
52
+ # Build artifacts
53
+ *.egg-info/
54
+ build/
55
+ dist/
56
+
57
+ # SolidWorks lock files
58
+ **/~$*
59
+
60
+ # User settings + runtime logs
61
+ gui/user_settings.json
62
+ gui/cfdtwin.log*
63
+ gui/cfdtwin_fault.log*
cfdtwin-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 UARK-NED3
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
cfdtwin-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,117 @@
1
+ Metadata-Version: 2.4
2
+ Name: cfdtwin
3
+ Version: 0.1.0
4
+ Summary: Neural-network surrogate models for ANSYS Fluent simulations.
5
+ Project-URL: Homepage, https://github.com/UARK-NED3/CFDTwin
6
+ Project-URL: Issues, https://github.com/UARK-NED3/CFDTwin/issues
7
+ Author-email: Danny Curl <dannycurl02@gmail.com>
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Scientific/Engineering
17
+ Requires-Python: >=3.10
18
+ Requires-Dist: ansys-fluent-core
19
+ Requires-Dist: matplotlib
20
+ Requires-Dist: numpy
21
+ Requires-Dist: pyside6
22
+ Requires-Dist: scikit-learn
23
+ Requires-Dist: scipy
24
+ Requires-Dist: tensorflow<2.21,>=2.20
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest; extra == 'dev'
27
+ Requires-Dist: pytest-qt; extra == 'dev'
28
+ Description-Content-Type: text/markdown
29
+
30
+ <p align="center">
31
+ <img src="docs/assets/logo_wordmark.png" alt="CFDTwin" width="420"/>
32
+ </p>
33
+
34
+ Build neural-network surrogate models from ANSYS Fluent simulations.
35
+ Two ways to drive the same pipeline:
36
+
37
+ - **GUI** — wizard-based desktop app, click through Setup → DOE → Simulate → Train → Analyze.
38
+ - **API** — Python library, the same pipeline as ten method calls in a script.
39
+
40
+ **Docs:** https://uark-ned3.github.io/CFDTwin/
41
+ **GUI walkthrough (video):** _coming soon_
42
+
43
+ ## Install
44
+
45
+ Requires Python 3.10+ and a working ANSYS Fluent installation.
46
+
47
+ ```
48
+ pip install cfdtwin # from PyPI (released)
49
+ pip install -e .[dev] # from a clone, with test deps
50
+ ```
51
+
52
+ ## Use the GUI
53
+
54
+ Three ways to launch, pick whichever you prefer:
55
+
56
+ ```
57
+ cfdtwin-gui # any terminal, after pip install
58
+ python -m gui # from a repo clone, no install needed
59
+ double-click scripts/launch_gui.bat # Windows, no terminal pops up
60
+ ```
61
+
62
+ To put a launcher on your Desktop (Windows): right-click `scripts/launch_gui.bat`,
63
+ **Copy**, then right-click your Desktop and **Paste shortcut**. Optionally
64
+ right-click the shortcut → Properties → Change Icon → browse to
65
+ `gui/assets/logo_icon.png` for the CFDTwin logo.
66
+
67
+ On launch, select or create a project. The sidebar steps unlock as prerequisites are met:
68
+
69
+ 1. **Setup** — pick `.cas` file, set Fluent options, define inputs and outputs
70
+ 2. **DOE** — generate LHS/factorial samples
71
+ 3. **Simulate** — batch-run Fluent with live progress
72
+ 4. **Train** — transfer-list filter, live loss curves, per-output NN
73
+ 5. **Analyze** — metrics dashboard, predictions, Fluent comparison with caching
74
+
75
+ ## Use the API
76
+
77
+ Same pipeline from a Python script — useful for automation, parameter sweeps,
78
+ or building bigger surrogate libraries:
79
+
80
+ ```python
81
+ import cfdtwin
82
+
83
+ project = cfdtwin.Project.create("./elbow_study", name="elbow_v1")
84
+ project.set_case_file("mixing_elbow.cas.h5")
85
+
86
+ project.set_inputs({
87
+ "cold-inlet|momentum > velocity_magnitude": (0.2, 0.6),
88
+ "hot-inlet|momentum > velocity_magnitude": (0.4, 1.2),
89
+ })
90
+ project.set_outputs([
91
+ {"name": "outlet", "category": "Surface",
92
+ "field_variables": ["temperature"]},
93
+ ])
94
+
95
+ project.generate_doe(n=20, method="lhs")
96
+ project.connect_fluent(precision="single") # mixing_elbow is a single-precision case
97
+ project.run_simulations()
98
+ project.train(model_name="run1")
99
+
100
+ pred = project.predict("run1", {
101
+ "cold-inlet|momentum > velocity_magnitude": 0.4,
102
+ "hot-inlet|momentum > velocity_magnitude": 0.8,
103
+ })
104
+ print(pred.values.shape)
105
+ ```
106
+
107
+ Runnable scripts live in [`docs/examples/`](docs/examples/) — quickstart,
108
+ full workflow, training tuning, and a "what BCs / parameters does my case
109
+ expose?" discovery script.
110
+
111
+ - [API reference](https://uark-ned3.github.io/CFDTwin/api/project/) — every method, every argument
112
+ - [Tutorials](https://uark-ned3.github.io/CFDTwin/tutorials/full_workflow/) — narrative walk-throughs
113
+ - [Quickstart](https://uark-ned3.github.io/CFDTwin/quickstart/) — smallest end-to-end script
114
+
115
+ ## License
116
+
117
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,88 @@
1
+ <p align="center">
2
+ <img src="docs/assets/logo_wordmark.png" alt="CFDTwin" width="420"/>
3
+ </p>
4
+
5
+ Build neural-network surrogate models from ANSYS Fluent simulations.
6
+ Two ways to drive the same pipeline:
7
+
8
+ - **GUI** — wizard-based desktop app, click through Setup → DOE → Simulate → Train → Analyze.
9
+ - **API** — Python library, the same pipeline as ten method calls in a script.
10
+
11
+ **Docs:** https://uark-ned3.github.io/CFDTwin/
12
+ **GUI walkthrough (video):** _coming soon_
13
+
14
+ ## Install
15
+
16
+ Requires Python 3.10+ and a working ANSYS Fluent installation.
17
+
18
+ ```
19
+ pip install cfdtwin # from PyPI (released)
20
+ pip install -e .[dev] # from a clone, with test deps
21
+ ```
22
+
23
+ ## Use the GUI
24
+
25
+ Three ways to launch, pick whichever you prefer:
26
+
27
+ ```
28
+ cfdtwin-gui # any terminal, after pip install
29
+ python -m gui # from a repo clone, no install needed
30
+ double-click scripts/launch_gui.bat # Windows, no terminal pops up
31
+ ```
32
+
33
+ To put a launcher on your Desktop (Windows): right-click `scripts/launch_gui.bat`,
34
+ **Copy**, then right-click your Desktop and **Paste shortcut**. Optionally
35
+ right-click the shortcut → Properties → Change Icon → browse to
36
+ `gui/assets/logo_icon.png` for the CFDTwin logo.
37
+
38
+ On launch, select or create a project. The sidebar steps unlock as prerequisites are met:
39
+
40
+ 1. **Setup** — pick `.cas` file, set Fluent options, define inputs and outputs
41
+ 2. **DOE** — generate LHS/factorial samples
42
+ 3. **Simulate** — batch-run Fluent with live progress
43
+ 4. **Train** — transfer-list filter, live loss curves, per-output NN
44
+ 5. **Analyze** — metrics dashboard, predictions, Fluent comparison with caching
45
+
46
+ ## Use the API
47
+
48
+ Same pipeline from a Python script — useful for automation, parameter sweeps,
49
+ or building bigger surrogate libraries:
50
+
51
+ ```python
52
+ import cfdtwin
53
+
54
+ project = cfdtwin.Project.create("./elbow_study", name="elbow_v1")
55
+ project.set_case_file("mixing_elbow.cas.h5")
56
+
57
+ project.set_inputs({
58
+ "cold-inlet|momentum > velocity_magnitude": (0.2, 0.6),
59
+ "hot-inlet|momentum > velocity_magnitude": (0.4, 1.2),
60
+ })
61
+ project.set_outputs([
62
+ {"name": "outlet", "category": "Surface",
63
+ "field_variables": ["temperature"]},
64
+ ])
65
+
66
+ project.generate_doe(n=20, method="lhs")
67
+ project.connect_fluent(precision="single") # mixing_elbow is a single-precision case
68
+ project.run_simulations()
69
+ project.train(model_name="run1")
70
+
71
+ pred = project.predict("run1", {
72
+ "cold-inlet|momentum > velocity_magnitude": 0.4,
73
+ "hot-inlet|momentum > velocity_magnitude": 0.8,
74
+ })
75
+ print(pred.values.shape)
76
+ ```
77
+
78
+ Runnable scripts live in [`docs/examples/`](docs/examples/) — quickstart,
79
+ full workflow, training tuning, and a "what BCs / parameters does my case
80
+ expose?" discovery script.
81
+
82
+ - [API reference](https://uark-ned3.github.io/CFDTwin/api/project/) — every method, every argument
83
+ - [Tutorials](https://uark-ned3.github.io/CFDTwin/tutorials/full_workflow/) — narrative walk-throughs
84
+ - [Quickstart](https://uark-ned3.github.io/CFDTwin/quickstart/) — smallest end-to-end script
85
+
86
+ ## License
87
+
88
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,39 @@
1
+ """cfdtwin — neural-network surrogate models for ANSYS Fluent simulations.
2
+
3
+ The public surface is intentionally small. Build a Project, then call its
4
+ methods. Result classes are exported for type hints and isinstance checks.
5
+
6
+ import cfdtwin
7
+
8
+ project = cfdtwin.Project.open("path/to/project")
9
+ project.set_inputs({"inlet|velocity": (0.1, 1.0)})
10
+ project.set_outputs([{"name": "outlet_temp", "category": "Report Definition"}])
11
+ project.generate_doe(n=50)
12
+ project.run_simulations()
13
+ result = project.train(model_name="my_run")
14
+ print(result.summary())
15
+
16
+ Lower-level modules (cfdtwin.doe, cfdtwin.training, cfdtwin.simulation, ...)
17
+ are public for power users who want fine-grained access.
18
+ """
19
+
20
+ from cfdtwin.project import Project
21
+ from cfdtwin.results import (
22
+ Metrics,
23
+ ModelInfo,
24
+ PredictionResult,
25
+ SimulationResult,
26
+ TrainingResult,
27
+ )
28
+
29
+ __version__ = "0.1.0"
30
+
31
+ __all__ = [
32
+ "Project",
33
+ "Metrics",
34
+ "ModelInfo",
35
+ "PredictionResult",
36
+ "SimulationResult",
37
+ "TrainingResult",
38
+ "__version__",
39
+ ]
@@ -0,0 +1,199 @@
1
+ """
2
+ Project Manager Module
3
+ ======================
4
+ Queries Fluent for available inputs/outputs. No UI -- returns data for GUI to display.
5
+ """
6
+
7
+ import logging
8
+ import re
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ # Regex for the value/unit format Fluent stores named-expression definitions in.
14
+ # Matches "1.5 [m/s]", "300[K]", " -2.3 [degC] ", etc.
15
+ _DEFINITION_RE = re.compile(
16
+ r"^\s*([+-]?\d*\.?\d+(?:[eE][+-]?\d+)?)\s*\[([^\]]+)\]\s*$"
17
+ )
18
+
19
+
20
+ def _parse_definition(definition):
21
+ """Parse a named-expression definition string into (value, unit_str).
22
+
23
+ Returns (None, None) if it doesn't match the simple "<number> [<unit>]"
24
+ shape — i.e. the expression is a formula, not a scalar input."""
25
+ if not isinstance(definition, str):
26
+ return None, None
27
+ m = _DEFINITION_RE.match(definition)
28
+ if not m:
29
+ return None, None
30
+ try:
31
+ return float(m.group(1)), m.group(2)
32
+ except ValueError:
33
+ return None, None
34
+
35
+
36
+ def get_available_inputs(solver):
37
+ """
38
+ Query Fluent for available boundary conditions and input parameters
39
+ (Fluent named expressions flagged as input_parameter).
40
+
41
+ Cell zones are deliberately excluded — they don't expose anything cfdtwin
42
+ can vary as a DOE input.
43
+
44
+ Returns
45
+ -------
46
+ list of dict
47
+ Each dict has keys: name, type, category. Input-parameter entries
48
+ additionally carry: unit (str, e.g. "m/s"), current_value (float),
49
+ definition (the raw "<value> [<unit>]" string).
50
+ """
51
+ items = []
52
+
53
+ # Boundary conditions
54
+ try:
55
+ boundary_conditions = solver.settings.setup.boundary_conditions
56
+ for bc_type in dir(boundary_conditions):
57
+ if bc_type.startswith('_') or bc_type in ['child_names', 'command_names']:
58
+ continue
59
+ bc_obj = getattr(boundary_conditions, bc_type)
60
+ if hasattr(bc_obj, '__iter__') and not isinstance(bc_obj, str):
61
+ try:
62
+ for name in bc_obj:
63
+ if name not in ['child_names', 'command_names']:
64
+ items.append({
65
+ 'name': name,
66
+ 'type': bc_type.replace('_', ' ').title(),
67
+ 'category': 'Boundary Condition'
68
+ })
69
+ except Exception:
70
+ pass
71
+ except Exception as e:
72
+ logger.warning(f"Error loading boundary conditions: {e}")
73
+
74
+ # Input parameters (Fluent named expressions with input_parameter=True).
75
+ # These have a "<value> [<unit>]" definition we can parse and re-write at
76
+ # DOE time, which is cleaner than navigating the BC settings tree.
77
+ try:
78
+ named_exprs = solver.settings.setup.named_expressions
79
+ for expr_name in named_exprs:
80
+ if expr_name in ['child_names', 'command_names']:
81
+ continue
82
+ try:
83
+ state = named_exprs[expr_name].get_state()
84
+ except Exception:
85
+ continue
86
+ if not state.get('input_parameter'):
87
+ continue
88
+ value, unit = _parse_definition(state.get('definition', ''))
89
+ if value is None:
90
+ # Skip formula-style inputs we can't safely re-write as scalars.
91
+ logger.info(
92
+ f"Skipping named expression '{expr_name}': "
93
+ f"definition {state.get('definition')!r} is not a scalar value."
94
+ )
95
+ continue
96
+ items.append({
97
+ 'name': expr_name,
98
+ 'type': 'Input Parameter',
99
+ 'category': 'Input Parameter',
100
+ 'unit': unit,
101
+ 'current_value': value,
102
+ 'definition': state.get('definition', ''),
103
+ })
104
+ except Exception as e:
105
+ logger.warning(f"Error loading input parameters: {e}")
106
+
107
+ return items
108
+
109
+
110
+ def get_available_outputs(solver):
111
+ """
112
+ Query Fluent for available surfaces, cell zones, and report definitions that can be outputs.
113
+
114
+ Returns
115
+ -------
116
+ list of dict
117
+ Each dict has keys: name, type, category
118
+ """
119
+ items = []
120
+
121
+ # Surfaces (from boundary conditions)
122
+ try:
123
+ boundary_conditions = solver.settings.setup.boundary_conditions
124
+ for bc_type in dir(boundary_conditions):
125
+ if bc_type.startswith('_') or bc_type in ['child_names', 'command_names']:
126
+ continue
127
+ bc_obj = getattr(boundary_conditions, bc_type)
128
+ if hasattr(bc_obj, '__iter__') and not isinstance(bc_obj, str):
129
+ try:
130
+ for name in bc_obj:
131
+ if name not in ['child_names', 'command_names']:
132
+ items.append({
133
+ 'name': name,
134
+ 'type': bc_type.replace('_', ' ').title(),
135
+ 'category': 'Surface'
136
+ })
137
+ except Exception:
138
+ pass
139
+
140
+ # Created surfaces (planes, iso-surfaces, etc.)
141
+ try:
142
+ if hasattr(solver, 'fields') and hasattr(solver.fields, 'field_data'):
143
+ all_surface_names = solver.fields.field_data.surfaces.allowed_values()
144
+ for surf_name in all_surface_names:
145
+ if not any(s['name'] == surf_name for s in items):
146
+ items.append({
147
+ 'name': surf_name,
148
+ 'type': 'Created Surface',
149
+ 'category': 'Surface'
150
+ })
151
+ except Exception:
152
+ pass
153
+ except Exception as e:
154
+ logger.warning(f"Error loading surfaces: {e}")
155
+
156
+ # Cell zones
157
+ try:
158
+ cell_zones_obj = solver.settings.setup.cell_zone_conditions
159
+ for zone_type in dir(cell_zones_obj):
160
+ if zone_type.startswith('_') or zone_type in ['child_names', 'command_names']:
161
+ continue
162
+ zone_obj = getattr(cell_zones_obj, zone_type)
163
+ if hasattr(zone_obj, '__iter__') and not isinstance(zone_obj, str):
164
+ try:
165
+ for name in zone_obj:
166
+ if name not in ['child_names', 'command_names']:
167
+ items.append({
168
+ 'name': name,
169
+ 'type': zone_type.replace('_', ' ').title(),
170
+ 'category': 'Cell Zone'
171
+ })
172
+ except Exception:
173
+ pass
174
+ except Exception as e:
175
+ logger.warning(f"Error loading cell zones: {e}")
176
+
177
+ # Report definitions
178
+ try:
179
+ report_defs_obj = solver.settings.solution.report_definitions
180
+ report_types = ['surface', 'volume', 'flux', 'force', 'lift', 'drag',
181
+ 'moment', 'expression', 'user_defined']
182
+ for report_type in report_types:
183
+ if hasattr(report_defs_obj, report_type):
184
+ report_obj = getattr(report_defs_obj, report_type)
185
+ if hasattr(report_obj, '__iter__') and not isinstance(report_obj, str):
186
+ try:
187
+ for name in report_obj:
188
+ if name not in ['child_names', 'command_names']:
189
+ items.append({
190
+ 'name': name,
191
+ 'type': report_type.replace('_', ' ').title(),
192
+ 'category': 'Report Definition'
193
+ })
194
+ except Exception:
195
+ pass
196
+ except Exception as e:
197
+ logger.warning(f"Error loading report definitions: {e}")
198
+
199
+ return items