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.
- cfdtwin-0.1.0/.github/workflows/ci.yml +44 -0
- cfdtwin-0.1.0/.github/workflows/docs.yml +30 -0
- cfdtwin-0.1.0/.github/workflows/publish.yml +28 -0
- cfdtwin-0.1.0/.gitignore +63 -0
- cfdtwin-0.1.0/LICENSE +21 -0
- cfdtwin-0.1.0/PKG-INFO +117 -0
- cfdtwin-0.1.0/README.md +88 -0
- cfdtwin-0.1.0/cfdtwin/__init__.py +39 -0
- cfdtwin-0.1.0/cfdtwin/_project_manager.py +199 -0
- cfdtwin-0.1.0/cfdtwin/_project_system.py +340 -0
- cfdtwin-0.1.0/cfdtwin/doe.py +390 -0
- cfdtwin-0.1.0/cfdtwin/fluent.py +139 -0
- cfdtwin-0.1.0/cfdtwin/metrics.py +65 -0
- cfdtwin-0.1.0/cfdtwin/nn.py +317 -0
- cfdtwin-0.1.0/cfdtwin/output_parameters.py +42 -0
- cfdtwin-0.1.0/cfdtwin/pod.py +138 -0
- cfdtwin-0.1.0/cfdtwin/project.py +700 -0
- cfdtwin-0.1.0/cfdtwin/results.py +163 -0
- cfdtwin-0.1.0/cfdtwin/simulation.py +891 -0
- cfdtwin-0.1.0/cfdtwin/training.py +565 -0
- cfdtwin-0.1.0/cfdtwin/visualization.py +813 -0
- cfdtwin-0.1.0/docs/api/project.md +5 -0
- cfdtwin-0.1.0/docs/api/results.md +23 -0
- cfdtwin-0.1.0/docs/assets/logo_icon.png +0 -0
- cfdtwin-0.1.0/docs/assets/logo_wordmark.png +0 -0
- cfdtwin-0.1.0/docs/examples/README.md +30 -0
- cfdtwin-0.1.0/docs/examples/discovering_bcs.py +123 -0
- cfdtwin-0.1.0/docs/examples/full_workflow.py +90 -0
- cfdtwin-0.1.0/docs/examples/quickstart.py +48 -0
- cfdtwin-0.1.0/docs/examples/training_tuning.py +61 -0
- cfdtwin-0.1.0/docs/index.md +55 -0
- cfdtwin-0.1.0/docs/quickstart.md +62 -0
- cfdtwin-0.1.0/docs/stylesheets/extra.css +18 -0
- cfdtwin-0.1.0/docs/tutorials/fluent_session.md +132 -0
- cfdtwin-0.1.0/docs/tutorials/full_workflow.md +154 -0
- cfdtwin-0.1.0/docs/tutorials/training_tuning.md +132 -0
- cfdtwin-0.1.0/gui/__init__.py +0 -0
- cfdtwin-0.1.0/gui/__main__.py +3 -0
- cfdtwin-0.1.0/gui/app.py +144 -0
- cfdtwin-0.1.0/gui/assets/logo_icon.png +0 -0
- cfdtwin-0.1.0/gui/assets/logo_wordmark.png +0 -0
- cfdtwin-0.1.0/gui/dataset_manager.py +73 -0
- cfdtwin-0.1.0/gui/error_helpers.py +66 -0
- cfdtwin-0.1.0/gui/fluent_cache.py +93 -0
- cfdtwin-0.1.0/gui/fluent_manager.py +134 -0
- cfdtwin-0.1.0/gui/main_window.py +337 -0
- cfdtwin-0.1.0/gui/pages/__init__.py +0 -0
- cfdtwin-0.1.0/gui/pages/doe_page.py +365 -0
- cfdtwin-0.1.0/gui/pages/setup_page.py +936 -0
- cfdtwin-0.1.0/gui/pages/simulate_page.py +323 -0
- cfdtwin-0.1.0/gui/pages/train_page.py +519 -0
- cfdtwin-0.1.0/gui/pages/validate_page.py +1418 -0
- cfdtwin-0.1.0/gui/project_dialog.py +368 -0
- cfdtwin-0.1.0/gui/settings_dialog.py +317 -0
- cfdtwin-0.1.0/gui/spinner.py +109 -0
- cfdtwin-0.1.0/gui/theme.py +420 -0
- cfdtwin-0.1.0/gui/user_settings.py +92 -0
- cfdtwin-0.1.0/gui/workers.py +242 -0
- cfdtwin-0.1.0/mkdocs.yml +66 -0
- 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
|
cfdtwin-0.1.0/.gitignore
ADDED
|
@@ -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).
|
cfdtwin-0.1.0/README.md
ADDED
|
@@ -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
|