diff --git a/.flake8 b/.flake8
index 7abeeeb56..e862f09ef 100644
--- a/.flake8
+++ b/.flake8
@@ -3,5 +3,4 @@ max-line-length = 99
ignore = W503, F401, W605, E203
-exclude = test/*, doc/*, build/*, dist/* setup.py, ceasiompy/Stability*
-# Should be removed: ceasiompy/Stability* (too many things to refactor in those modules)
\ No newline at end of file
+exclude = test/*, doc/*, build/*, dist/* setup.py, ceasiompy/Stability*
\ No newline at end of file
diff --git a/.github/workflows/docker_centos.yml b/.github/workflows/docker_centos.yml
index 1e01fe0f6..0f324fc16 100644
--- a/.github/workflows/docker_centos.yml
+++ b/.github/workflows/docker_centos.yml
@@ -15,10 +15,10 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- - name: Set up Python 3.8
+ - name: Set up Python 3.11.11
uses: actions/setup-python@v2
with:
- python-version: 3.8
+ python-version: 3.11.11
- name: Run Dockerfile installation and tests
run: |
diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml
index f25aea379..68eba19fb 100644
--- a/.github/workflows/flake8.yml
+++ b/.github/workflows/flake8.yml
@@ -16,13 +16,13 @@ jobs:
steps:
- uses: actions/checkout@v2
- - name: Set up Python 3.8
+ - name: Set up Python 3.11.11
uses: actions/setup-python@v2
with:
- python-version: 3.8
+ python-version: 3.11.11
- name: Install flake8
run: pip install flake8
- name: Run flake8
- run: flake8
+ run: flake8
\ No newline at end of file
diff --git a/.github/workflows/integrationtests.yml b/.github/workflows/integrationtests.yml
index f593eae57..741c2bd75 100644
--- a/.github/workflows/integrationtests.yml
+++ b/.github/workflows/integrationtests.yml
@@ -16,24 +16,25 @@ env:
jobs:
build-linux:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
strategy:
max-parallel: 5
steps:
- uses: actions/checkout@v3
- - name: Set up Python 3.8
+ - name: Set up Python 3.11.11
uses: actions/setup-python@v2
with:
- python-version: 3.8
+ python-version: 3.11.11
- - name: Setup Mambaforge
+ - name: Setup Miniforge3
uses: conda-incubator/setup-miniconda@v2
with:
miniforge-variant: Miniforge3
miniforge-version: latest
activate-environment: ceasiompy
- use-mamba: true
+ use-mamba: false
+ conda-version: 25.3.1
- name: Set cache date # With that, cache will be updated every day
run: echo "DATE=$(date +'%Y%m%d')" >> $GITHUB_ENV
@@ -45,20 +46,15 @@ jobs:
id: cache
- name: Update environment
- run: mamba env update -n ceasiompy -f environment.yml
+ run: conda env update -n ceasiompy -f environment.yml
if: steps.cache.outputs.cache-hit != 'true'
- name: Install ceasiompy with pip
shell: bash -l {0}
run: pip install -e .
- - name: Install PyTornado
- shell: bash -l {0}
- run: installation/Ubuntu/install_pytornado.sh
-
- - name: Install SUMO
- shell: bash -l {0}
- run: installation/Ubuntu/install_sumo.sh
+ - name: Install libGLU
+ run: sudo apt-get install -y libglu1-mesa-dev
- name: Install AVL
shell: bash -l {0}
@@ -92,4 +88,4 @@ jobs:
# - name: Setup tmate session to SSH to the remote machine (for debugging)
# if: ${{ failure() }}
# uses: mxschmitt/action-tmate@v3
- # timeout-minutes: 15
+ # timeout-minutes: 15
\ No newline at end of file
diff --git a/.github/workflows/unittests.yml b/.github/workflows/unittests.yml
index d6a2e9792..e25bc0605 100644
--- a/.github/workflows/unittests.yml
+++ b/.github/workflows/unittests.yml
@@ -22,18 +22,19 @@ jobs:
steps:
- uses: actions/checkout@v2
- - name: Set up Python 3.8
+ - name: Set up Python 3.11.11
uses: actions/setup-python@v2
with:
- python-version: 3.8
+ python-version: 3.11.11
- - name: Setup Mambaforge
+ - name: Setup Miniforge3
uses: conda-incubator/setup-miniconda@v2
with:
miniforge-variant: Miniforge3
miniforge-version: latest
activate-environment: ceasiompy
- use-mamba: true
+ use-mamba: false
+ conda-version: 25.3.1
- name: Set cache date # With that, cache will be updated every day
run: echo "DATE=$(date +'%Y%m%d')" >> $GITHUB_ENV
@@ -45,13 +46,16 @@ jobs:
id: cache
- name: Update environment
- run: mamba env update -n ceasiompy -f environment.yml
+ run: conda env update -n ceasiompy -f environment.yml
if: steps.cache.outputs.cache-hit != 'true'
- name: Install ceasiompy with pip
shell: bash -l {0}
run: pip install -e .
-
+
+ - name: Install libGLU
+ run: sudo apt-get install -y libglu1-mesa-dev
+
- name: Run unit tests
shell: bash -l {0}
run: pytest -v ./ceasiompy --cov=ceasiompy/ --cov-report xml:coverage.xml
@@ -63,4 +67,4 @@ jobs:
files: ./coverage.xml
flags: unittests
fail_ci_if_error: true
- verbose: true
+ verbose: true
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 979afbb6a..1e1eb5c71 100644
--- a/.gitignore
+++ b/.gitignore
@@ -181,3 +181,5 @@ aircraft_loads.csv
!/ceasiompy/ModuleTemplate/ToolInput/ToolInput.xml
!/ceasiompy/utils/tests/tests_workflowfunctions/ToolInput/ToolInput.xml
!/test_files/AeroMaps/*.csv
+
+/ceasiompy/Database/databases/ceasiompy.db
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 829e163dd..08005e4f4 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -166,11 +166,6 @@ If you are new to Python we recommend some material that could help you (you can
Other resources that could the useful.
-#### PyTornado
-
-- [PyTornado website](https://pytornado.readthedocs.io/en/latest/)
-- [Github repository](https://github.com/airinnova/pytornado)
-
#### GMSH
- [GMSH website](https://gmsh.info/)
diff --git a/README.md b/README.md
index 3343a99d0..c4971fab2 100644
--- a/README.md
+++ b/README.md
@@ -66,14 +66,14 @@ These test cases are there to learn how to use CEASIOMpy. You will probably also
```bash
cd WKDIR
- ceasiompy_run -m ../test_files/CPACSfiles/D150_simple.xml PyTornado SaveAeroCoefficients
+ ceasiompy_run -m ../test_files/CPACSfiles/D150_simple.xml PyAVL SaveAeroCoefficients
```
Follow the [test cases](#test-cases) to discover the different way of using CEASIOMpy.
### Examples of workflows
-- **Simple workflow with PyTornado (Vortex Lattice Method)**
+- **Simple workflow with PyAVL (Vortex Lattice Method)**
@@ -90,21 +90,21 @@ Follow the [test cases](#test-cases) to discover the different way of using CEAS
```mermaid
graph LR;
- CLCalculator-->CPACS2SUMO;
- CPACS2SUMO-->SUMOAutoMesh;
- SUMOAutoMesh-->SU2Run;
+ CPACS2Updater-->CPACS2Gmsh;
+ CPACS2Gmsh-->SU2Run;
SU2Run-->ExportCSV;
+ ExportCSV-->Database;
```
### Available modules
-There are many different modules available in CEASIOMpy that can be combined in different workflows. The list of available modules is shown below. The module status is marked as follows:
+There are many different modules available in CEASIOMpy that can be combined to create different workflows. The list of available modules is shown below. The modules' statuses are marked as follows:
-:heavy_check_mark: : The module should work as expected. There may be some minor bugs, don't hesitate to report them (more details [here](CONTRIBUTING.md#reporting-bugs)).
+:heavy_check_mark: : The module should work as expected. If you find bugs, do not hesitate to report them (more details [here](CONTRIBUTING.md#reporting-bugs)).
-:warning: : The module does not work completely as expected. It is not a bug, but some features or data handling are not yet compatible with the new file structure. Check the [Kanban Board](https://github.com/cfsengineering/CEASIOMpy/projects/1) to see planned and in-progress features.
+:warning: : The module does not work as expected. It is not a bug, but some features or data handling are not yet compatible with the new file structure. Check the [Kanban Board](https://github.com/cfsengineering/CEASIOMpy/projects/1) to see planned and in-progress features.
:x: : The module does not work at all. Some functions have been written, but need a lot of changes to be compatible with the rest of CEASIOMpy.
@@ -123,6 +123,7 @@ There are many different modules available in CEASIOMpy that can be combined in
#### Geometry and Mesh
- [CPACSCreator](ceasiompy/CPACSCreator/README.md) :heavy_check_mark:
+- [CPACSUpdater](ceasiompy/CPACSUpdater/README.md) :heavy_check_mark:
- [CPACS2GMSH](ceasiompy/CPACS2GMSH/README.md) :heavy_check_mark:
- [CPACS2SUMO](ceasiompy/CPACS2SUMO/README.md) :heavy_check_mark:
- [SUMOAutoMesh](ceasiompy/SUMOAutoMesh/README.md) :warning:
@@ -133,7 +134,6 @@ There are many different modules available in CEASIOMpy that can be combined in
#### Aerodynamics
- [CLCalculator](ceasiompy/CLCalculator/README.md) :heavy_check_mark:
-- [PyTornado](ceasiompy/PyTornado/README.md) :heavy_check_mark:
- [PyAVL](ceasiompy/PyAVL/README.md) :heavy_check_mark:
- [SU2Run](ceasiompy/SU2Run/README.md) :heavy_check_mark:
- [SkinFriction](ceasiompy/SkinFriction/README.md) :heavy_check_mark:
@@ -154,12 +154,17 @@ There are many different modules available in CEASIOMpy that can be combined in
- Range :warning:
- [StaticStability](./ceasiompy/StaticStability/README.md) :heavy_check_mark:
+- [DynamicStability](./ceasiompy/DynamicStability/README.md) :heavy_check_mark:
#### Structure
-- AeroFrame :heavy_check_mark:
+- AeroFrame :x:
+
+#### Data Analysis and Storage
+
+- [Database](./ceasiompy/Database/README.md) :heavy_check_mark:
## Contributing
diff --git a/ceasiompy/.flake8 b/ceasiompy/.flake8
new file mode 100644
index 000000000..7abeeeb56
--- /dev/null
+++ b/ceasiompy/.flake8
@@ -0,0 +1,7 @@
+[flake8]
+max-line-length = 99
+
+ignore = W503, F401, W605, E203
+
+exclude = test/*, doc/*, build/*, dist/* setup.py, ceasiompy/Stability*
+# Should be removed: ceasiompy/Stability* (too many things to refactor in those modules)
\ No newline at end of file
diff --git a/ceasiompy/.gitignore b/ceasiompy/.gitignore
new file mode 100644
index 000000000..979afbb6a
--- /dev/null
+++ b/ceasiompy/.gitignore
@@ -0,0 +1,183 @@
+# Mac files
+*.DS_Store
+
+# Vim temporary files
+*.swp
+
+# Sphinx
+build/
+doc/source/dev_doc/ceasiompy.*
+doc/source/user_guide/module_interfaces/*
+
+# Dolphin file manager
+.directory
+
+
+# See https://www.gitignore.io/
+# =============================
+
+### Python ###
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+*.pyc
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+pip-wheel-metadata/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+.python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# celery beat schedule file
+celerybeat-schedule
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+.vscode
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+
+## Specific to CEASIOMpy
+
+# Install directory
+install_dir/
+
+# Ignore logfiles, temp file and errors
+*.log
+*.log.*
+/temp
+/tmp
+CrashInfo/
+failure.xml
+
+# CEASIOMpy settings and history
+.ceasiompy
+
+# Ignore all SU2 meshes (.su2)
+*.su2
+forces_breakdown.dat
+
+# Ignore temp folder and and temp files
+/INSTALLDIR
+/Results
+/WKDIR/*
+!/WKDIR/.keep
+
+# Ignore ToolInput and ToolOutput
+ToolInput.xml
+ToolInput*.xml
+/ToolOutput
+ToolOutput.xml
+
+aircraft_loads.csv
+*.out
+*MTOM_Prediction.png
+*Payload_vs_Range.png
+*_Cog.png
+**/ToolOutput/*.svg
+
+# Exceptions
+!/ceasiompy/ModuleTemplate/ToolInput/ToolInput.xml
+!/ceasiompy/utils/tests/tests_workflowfunctions/ToolInput/ToolInput.xml
+!/test_files/AeroMaps/*.csv
diff --git a/ceasiompy/AeroFrame/.gitignore b/ceasiompy/AeroFrame/.gitignore
deleted file mode 100644
index 3225cbe4e..000000000
--- a/ceasiompy/AeroFrame/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-log.txt
-ToolInput.cfg
diff --git a/ceasiompy/AeroFrame/ToolInput/.keep b/ceasiompy/AeroFrame/ToolInput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/AeroFrame/ToolInput/.keep
+++ b/ceasiompy/AeroFrame/ToolInput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/AeroFrame/ToolOutput/.keep b/ceasiompy/AeroFrame/ToolOutput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/AeroFrame/ToolOutput/.keep
+++ b/ceasiompy/AeroFrame/ToolOutput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/AeroFrame/__init__.py b/ceasiompy/AeroFrame/__init__.py
index e69de29bb..46d85e618 100644
--- a/ceasiompy/AeroFrame/__init__.py
+++ b/ceasiompy/AeroFrame/__init__.py
@@ -0,0 +1,46 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for AeroFrame module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+from ceasiompy import log
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+# True if the module is active.
+# False if the module is disabled (not working or not ready).
+module_status = False
+
+# ===== Include GUI =====
+# True if you want to add a GUI for this module.
+# False if module is desactivated or no GUI to be displayed.
+include_gui = False
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/AeroFrame/__specs__.py b/ceasiompy/AeroFrame/__specs__.py
index ea9e62ff6..69170937c 100644
--- a/ceasiompy/AeroFrame/__specs__.py
+++ b/ceasiompy/AeroFrame/__specs__.py
@@ -1,5 +1,6 @@
from ceasiompy.utils.moduleinterfaces import CPACSInOut
+from ceasiompy import log
# ===== Module Status =====
# True if the module is active
@@ -8,3 +9,11 @@
# ===== CPACS inputs and outputs =====
cpacs_inout = CPACSInOut()
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/AeroFrame/runaeroframe.py b/ceasiompy/AeroFrame/aeroframe.py
similarity index 79%
rename from ceasiompy/AeroFrame/runaeroframe.py
rename to ceasiompy/AeroFrame/aeroframe.py
index 39d651795..b70764de1 100644
--- a/ceasiompy/AeroFrame/runaeroframe.py
+++ b/ceasiompy/AeroFrame/aeroframe.py
@@ -16,14 +16,13 @@
* https://github.com/airinnova/aeroframe/issues
-Python version: >=3.8
| Author: Aaron Dettmann
| Creation: 2019-09-25
TODO:
- * Refactoring
+ * make working directories
"""
@@ -36,9 +35,11 @@
from pathlib import Path
import aeroframe.stdfun.run as af
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
-log = get_logger()
+# =================================================================================================
+# CONSTANTS
+# =================================================================================================
MODULE_DIR = Path(__file__).parent
MODULE_NAME = MODULE_DIR.name
@@ -55,8 +56,10 @@
# =================================================================================================
if __name__ == "__main__":
- log.info("----- Start of " + MODULE_NAME + " -----")
+
+ module_name = MODULE_NAME
+ log.info("----- Start of " + module_name + " -----")
af.standard_run(args=af.StdRunArgs(dest=DIR_AEROFRAME_WKDIR, verbose=True))
- log.info("----- End of " + MODULE_NAME + " -----")
+ log.info("----- End of " + module_name + " -----")
diff --git a/ceasiompy/AeroFrame/wrappers/cfd/su2.py b/ceasiompy/AeroFrame/wrappers/cfd/su2.py
index 1c876e1f1..487db0942 100644
--- a/ceasiompy/AeroFrame/wrappers/cfd/su2.py
+++ b/ceasiompy/AeroFrame/wrappers/cfd/su2.py
@@ -97,7 +97,7 @@ def _write_su2_disp_file(self):
def run_analysis(self, turn_off_deform=False):
"""
- Run the PyTornado analysis
+ Run the PyAVL analysis
Args:
:turn_off_deform: Flag which can be used to turn off all deformations
diff --git a/ceasiompy/AeroFrame_new/ToolInput/.keep b/ceasiompy/AeroFrame_new/ToolInput/.keep
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/ceasiompy/AeroFrame_new/ToolInput/.keep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ceasiompy/AeroFrame_new/ToolOutput/.keep b/ceasiompy/AeroFrame_new/ToolOutput/.keep
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/ceasiompy/AeroFrame_new/ToolOutput/.keep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ceasiompy/AeroFrame_new/__init__.py b/ceasiompy/AeroFrame_new/__init__.py
index e69de29bb..92b46e607 100644
--- a/ceasiompy/AeroFrame_new/__init__.py
+++ b/ceasiompy/AeroFrame_new/__init__.py
@@ -0,0 +1,45 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for AeroFrame new module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+from ceasiompy import log
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = False
+
+# ===== Include GUI =====
+include_gui = False
+
+# ===== Add a Results Directory =====
+RES_DIR = True
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/AeroFrame_new/__specs__.py b/ceasiompy/AeroFrame_new/__specs__.py
index 87eb9496a..13088d639 100644
--- a/ceasiompy/AeroFrame_new/__specs__.py
+++ b/ceasiompy/AeroFrame_new/__specs__.py
@@ -1,9 +1,13 @@
from ceasiompy.utils.moduleinterfaces import CPACSInOut
-from ceasiompy.utils.commonxpath import (
- CEASIOMPY_XPATH,
+
+from ceasiompy import log
+from ceasiompy.PyAVL import (
AVL_PLOT_XPATH,
AVL_VORTEX_DISTR_XPATH,
AVL_AEROMAP_UID_XPATH,
+)
+from ceasiompy.utils.commonxpath import (
+ CEASIOMPY_XPATH,
AEROPERFORMANCE_XPATH,
FRAMAT_MATERIAL_XPATH,
FRAMAT_SECTION_XPATH,
@@ -12,6 +16,7 @@
)
from pathlib import Path
+import streamlit as st
# ===== Module Status =====
# True if the module is active
# False if the module is disabled (not working or not ready)
@@ -28,7 +33,7 @@
cpacs_inout.add_input(
var_name="aeromap_uid",
var_type=list,
- default_value=None,
+ default_value=st.session_state.cpacs.get_aeromap_uid_list(),
unit=None,
descr="Name of the aero map to calculate",
xpath=AVL_AEROMAP_UID_XPATH,
@@ -213,3 +218,10 @@
descr="aeroMap with aero coefficients calculated by AVL",
xpath=AEROPERFORMANCE_XPATH + "/aeroMap/aeroPerformanceMap",
)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/AeroFrame_new/aeroframe_run.py b/ceasiompy/AeroFrame_new/aeroframe_new.py
similarity index 90%
rename from ceasiompy/AeroFrame_new/aeroframe_run.py
rename to ceasiompy/AeroFrame_new/aeroframe_new.py
index 56c34093d..791630e1a 100644
--- a/ceasiompy/AeroFrame_new/aeroframe_run.py
+++ b/ceasiompy/AeroFrame_new/aeroframe_new.py
@@ -6,7 +6,6 @@
Script to run aeroelastic computations using AVL to compute
aerodynamic loads and FramAT for structural calculations.
-Python version: >=3.8
| Author: Romain Gauthier
| Creation: 2024-06-17
@@ -20,17 +19,17 @@
# ==============================================================================
# IMPORTS
# ==============================================================================
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.moduleinterfaces import get_toolinput_file_path, get_tooloutput_file_path
-from ceasiompy.utils.ceasiompyutils import get_results_directory
-from ceasiompy.PyAVL.avlrun import run_avl
-from ceasiompy.PyAVL.func.avlconfig import get_aeromap_conditions
+from ceasiompy import log
+
+from ceasiompy.utils.ceasiompyutils import call_main
+
+from ceasiompy.PyAVL.pyavl import main as run_avl
+from ceasiompy.utils.ceasiompyutils import get_aeromap_conditions
from cpacspy.cpacsfunctions import (
get_value_or_default,
create_branch,
- open_tixi
)
-from ceasiompy.PyAVL.func.avlresults import convert_ps_to_pdf
+from ceasiompy.PyAVL.func.plot import convert_ps_to_pdf
from ceasiompy.AeroFrame_new.func.aeroframe_config import (
read_AVL_fe_file,
create_framat_model,
@@ -42,19 +41,22 @@
write_deformed_command
)
-from ceasiompy.AeroFrame_new.func.aeroframe_results import (
+from ceasiompy.AeroFrame_new.func.results import (
compute_deformations,
plot_translations_rotations,
plot_convergence
)
-from ceasiompy.AeroFrame_new.func.aeroframe_debbug import (
+from ceasiompy.AeroFrame_new.func.plot import (
plot_fem_mesh,
plot_deformed_wing
)
-from ceasiompy.utils.commonxpath import (
+from ceasiompy.PyAVL import (
AVL_PLOT_XPATH,
+ AVL_AEROMAP_UID_XPATH,
+)
+from ceasiompy.utils.commonxpath import (
FRAMAT_RESULTS_XPATH,
FRAMAT_MESH_XPATH,
AEROFRAME_SETTINGS
@@ -69,9 +71,6 @@
import pandas as pd
import shutil
-
-log = get_logger()
-
MODULE_DIR = Path(__file__).parent
MODULE_NAME = MODULE_DIR.name
@@ -203,9 +202,9 @@ def aeroelastic_loop(cpacs_path, CASE_PATH, q, xyz, fxyz):
log.info("")
log.info(f"----- FramAT: Deformation {n_iter} -----")
- Path(CASE_PATH, f"Iteration_{n_iter+1}", "AVL").mkdir(parents=True, exist_ok=True)
+ Path(CASE_PATH, f"Iteration_{n_iter + 1}", "AVL").mkdir(parents=True, exist_ok=True)
Path(CASE_PATH, f"Iteration_{n_iter}", "FramAT").mkdir(parents=True, exist_ok=True)
- AVL_ITER_PATH = Path(CASE_PATH, f"Iteration_{n_iter+1}", "AVL")
+ AVL_ITER_PATH = Path(CASE_PATH, f"Iteration_{n_iter + 1}", "AVL")
FRAMAT_ITER_PATH = Path(CASE_PATH, f"Iteration_{n_iter}", "FramAT")
AVL_DEFORMED_PATH = Path(AVL_ITER_PATH, "deformed.avl")
AVL_DEFORMED_COMMAND = Path(AVL_ITER_PATH, "avl_commands.txt")
@@ -282,7 +281,7 @@ def aeroelastic_loop(cpacs_path, CASE_PATH, q, xyz, fxyz):
write_deformed_geometry(AVL_UNDEFORMED_PATH, AVL_DEFORMED_PATH, centerline_df, deformed_df)
write_deformed_command(AVL_UNDEFORMED_COMMAND, AVL_DEFORMED_COMMAND)
log.info("")
- log.info(f"----- AVL: Calculation {n_iter+1} -----")
+ log.info(f"----- AVL: Calculation {n_iter + 1} -----")
log.info("Running AVL ...")
subprocess.run(["xvfb-run", "avl"],
stdin=open(str(AVL_DEFORMED_COMMAND), "r"),
@@ -343,8 +342,10 @@ def compute_structural_work(row):
percentage = deflection / semi_span
log.info(f"Final tip deflection residual : {res[-1]:.3e}")
- log.info("Wing tip deflection : "
- f"{deflection:.3e} m ({percentage:.2%} of the semi-span length).")
+ log.info(
+ "Wing tip deflection : "
+ f"{deflection:.3e} m ({percentage:.2%} of the semi-span length)."
+ )
log.info(f"Wing tip twist : {tip_twist:.3e} degrees.")
log.info(f"Total aerodynamic work : {total_aero_work:.3e} J.")
log.info(f"Total structural work : {total_structural_work:.3e} J.")
@@ -356,7 +357,7 @@ def compute_structural_work(row):
return delta_tip, res
-def aeroframe_run(cpacs_path, cpacs_out_path, wkdir):
+def main(cpacs: CPACS, wkdir: Path) -> None:
"""Function to run aeroelastic calculations.
Function 'aeroframe_run' runs aeroelastic calculations
@@ -368,8 +369,12 @@ def aeroframe_run(cpacs_path, cpacs_out_path, wkdir):
cpacs_out_path (Path): path to the CPACS output file.
wkdir (Path): path to the working directory.
"""
- tixi = open_tixi(cpacs_path)
- alt_list, mach_list, aoa_list, aos_list = get_aeromap_conditions(cpacs_path)
+ cpacs_path = cpacs.cpacs_file
+ tixi = cpacs.tixi
+ alt_list, mach_list, aoa_list, aos_list = get_aeromap_conditions(
+ cpacs_path,
+ AVL_AEROMAP_UID_XPATH
+ )
log.info("FLIGHT CONDITIONS:")
log.info(f"\tAltitude : {', '.join(str(a) for a in alt_list)} meters")
log.info(f"\tMach number : {', '.join(str(m) for m in mach_list)}")
@@ -378,9 +383,11 @@ def aeroframe_run(cpacs_path, cpacs_out_path, wkdir):
# First AVL run
log.info("----- AVL: Calculation 1 -----")
- run_avl(cpacs_path, wkdir)
- for i_case in range(len(alt_list)):
+ # First AVL run
+ run_avl(cpacs, wkdir)
+
+ for i_case, _ in enumerate(alt_list):
alt = alt_list[i_case]
mach = mach_list[i_case]
aoa = aoa_list[i_case]
@@ -419,7 +426,6 @@ def aeroframe_run(cpacs_path, cpacs_out_path, wkdir):
# Write results in CPACS out
create_branch(tixi, FRAMAT_RESULTS_XPATH + "/TipDeflection")
tixi.updateDoubleElement(FRAMAT_RESULTS_XPATH + "/TipDeflection", tip_deflection[-1], "%g")
- tixi.save(str(cpacs_out_path))
plot_convergence(tip_deflection, residuals, wkdir=CASE_PATH)
@@ -428,18 +434,5 @@ def aeroframe_run(cpacs_path, cpacs_out_path, wkdir):
# MAIN
# =================================================================================================
-
-def main(cpacs_path, cpacs_out_path):
- log.info("----- Start of " + MODULE_NAME + " -----")
-
- results_dir = get_results_directory("AeroFrame_new")
- aeroframe_run(cpacs_path, cpacs_out_path, wkdir=results_dir)
-
- log.info("----- End of " + MODULE_NAME + " -----")
-
-
if __name__ == "__main__":
- cpacs_path = get_toolinput_file_path(MODULE_NAME)
- cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
-
- main(cpacs_path, cpacs_out_path)
+ call_main(main, MODULE_NAME)
diff --git a/ceasiompy/AeroFrame_new/func/aeroframe_config.py b/ceasiompy/AeroFrame_new/func/aeroframe_config.py
index 192f4f27e..5205fef18 100644
--- a/ceasiompy/AeroFrame_new/func/aeroframe_config.py
+++ b/ceasiompy/AeroFrame_new/func/aeroframe_config.py
@@ -6,7 +6,6 @@
Script to extract panel forces of a surface,
from AVL 'fe.txt' element force file
-Python version: >=3.8
| Author: Romain Gauthier
| Creation: 2024-06-17
@@ -31,7 +30,7 @@
from framat import Model
from cpacspy.cpacspy import CPACS
-from cpacspy.cpacsfunctions import get_value_or_default, get_value
+from cpacspy.cpacsfunctions import get_value_or_default
from cpacspy.cpacsfunctions import open_tixi
from ceasiompy.utils.commonxpath import (
@@ -39,17 +38,15 @@
FRAMAT_SECTION_XPATH,
WINGS_XPATH
)
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from ceasiompy.utils.generalclasses import SimpleNamespace, Transformation
-from ceasiompy.utils.mathfunctions import euler2fix
+from ceasiompy.utils.mathsfunctions import euler2fix, rotate_points
from ceasiompy.CPACS2SUMO.func.getprofile import get_profile_coord
-from ceasiompy.AeroFrame_new.func.aeroframe_utils import (
+from ceasiompy.AeroFrame_new.func.utils import (
PolyArea,
second_moments_of_area,
- rotate_3D_points
)
-log = get_logger()
# =================================================================================================
# FUNCTIONS
@@ -303,12 +300,14 @@ def create_framat_model(young_modulus, shear_modulus, material_density,
# Add orientation property to the beam
for i_node in range(len(centerline_df) - 1):
- up_x, up_y, up_z = rotate_3D_points(x=0,
- y=0,
- z=1,
- angle_x=centerline_df.iloc[i_node]["thx_new"],
- angle_y=centerline_df.iloc[i_node]["thy_new"],
- angle_z=centerline_df.iloc[i_node]["thz_new"])
+ up_x, up_y, up_z = rotate_points(
+ x=0,
+ y=0,
+ z=1,
+ RaX=centerline_df.iloc[i_node]["thx_new"],
+ RaY=-centerline_df.iloc[i_node]["thy_new"],
+ RaZ=centerline_df.iloc[i_node]["thz_new"],
+ )
beam.add('orientation', {'from': centerline_df.iloc[i_node]['node_uid'],
'to': centerline_df.iloc[i_node + 1]['node_uid'],
@@ -1069,5 +1068,4 @@ def interpolate_leading_edge_points(Xle_array, Yle_array, Zle_array, y_queries):
if __name__ == "__main__":
-
log.info("Nothing to execute!")
diff --git a/ceasiompy/AeroFrame_new/func/aeroframe_utils.py b/ceasiompy/AeroFrame_new/func/aeroframe_utils.py
deleted file mode 100644
index a28aacce9..000000000
--- a/ceasiompy/AeroFrame_new/func/aeroframe_utils.py
+++ /dev/null
@@ -1,119 +0,0 @@
-"""
-CEASIOMpy: Conceptual Aircraft Design Software
-
-Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-
-Script to ...
-
-Python version: >=3.8
-
-| Author: Romain Gauthier
-| Creation: 2024-06-17
-
-TODO:
-
- * Things to improve...
-
-"""
-
-# ==============================================================================
-# IMPORTS
-# ==============================================================================
-import numpy as np
-import math
-from shapely.geometry import Polygon
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
-
-
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
-
-def PolyArea(x, y):
- return 0.5 * np.abs(np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1)))
-
-
-def compute_centroid(x_coords, y_coords):
- polygon = Polygon(zip(x_coords, y_coords))
- centroid = polygon.centroid
- return centroid.x, centroid.y
-
-
-def second_moments_of_area(x, y):
- Ix = 0
- Iy = 0
- x_centr, y_centr = compute_centroid(x, y)
- x = [xi - x_centr for xi in x]
- y = [yi - y_centr for yi in y]
-
- n = len(x)
- for i in range(n):
- j = (i + 1) % n
- Ix += (y[i]**2 + y[i] * y[j] + y[j]**2) * (x[i] * y[j] - x[j] * y[i])
- Iy += (x[i]**2 + x[i] * x[j] + x[j]**2) * (x[i] * y[j] - x[j] * y[i])
-
- Ix /= 12
- Iy /= 12
-
- return Ix, Iy
-
-
-def rotate_3D_points(x, y, z, angle_x, angle_y, angle_z):
- """Function to apply a 3D rotation to the coordinates of a point
-
- Function 'rotate_3D_points' returns the rotated points after applying
- a 3D rotation matrix.
-
- Source:
- * https://en.wikipedia.org/wiki/Rotation_matrix
-
- Args:
- x (float): x coordinate of the initial point
- y (float): y coordinate of the initial point
- z (float): z coordinate of the initial point
- angle_x (float): rotation angle around x-axis [rad]
- angle_y (float): rotation angle around y-axis [rad]
- angle_z (float): rotation angle around z-axis [rad]
-
- Returns:
- x_rot (float): x coordinate of the rotated point
- y_rot (float): y coordinate of the rotated point
- z_rot (float): z coordinate of the rotated point
- """
- R11 = math.cos(angle_z) * math.cos(angle_y)
- R12 = math.cos(angle_z) * math.sin(angle_y) * math.sin(angle_x) - \
- math.sin(angle_z) * math.cos(angle_x)
- R13 = math.cos(angle_z) * math.sin(angle_y) * math.cos(angle_x) + \
- math.sin(angle_z) * math.sin(angle_x)
- R21 = math.sin(angle_z) * math.cos(angle_y)
- R22 = math.sin(angle_z) * math.sin(angle_y) * math.sin(angle_x) + \
- math.cos(angle_z) * math.cos(angle_x)
- R23 = math.sin(angle_z) * math.sin(angle_y) * math.cos(angle_x) - \
- math.cos(angle_z) * math.sin(angle_x)
- R31 = -math.sin(angle_y)
- R32 = math.cos(angle_y) * math.sin(angle_x)
- R33 = math.cos(angle_y) * math.cos(angle_x)
-
- rotation_matrix = np.array([[R11, R12, R13],
- [R21, R22, R23],
- [R31, R32, R33]])
-
- x_rot = x * rotation_matrix[0, 0] + y * \
- rotation_matrix[0, 1] + z * rotation_matrix[0, 2]
- y_rot = x * rotation_matrix[1, 0] + y * \
- rotation_matrix[1, 1] + z * rotation_matrix[1, 2]
- z_rot = x * rotation_matrix[2, 0] + y * \
- rotation_matrix[2, 1] + z * rotation_matrix[2, 2]
-
- return x_rot, y_rot, z_rot
-
-
-# =================================================================================================
-# MAIN
-# =================================================================================================
-
-if __name__ == "__main__":
-
- log.info("Nothing to execute!")
diff --git a/ceasiompy/AeroFrame_new/func/aeroframe_debbug.py b/ceasiompy/AeroFrame_new/func/plot.py
similarity index 81%
rename from ceasiompy/AeroFrame_new/func/aeroframe_debbug.py
rename to ceasiompy/AeroFrame_new/func/plot.py
index 3dddf2dae..56cc999c8 100644
--- a/ceasiompy/AeroFrame_new/func/aeroframe_debbug.py
+++ b/ceasiompy/AeroFrame_new/func/plot.py
@@ -7,27 +7,21 @@
and plot the shape of the deformed wing. It helps to see if the geometry was
accurately captured and if the meshes are fine.
-Python version: >=3.8
| Author: Romain Gauthier
| Creation: 2024-06-19
-TODO:
-
- * Things to improve...
-
"""
# ==============================================================================
# IMPORTS
# ==============================================================================
-import matplotlib.pyplot as plt
-from pathlib import Path
-from ceasiompy.utils.ceasiomlogger import get_logger
+import matplotlib.pyplot as plt
+from pathlib import Path
-log = get_logger()
+from ceasiompy import log
# =================================================================================================
# FUNCTIONS
@@ -35,10 +29,8 @@
def plot_fem_mesh(wing_df, centerline_df, wkdir):
- """Function to plot the VLM and FEM meshes.
-
- Function 'plot_fem_mesh' saves a plot of the
- VLM and FEM meshes in the x-y and y-z planes.
+ """
+ Saves a plot of the VLM and FEM meshes in the x-y and y-z planes.
Args:
wing_df (pandas dataframe): dataframe containing the VLM nodes.
@@ -69,15 +61,13 @@ def plot_fem_mesh(wing_df, centerline_df, wkdir):
def plot_deformed_wing(centerline_df, undeformed_df, wkdir):
- """Function to plot the deformed shape of the wing.
-
- Function 'plot_deformed_wing' saves a plot of the
- deformed and undeformed shapes of the wing.
+ """
+ Saves a plot of the deformed and undeformed shapes of the wing.
Args:
- centerline_df (pandas dataframe): dataframe containing the nodes of the deformed wing.
- undeformed_df (pandas dataframe): dataframe containing the nodes of the initial wing.
- wkdir (Path): path to the directory to save the plot.
+ centerline_df (DataFrame): Contains nodes of the deformed wing.
+ undeformed_df (DataFrame): Contains nodes of the initial wing.
+ wkdir (Path): Path to the directory to save the plot.
"""
fig, axs = plt.subplots()
axs.plot(centerline_df['y_new'],
@@ -108,5 +98,4 @@ def plot_deformed_wing(centerline_df, undeformed_df, wkdir):
# =================================================================================================
if __name__ == "__main__":
-
log.info("Nothing to execute!")
diff --git a/ceasiompy/AeroFrame_new/func/aeroframe_results.py b/ceasiompy/AeroFrame_new/func/results.py
similarity index 98%
rename from ceasiompy/AeroFrame_new/func/aeroframe_results.py
rename to ceasiompy/AeroFrame_new/func/results.py
index 1b4b5117c..4ee57fb14 100644
--- a/ceasiompy/AeroFrame_new/func/aeroframe_results.py
+++ b/ceasiompy/AeroFrame_new/func/results.py
@@ -6,7 +6,6 @@
Script to compute the wing deformation, plot the displacements and rotations,
and plot the convergence.
-Python version: >=3.8
| Author: Romain Gauthier
| Creation: 2024-06-17
@@ -27,9 +26,7 @@
from pathlib import Path
from scipy import interpolate
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
+from ceasiompy import log
# =================================================================================================
# FUNCTIONS
diff --git a/ceasiompy/AeroFrame_new/func/utils.py b/ceasiompy/AeroFrame_new/func/utils.py
new file mode 100644
index 000000000..975fa6544
--- /dev/null
+++ b/ceasiompy/AeroFrame_new/func/utils.py
@@ -0,0 +1,63 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Script to ...
+
+
+| Author: Romain Gauthier
+| Creation: 2024-06-17
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+import numpy as np
+
+from shapely.geometry import Polygon
+
+from ceasiompy import log
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def PolyArea(x, y):
+ return 0.5 * np.abs(np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1)))
+
+
+def compute_centroid(x_coords, y_coords):
+ polygon = Polygon(zip(x_coords, y_coords))
+ centroid = polygon.centroid
+ return centroid.x, centroid.y
+
+
+def second_moments_of_area(x, y):
+ Ix = 0
+ Iy = 0
+ x_centr, y_centr = compute_centroid(x, y)
+ x = [xi - x_centr for xi in x]
+ y = [yi - y_centr for yi in y]
+
+ n = len(x)
+ for i in range(n):
+ j = (i + 1) % n
+ Ix += (y[i]**2 + y[i] * y[j] + y[j]**2) * (x[i] * y[j] - x[j] * y[i])
+ Iy += (x[i]**2 + x[i] * x[j] + x[j]**2) * (x[i] * y[j] - x[j] * y[i])
+
+ Ix /= 12
+ Iy /= 12
+
+ return Ix, Iy
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/AeroFrame_new/tests/ToolOutput/.keep b/ceasiompy/AeroFrame_new/tests/ToolOutput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/AeroFrame_new/tests/ToolOutput/.keep
+++ b/ceasiompy/AeroFrame_new/tests/ToolOutput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/BalanceConventional/ToolInput/.keep b/ceasiompy/BalanceConventional/ToolInput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/BalanceConventional/ToolInput/.keep
+++ b/ceasiompy/BalanceConventional/ToolInput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/BalanceConventional/ToolOutput/.keep b/ceasiompy/BalanceConventional/ToolOutput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/BalanceConventional/ToolOutput/.keep
+++ b/ceasiompy/BalanceConventional/ToolOutput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/BalanceConventional/__init__.py b/ceasiompy/BalanceConventional/__init__.py
index e69de29bb..ab1f319f5 100644
--- a/ceasiompy/BalanceConventional/__init__.py
+++ b/ceasiompy/BalanceConventional/__init__.py
@@ -0,0 +1,46 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for BalanceConventional module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+from ceasiompy import log
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+# True if the module is active.
+# False if the module is disabled (not working or not ready).
+module_status = False
+
+# ===== Include GUI =====
+# True if you want to add a GUI for this module.
+# False if module is desactivated or no GUI to be displayed.
+include_gui = False
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+
+# ==============================================================================
+# MAIN
+# ==============================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/BalanceConventional/__specs__.py b/ceasiompy/BalanceConventional/__specs__.py
index 820ea69e3..6e81644d6 100644
--- a/ceasiompy/BalanceConventional/__specs__.py
+++ b/ceasiompy/BalanceConventional/__specs__.py
@@ -1,5 +1,7 @@
from ceasiompy.utils.moduleinterfaces import CPACSInOut
+from ceasiompy import log
+
# ===== Module Status =====
# True if the module is active
# False if the module is disabled (not working or not ready)
@@ -80,3 +82,10 @@
# descr='Description of the output',
# xpath='/cpacs/toolspecific/CEASIOMpy/test/myOutput',
# )
+
+# ==============================================================================
+# MAIN
+# ==============================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/BalanceConventional/balancemain.py b/ceasiompy/BalanceConventional/balanceconventional.py
similarity index 82%
rename from ceasiompy/BalanceConventional/balancemain.py
rename to ceasiompy/BalanceConventional/balanceconventional.py
index 74b2564ce..22a62f630 100644
--- a/ceasiompy/BalanceConventional/balancemain.py
+++ b/ceasiompy/BalanceConventional/balanceconventional.py
@@ -15,7 +15,6 @@
folder after copying it into the ToolOutput folder
as ToolOutput.xml
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-09-27
@@ -31,7 +30,6 @@
import os
import shutil
-from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
@@ -39,28 +37,13 @@
from ceasiompy.BalanceConventional.func.AoutFunc import cpacsbalanceupdate, outputbalancegen
from ceasiompy.BalanceConventional.func.Cog.centerofgravity import center_of_gravity_evaluation
from ceasiompy.BalanceConventional.func.Inertia import lumpedmassesinertia
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.ceasiompyutils import aircraft_name
+from ceasiompy.utils.ceasiompyutils import aircraft_name, call_main
from ceasiompy.utils.InputClasses.Conventional import balanceconvclass
-from ceasiompy.utils.moduleinterfaces import (
- check_cpacs_input_requirements,
- get_toolinput_file_path,
- get_tooloutput_file_path,
-)
from ceasiompy.utils.WB.ConvGeometry import geometry
from cpacspy.cpacspy import CPACS
-log = get_logger()
-
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-# All classes are defined inside the InputClasses/Conventional
-
+from ceasiompy import log
+from ceasiompy.BalanceConventional import MODULE_NAME
# =================================================================================================
# FUNCTIONS
@@ -94,7 +77,11 @@ def check_rounding(I1, I2):
return rd
-def get_balance_estimations(cpacs_path, cpacs_out_path):
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+def main(cpacs: CPACS) -> None:
"""Function to estimate inertia value and CoF of an conventional aircraft.
Function 'get_balance_unc_estimations' ...
@@ -104,11 +91,10 @@ def get_balance_estimations(cpacs_path, cpacs_out_path):
Args:
cpacs_path (str): Path to CPACS file
- cpacs_out_path (str):Path to CPACS output file
+ cpacs_path (str):Path to CPACS output file
"""
-
- cpacs = CPACS(cpacs_path)
+ cpacs_path = cpacs.cpacs_file
# TODO: when refactor, use Pathlib and absolute path
# Removing and recreating the ToolOutput folder.
@@ -121,7 +107,7 @@ def get_balance_estimations(cpacs_path, cpacs_out_path):
name = aircraft_name(cpacs_path)
- shutil.copyfile(cpacs_path, cpacs_out_path) # TODO: shoud not be like that
+ # shutil.copyfile(cpacs_path, cpacs_path) # TODO: shoud not be like that
newpath = "ToolOutput/" + name
if not os.path.exists(newpath):
os.makedirs(newpath)
@@ -130,7 +116,7 @@ def get_balance_estimations(cpacs_path, cpacs_out_path):
bi = balanceconvclass.BalanceInputs()
out = balanceconvclass.BalanceOutputs()
mw = balanceconvclass.MassesWeights()
- (mw, bi) = getdatafromcpacs.get_data(mw, bi, cpacs_out_path)
+ (mw, bi) = getdatafromcpacs.get_data(mw, bi, cpacs_path)
# BALANCE ANALYSIS
@@ -139,7 +125,7 @@ def get_balance_estimations(cpacs_path, cpacs_out_path):
F_PERC_MAXPASS = (mw.mass_fuel_maxpass / mw.mass_fuel_max) * 100
# CENTER OF GRAVITY---------------------------------------------------------
- # ag = geometry.geometry_eval(cpacs_out_path, name)
+ # ag = geometry.geometry_eval(cpacs_path, name)
# TODO: get CPACS object
ag = geometry.AircraftGeometry()
ag.fuse_geom_eval(cpacs)
@@ -148,22 +134,22 @@ def get_balance_estimations(cpacs_path, cpacs_out_path):
log.info("------- Center of Gravity coordinates -------")
log.info("--------- Max Payload configuration ---------")
- (out.center_of_gravity, mass_seg_i, airplane_centers_segs) = center_of_gravity_evaluation(
+ (out.center_of_gravity, mass_seg_i, _) = center_of_gravity_evaluation(
F_PERC_MAXPASS, 100, ag.cabin_seg, ag, mw, bi.WING_MOUNTED
)
log.info("[x, y, z] = " + str(out.center_of_gravity))
log.info("---------- Zero Fuel configuration ----------")
- (out.cg_zfm, ms_zfm, airplane_centers_segs) = center_of_gravity_evaluation(
+ (out.cg_zfm, ms_zfm, _) = center_of_gravity_evaluation(
0, 100, ag.cabin_seg, ag, mw, bi.WING_MOUNTED
)
log.info("[x, y, z] = " + str(out.cg_zfm))
log.info("-------- Zero Payload configuration ---------")
- (out.cg_zpm, ms_zpm, airplane_centers_segs) = center_of_gravity_evaluation(
+ (out.cg_zpm, ms_zpm, _) = center_of_gravity_evaluation(
100, 0, ag.cabin_seg, ag, mw, bi.WING_MOUNTED
)
log.info("[x, y, z] = " + str(out.cg_zpm))
log.info("------------- OEM configuration -------------")
- (out.cg_oem, ms_oem, airplane_centers_segs) = center_of_gravity_evaluation(
+ (out.cg_oem, ms_oem, _) = center_of_gravity_evaluation(
0, 0, ag.cabin_seg, ag, mw, bi.WING_MOUNTED
)
log.info("[x, y, z] = " + str(out.cg_oem))
@@ -181,7 +167,7 @@ def get_balance_estimations(cpacs_path, cpacs_out_path):
bi.F_PERC = 1 + ((mw.mass_payload / mw.mass_fuel_maxpass) * (1 - (bi.P_PERC / 100.0)))
log.warning("FUEL percentage: " + str(bi.F_PERC))
log.info("------------- User configuration ------------")
- (out.cg_user, ms_user, airplane_centers_segs) = center_of_gravity_evaluation(
+ (out.cg_user, ms_user, _) = center_of_gravity_evaluation(
bi.F_PERC * 100, bi.P_PERC, ag.cabin_seg, ag, mw, bi.WING_MOUNTED
)
@@ -190,10 +176,10 @@ def get_balance_estimations(cpacs_path, cpacs_out_path):
log.info("------------ Lumped mass Inertia ------------")
log.info("--------- Max Payload configuration ---------")
(_, _, _, Ixxf, Iyyf, Izzf, Ixyf, Iyzf, Ixzf) = lumpedmassesinertia.fuselage_inertia(
- bi.SPACING_FUSE, out.center_of_gravity, mass_seg_i, ag, cpacs_out_path
+ bi.SPACING_FUSE, out.center_of_gravity, mass_seg_i, ag, cpacs_path
)
(_, _, _, Ixxw, Iyyw, Izzw, Ixyw, Iyzw, Ixzw) = lumpedmassesinertia.wing_inertia(
- bi.WPP, bi.SPACING_WING, out.center_of_gravity, mass_seg_i, ag, cpacs_out_path
+ bi.WPP, bi.SPACING_WING, out.center_of_gravity, mass_seg_i, ag, cpacs_path
)
rd = check_rounding(Ixxf + Ixxw, Iyzf + Iyzw)
@@ -206,10 +192,10 @@ def get_balance_estimations(cpacs_path, cpacs_out_path):
log.info("---------- Zero Fuel configuration ----------")
(_, _, _, Ixxf2, Iyyf2, Izzf2, Ixyf2, Iyzf2, Ixzf2) = lumpedmassesinertia.fuselage_inertia(
- bi.SPACING_FUSE, out.cg_zfm, ms_zfm, ag, cpacs_out_path
+ bi.SPACING_FUSE, out.cg_zfm, ms_zfm, ag, cpacs_path
)
(_, _, _, Ixxw2, Iyyw2, Izzw2, Ixyw2, Iyzw2, Ixzw2) = lumpedmassesinertia.wing_inertia(
- bi.WPP, bi.SPACING_WING, out.cg_zfm, ms_zfm, ag, cpacs_out_path
+ bi.WPP, bi.SPACING_WING, out.cg_zfm, ms_zfm, ag, cpacs_path
)
out.Ixx_lump_zfm = round(Ixxf2 + Ixxw2, rd)
@@ -221,10 +207,10 @@ def get_balance_estimations(cpacs_path, cpacs_out_path):
log.info("--------- Zero Payload configuration --------")
(_, _, _, Ixxf3, Iyyf3, Izzf3, Ixyf3, Iyzf3, Ixzf3) = lumpedmassesinertia.fuselage_inertia(
- bi.SPACING_FUSE, out.cg_zpm, ms_zpm, ag, cpacs_out_path
+ bi.SPACING_FUSE, out.cg_zpm, ms_zpm, ag, cpacs_path
)
(_, _, _, Ixxw3, Iyyw3, Izzw3, Ixyw3, Iyzw3, Ixzw3) = lumpedmassesinertia.wing_inertia(
- bi.WPP, bi.SPACING_WING, out.cg_zpm, ms_zpm, ag, cpacs_out_path
+ bi.WPP, bi.SPACING_WING, out.cg_zpm, ms_zpm, ag, cpacs_path
)
out.Ixx_lump_zpm = round(Ixxf3 + Ixxw3, rd)
@@ -235,11 +221,11 @@ def get_balance_estimations(cpacs_path, cpacs_out_path):
out.Ixz_lump_zpm = round(Ixzf3 + Ixzw3, rd)
log.info("------------- OEM configuration -------------")
- (fx, fy, fz, Ixxf4, Iyyf4, Izzf4, Ixyf4, Iyzf4, Ixzf4) = lumpedmassesinertia.fuselage_inertia(
- bi.SPACING_FUSE, out.cg_oem, ms_oem, ag, cpacs_out_path
+ (_, _, _, Ixxf4, Iyyf4, Izzf4, Ixyf4, Iyzf4, Ixzf4) = lumpedmassesinertia.fuselage_inertia(
+ bi.SPACING_FUSE, out.cg_oem, ms_oem, ag, cpacs_path
)
- (wx, wy, wz, Ixxw4, Iyyw4, Izzw4, Ixyw4, Iyzw4, Ixzw4) = lumpedmassesinertia.wing_inertia(
- bi.WPP, bi.SPACING_WING, out.cg_oem, ms_oem, ag, cpacs_out_path
+ (_, _, _, Ixxw4, Iyyw4, Izzw4, Ixyw4, Iyzw4, Ixzw4) = lumpedmassesinertia.wing_inertia(
+ bi.WPP, bi.SPACING_WING, out.cg_oem, ms_oem, ag, cpacs_path
)
out.Ixx_lump_oem = round(Ixxf4 + Ixxw4, rd)
@@ -252,9 +238,9 @@ def get_balance_estimations(cpacs_path, cpacs_out_path):
if bi.USER_CASE:
log.info("------------- User configuration ------------")
(
- fx,
- fy,
- fz,
+ _,
+ _,
+ _,
Ixxfu,
Iyyfu,
Izzfu,
@@ -262,10 +248,10 @@ def get_balance_estimations(cpacs_path, cpacs_out_path):
Iyzfu,
Ixzfu,
) = lumpedmassesinertia.fuselage_inertia(
- bi.SPACING_FUSE, out.cg_user, ms_user, ag, cpacs_out_path
+ bi.SPACING_FUSE, out.cg_user, ms_user, ag, cpacs_path
)
- (wx, wy, wz, Ixxwu, Iyywu, Izzwu, Ixywu, Iyzwu, Ixzwu) = lumpedmassesinertia.wing_inertia(
- bi.WPP, bi.SPACING_WING, out.cg_user, ms_user, ag, cpacs_out_path
+ (_, _, _, Ixxwu, Iyywu, Izzwu, Ixywu, Iyzwu, Ixzwu) = lumpedmassesinertia.wing_inertia(
+ bi.WPP, bi.SPACING_WING, out.cg_user, ms_user, ag, cpacs_path
)
out.Ixx_lump_user = round(Ixxfu + Ixxwu, rd)
@@ -281,7 +267,7 @@ def get_balance_estimations(cpacs_path, cpacs_out_path):
outputbalancegen.output_txt(out, mw, bi, name)
# CPACS WRITING
- cpacsbalanceupdate.cpacs_mbd_update(out, mw, bi, np.sum(ms_zpm), cpacs_out_path)
+ cpacsbalanceupdate.cpacs_mbd_update(out, mw, bi, np.sum(ms_zpm), cpacs_path)
# PLOTS
# Aircraft Cog Plot
@@ -363,24 +349,5 @@ def get_balance_estimations(cpacs_path, cpacs_out_path):
log.info("############## Balance estimation completed ##############")
-# =================================================================================================
-# MAIN
-# =================================================================================================
-
-
-def main(cpacs_path, cpacs_out_path):
-
- log.info("----- Start of " + MODULE_NAME + " -----")
-
- check_cpacs_input_requirements(cpacs_path)
- get_balance_estimations(cpacs_path, cpacs_out_path)
-
- log.info("----- End of " + MODULE_NAME + " -----")
-
-
if __name__ == "__main__":
-
- cpacs_path = get_toolinput_file_path(MODULE_NAME)
- cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
-
- main(cpacs_path, cpacs_out_path)
+ call_main(main, MODULE_NAME)
diff --git a/ceasiompy/BalanceConventional/func/AinFunc/getdatafromcpacs.py b/ceasiompy/BalanceConventional/func/AinFunc/getdatafromcpacs.py
index dbda3a251..a79ab5584 100644
--- a/ceasiompy/BalanceConventional/func/AinFunc/getdatafromcpacs.py
+++ b/ceasiompy/BalanceConventional/func/AinFunc/getdatafromcpacs.py
@@ -31,19 +31,10 @@
# IMPORTS
# =============================================================================
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from cpacspy.cpacsfunctions import create_branch, open_tixi
-log = get_logger()
-
-
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-# All classes are defined inside the InputClasses/Conventional
-
# =============================================================================
# FUNCTIONS
diff --git a/ceasiompy/BalanceConventional/func/AoutFunc/cpacsbalanceupdate.py b/ceasiompy/BalanceConventional/func/AoutFunc/cpacsbalanceupdate.py
index 870d34e7d..fa12ee2c5 100644
--- a/ceasiompy/BalanceConventional/func/AoutFunc/cpacsbalanceupdate.py
+++ b/ceasiompy/BalanceConventional/func/AoutFunc/cpacsbalanceupdate.py
@@ -14,20 +14,9 @@
# =============================================================================
# IMPORTS
# =============================================================================
-
from cpacspy.cpacsfunctions import add_uid, create_branch, open_tixi
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-
-log = get_logger()
-
-
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-# All classes are defined inside the InputClasses/Conventional
+from ceasiompy import log
# =============================================================================
diff --git a/ceasiompy/BalanceConventional/func/AoutFunc/outputbalancegen.py b/ceasiompy/BalanceConventional/func/AoutFunc/outputbalancegen.py
index 871bf20ac..7c06e6184 100644
--- a/ceasiompy/BalanceConventional/func/AoutFunc/outputbalancegen.py
+++ b/ceasiompy/BalanceConventional/func/AoutFunc/outputbalancegen.py
@@ -20,13 +20,6 @@
import matplotlib.pyplot as plt
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-# All classes are defined inside the InputClasses/Conventional
-
-
# =============================================================================
# FUNCTIONS
# =============================================================================
diff --git a/ceasiompy/BalanceConventional/func/Cog/centerofgravity.py b/ceasiompy/BalanceConventional/func/Cog/centerofgravity.py
index 9b7253a70..64a612bee 100644
--- a/ceasiompy/BalanceConventional/func/Cog/centerofgravity.py
+++ b/ceasiompy/BalanceConventional/func/Cog/centerofgravity.py
@@ -24,9 +24,8 @@
import numpy as np
-from ceasiompy.utils.ceasiomlogger import get_logger
-log = get_logger()
+from ceasiompy import log
# =============================================================================
@@ -157,6 +156,7 @@ def center_of_gravity_evaluation(F_PERC, P_PERC, cabin_seg, ag, mw, WING_MOUNTED
if htw != 0:
a = wg.index(htw)
else:
+ tw = 0
a = wg.index(tw)
else:
a = wg.index(ag.main_wing_index)
diff --git a/ceasiompy/BalanceConventional/func/Inertia/lumpedmassesinertia.py b/ceasiompy/BalanceConventional/func/Inertia/lumpedmassesinertia.py
index 8827000a2..c92487578 100644
--- a/ceasiompy/BalanceConventional/func/Inertia/lumpedmassesinertia.py
+++ b/ceasiompy/BalanceConventional/func/Inertia/lumpedmassesinertia.py
@@ -21,9 +21,8 @@
from cpacspy.cpacsfunctions import open_tigl, open_tixi
-from ceasiompy.utils.ceasiomlogger import get_logger
-log = get_logger()
+from ceasiompy import log
# =============================================================================
@@ -151,9 +150,9 @@ def fuselage_inertia(SPACING, center_of_gravity, mass_seg_i, ag, cpacs_in):
fx_t = fx * symx
fy_t = fy * symy
fz_t = fz * symz
- [sfx.append(x) for x in fx_t]
- [sfx.append(y) for y in fy_t]
- [sfx.append(z) for z in fz_t]
+ # [sfx.append(x) for x in fx_t]
+ # [sfx.append(y) for y in fy_t]
+ # [sfx.append(z) for z in fz_t]
M = mass_seg_i[int(i) - 1, f + a - 1] / np.max(np.shape(fx))
fcx_t = fx_t - (np.zeros((np.shape(fx_t))) + center_of_gravity[0])
fcy_t = fy_t - (np.zeros((np.shape(fy_t))) + center_of_gravity[1])
diff --git a/ceasiompy/BalanceUnconventional/ToolOutput/.keep b/ceasiompy/BalanceUnconventional/ToolOutput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/BalanceUnconventional/ToolOutput/.keep
+++ b/ceasiompy/BalanceUnconventional/ToolOutput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/BalanceUnconventional/__init__.py b/ceasiompy/BalanceUnconventional/__init__.py
index e69de29bb..be8469721 100644
--- a/ceasiompy/BalanceUnconventional/__init__.py
+++ b/ceasiompy/BalanceUnconventional/__init__.py
@@ -0,0 +1,36 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for BalanceUnconventional module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+# True if the module is active.
+# False if the module is disabled (not working or not ready).
+module_status = False
+
+# ===== Include GUI =====
+# True if you want to add a GUI for this module.
+# False if module is desactivated or no GUI to be displayed.
+include_gui = False
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
diff --git a/ceasiompy/BalanceUnconventional/balanceuncmain.py b/ceasiompy/BalanceUnconventional/balanceunconventional.py
similarity index 90%
rename from ceasiompy/BalanceUnconventional/balanceuncmain.py
rename to ceasiompy/BalanceUnconventional/balanceunconventional.py
index 8b0923d64..b918ab59a 100644
--- a/ceasiompy/BalanceUnconventional/balanceuncmain.py
+++ b/ceasiompy/BalanceUnconventional/balanceunconventional.py
@@ -15,7 +15,6 @@
folder after copying it into the ToolOutput folder
as ToolOutput.xml
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-09-27
@@ -42,7 +41,6 @@
unc_center_of_gravity,
)
from ceasiompy.BalanceUnconventional.func.Inertia import uncinertia
-from ceasiompy.utils.ceasiomlogger import get_logger
from ceasiompy.utils.ceasiompyutils import aircraft_name
from ceasiompy.utils.InputClasses.Unconventional import (
balanceuncclass,
@@ -56,17 +54,8 @@
)
from ceasiompy.utils.WB.UncGeometry import uncgeomanalysis
-log = get_logger()
-
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-"""All classes are defined inside the classes folder and into the
- InputClasses/Uconventional folder"""
+from ceasiompy import log
+from ceasiompy.BalanceUnconventional import MODULE_NAME
# =================================================================================================
@@ -123,7 +112,7 @@ def get_balance_unc_estimations(cpacs_path, cpacs_out_path):
log.warning("Aircraft does not have wings")
raise Exception("Aircraft does not have wings")
elif not fus_nb:
- (awg, wing_nodes) = uncgeomanalysis.no_fuse_geom_analysis(
+ (awg, _) = uncgeomanalysis.no_fuse_geom_analysis(
cpacs_path, ui.FLOORS_NB, w_nb, ui.H_LIM_CABIN, ui.FUEL_ON_CABIN, name, ed.turboprop
)
else:
@@ -146,16 +135,16 @@ def get_balance_unc_estimations(cpacs_path, cpacs_out_path):
# CENTER OF GRAVITY
if not fus_nb:
- (bout, airplane_centers_segs) = bwb_center_of_gravity(awg, bout, ui, bi, mw, ed)
+ (bout, _) = bwb_center_of_gravity(awg, bout, ui, bi, mw, ed)
else:
- (bout, airplane_centers_segs) = unc_center_of_gravity(awg, afg, bout, ui, bi, mw, ed)
+ (bout, _) = unc_center_of_gravity(awg, afg, bout, ui, bi, mw, ed)
# MOMENT OF INERTIA
if not fus_nb:
- (bout, wx, wy, wz) = uncinertia.bwb_inertia_eval(awg, bout, bi, mw, ed, cpacs_out_path)
+ (bout, _, _, _) = uncinertia.bwb_inertia_eval(awg, bout, bi, mw, ed, cpacs_out_path)
else:
- (bout, fx, fy, fz, wx, wy, wz) = uncinertia.unc_inertia_eval(
+ (bout, _, _, _, _, _, _) = uncinertia.unc_inertia_eval(
awg, afg, bout, bi, mw, ed, cpacs_out_path
)
@@ -275,20 +264,19 @@ def get_balance_unc_estimations(cpacs_path, cpacs_out_path):
# =================================================================================================
-def main(cpacs_path, cpacs_out_path):
-
- log.info("----- Start of " + MODULE_NAME + " -----")
-
- check_cpacs_input_requirements(cpacs_path)
+def main(cpacs_path: Path, cpacs_out_path: Path) -> None:
+ module_name = MODULE_NAME
+ log.info("----- Start of " + module_name + " -----")
get_balance_unc_estimations(cpacs_path, cpacs_out_path)
- log.info("----- End of " + MODULE_NAME + " -----")
+ log.info("----- End of " + module_name + " -----")
if __name__ == "__main__":
cpacs_path = get_toolinput_file_path(MODULE_NAME)
cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
+ check_cpacs_input_requirements(cpacs_path)
main(cpacs_path, cpacs_out_path)
diff --git a/ceasiompy/BalanceUnconventional/func/AinFunc/getdatafromcpacs.py b/ceasiompy/BalanceUnconventional/func/AinFunc/getdatafromcpacs.py
index 52ccacbdd..59c5e1b94 100644
--- a/ceasiompy/BalanceUnconventional/func/AinFunc/getdatafromcpacs.py
+++ b/ceasiompy/BalanceUnconventional/func/AinFunc/getdatafromcpacs.py
@@ -34,18 +34,7 @@
from cpacspy.cpacsfunctions import create_branch, open_tixi
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
-
-
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-"""All classes are defined inside the classes folder and into the
- InputClasses/Uconventional folder"""
-
+from ceasiompy import log
# =============================================================================
# FUNCTIONS
diff --git a/ceasiompy/BalanceUnconventional/func/AoutFunc/cpacsbalanceupdate.py b/ceasiompy/BalanceUnconventional/func/AoutFunc/cpacsbalanceupdate.py
index 0113f4df0..ea524b3d4 100644
--- a/ceasiompy/BalanceUnconventional/func/AoutFunc/cpacsbalanceupdate.py
+++ b/ceasiompy/BalanceUnconventional/func/AoutFunc/cpacsbalanceupdate.py
@@ -18,17 +18,8 @@
from cpacspy.cpacsfunctions import add_uid, create_branch, open_tixi
-from ceasiompy.utils.ceasiomlogger import get_logger
-log = get_logger()
-
-
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-"""All classes are defined inside the classes folder and into the
- InputClasses/Uconventional folder"""
+from ceasiompy import log
# =============================================================================
diff --git a/ceasiompy/BalanceUnconventional/func/AoutFunc/outputbalancegen.py b/ceasiompy/BalanceUnconventional/func/AoutFunc/outputbalancegen.py
index 5107f667e..cdc4bd2d7 100644
--- a/ceasiompy/BalanceUnconventional/func/AoutFunc/outputbalancegen.py
+++ b/ceasiompy/BalanceUnconventional/func/AoutFunc/outputbalancegen.py
@@ -20,15 +20,6 @@
import matplotlib as mpl
import matplotlib.pyplot as plt
-
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-"""All classes are defined inside the classes folder and into the
- InputClasses/Uconventional folder"""
-
-
# =============================================================================
# FUNCTIONS
# =============================================================================
@@ -327,7 +318,6 @@ def aircraft_cog_unc_plot(cg, bi, ed, afg, awg, NAME):
def aircraft_cog_bwb_plot(cg, bi, ed, awg, NAME):
-
"""The function that generate the plot of the blended wing body
center of gravity and the nodes used to evaluate it.
diff --git a/ceasiompy/BalanceUnconventional/func/Cog/bwbcog.py b/ceasiompy/BalanceUnconventional/func/Cog/bwbcog.py
index 0c03152ac..acfae8136 100644
--- a/ceasiompy/BalanceUnconventional/func/Cog/bwbcog.py
+++ b/ceasiompy/BalanceUnconventional/func/Cog/bwbcog.py
@@ -19,18 +19,8 @@
import numpy as np
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
-
-
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-"""All classes are defined inside the classes and into
- the InputClasses/Unconventional folder."""
+from ceasiompy import log
# =============================================================================
# FUNCTIONS
diff --git a/ceasiompy/BalanceUnconventional/func/Cog/fusecog.py b/ceasiompy/BalanceUnconventional/func/Cog/fusecog.py
index 7b8f249ce..429a061d5 100644
--- a/ceasiompy/BalanceUnconventional/func/Cog/fusecog.py
+++ b/ceasiompy/BalanceUnconventional/func/Cog/fusecog.py
@@ -20,17 +20,8 @@
import numpy as np
-from ceasiompy.utils.ceasiomlogger import get_logger
-log = get_logger()
-
-
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-"""All classes are defined inside the classes and into
- the InputClasses/Unconventional folder."""
+from ceasiompy import log
# =============================================================================
@@ -99,7 +90,8 @@ def center_of_gravity_evaluation(F_PERC, P_PERC, afg, awg, mw, ed, ui, bi):
x0 = x
mass_seg_i = np.zeros((max_seg_n, tot_nb))
- oem_vol = (awg.wing_tot_vol - awg.wing_fuel_vol) + (np.sum(afg.fuse_vol) - fuse_fuel_vol)
+ oem_vol = (awg.wing_tot_vol - awg.wing_fuel_vol) + \
+ (np.sum(afg.fuse_vol) - fuse_fuel_vol)
# Evaluating oem density, fuel density, passenger density
if bi.USER_EN_PLACEMENT:
@@ -111,7 +103,8 @@ def center_of_gravity_evaluation(F_PERC, P_PERC, afg, awg, mw, ed, ui, bi):
mpass_par = (mw.mass_payload * (P_PERC / 100.0)) / pass_vol
- mfuel_par = (mw.mass_fuel_tot * (F_PERC / 100.0)) / (awg.wing_fuel_vol + fuse_fuel_vol)
+ mfuel_par = (mw.mass_fuel_tot * (F_PERC / 100.0)) / \
+ (awg.wing_fuel_vol + fuse_fuel_vol)
mtom = (
mw.operating_empty_mass
@@ -136,7 +129,8 @@ def center_of_gravity_evaluation(F_PERC, P_PERC, afg, awg, mw, ed, ui, bi):
i - 1
]
else:
- mass_seg_i[j - 1][i - 1] = oem_par * afg.fuse_seg_vol[j - 1][i - 1]
+ mass_seg_i[j - 1][i - 1] = oem_par * \
+ afg.fuse_seg_vol[j - 1][i - 1]
w = 0
for i in range(afg.fus_nb + 1, t_nb + 1):
for j in range(1, awg.wing_seg_nb[i - 1 - afg.fus_nb] + 1):
@@ -194,13 +188,16 @@ def center_of_gravity_evaluation(F_PERC, P_PERC, afg, awg, mw, ed, ui, bi):
center_of_gravity = []
center_of_gravity.append(
- round((np.sum(airplane_centers_segs[:, :, 0] * mass_seg_i) + cog_enx) / mtom, 3)
+ round(
+ (np.sum(airplane_centers_segs[:, :, 0] * mass_seg_i) + cog_enx) / mtom, 3)
)
center_of_gravity.append(
- round((np.sum(airplane_centers_segs[:, :, 1] * mass_seg_i) + cog_eny) / mtom, 3)
+ round(
+ (np.sum(airplane_centers_segs[:, :, 1] * mass_seg_i) + cog_eny) / mtom, 3)
)
center_of_gravity.append(
- round((np.sum(airplane_centers_segs[:, :, 2] * mass_seg_i) + cog_enz) / mtom, 3)
+ round(
+ (np.sum(airplane_centers_segs[:, :, 2] * mass_seg_i) + cog_enz) / mtom, 3)
)
for i in range(1, 4):
diff --git a/ceasiompy/BalanceUnconventional/func/Cog/unccog.py b/ceasiompy/BalanceUnconventional/func/Cog/unccog.py
index a2678bf20..7d06a60bd 100644
--- a/ceasiompy/BalanceUnconventional/func/Cog/unccog.py
+++ b/ceasiompy/BalanceUnconventional/func/Cog/unccog.py
@@ -24,22 +24,15 @@
from .fusecog import center_of_gravity_evaluation
from .bwbcog import center_of_gravity_evaluation_bwb
-from ceasiompy.utils.ceasiomlogger import get_logger
-log = get_logger()
+from ceasiompy import log
# =============================================================================
-# CLASSES
+# FUNCTIONS
# =============================================================================
-"""All classes are defined inside the classes and into
- the InputClasses/Unconventional folder."""
-
-# =============================================================================
-# FUNCTIONS
-# =============================================================================
def unc_center_of_gravity(awg, afg, bout, ui, bi, mw, ed):
"""Unconventional aircraft center of gravity analysis main function.
It dvides the cases defined and evaluates them calling the
@@ -106,7 +99,7 @@ def unc_center_of_gravity(awg, afg, bout, ui, bi, mw, ed):
if bi.USER_CASE:
if bi.P_PERC < 0 or bi.F_PERC < 0:
- raise Exception("Error, F_PERC and P_PERC can" + " not be negative.")
+ raise Exception("Error, F_PERC and P_PERC can not be negative.")
if (
mw.mass_fuel_maxpass * (bi.F_PERC / 100) + mw.mass_payload * (bi.P_PERC / 100)
) > mw.mass_fuel_maxpass + mw.mass_payload:
@@ -115,7 +108,8 @@ def unc_center_of_gravity(awg, afg, bout, ui, bi, mw, ed):
+ "chosen payload mass,"
+ "fuel mass automatically reduced"
)
- bi.F_PERC = 1 + ((mw.mass_payload / mw.mass_fuel_maxpass) * (1 - (bi.P_PERC / 100)))
+ bi.F_PERC = 1 + \
+ ((mw.mass_payload / mw.mass_fuel_maxpass) * (1 - (bi.P_PERC / 100)))
log.warning("FUEL percentage: " + str(bi.F_PERC * 100))
log.info("---------- User configuration ---------")
(bout.cg_user, mw.ms_user, airplane_centers_segs) = center_of_gravity_evaluation(
@@ -182,7 +176,7 @@ def bwb_center_of_gravity(awg, bout, ui, bi, mw, ed):
if bi.USER_CASE:
if bi.P_PERC < 0 or bi.F_PERC < 0:
- raise Exception("Error, F_PERC and P_PERC can" + " not be negative.")
+ raise Exception("Error, F_PERC and P_PERC can not be negative.")
if (
mw.mass_fuel_maxpass * (bi.F_PERC / 100) + mw.mass_payload * (bi.P_PERC / 100)
) > mw.mass_fuel_maxpass + mw.mass_payload:
@@ -191,7 +185,8 @@ def bwb_center_of_gravity(awg, bout, ui, bi, mw, ed):
+ "chosen payload mass,"
+ "fuel mass automatically reduced"
)
- bi.F_PERC = 1 + ((mw.mass_payload / mw.mass_fuel_maxpass) * (1 - bi.P_PERC / 100))
+ bi.F_PERC = 1 + \
+ ((mw.mass_payload / mw.mass_fuel_maxpass) * (1 - bi.P_PERC / 100))
log.warning("FUEL percentage: " + str(bi.F_PERC))
log.info("---------- User configuration ---------")
(bout.cg_user, mw.ms_user, airplane_centers_segs) = center_of_gravity_evaluation_bwb(
diff --git a/ceasiompy/BalanceUnconventional/func/Inertia/lumpedmassesinertia.py b/ceasiompy/BalanceUnconventional/func/Inertia/lumpedmassesinertia.py
index 12650280d..ddc367a73 100644
--- a/ceasiompy/BalanceUnconventional/func/Inertia/lumpedmassesinertia.py
+++ b/ceasiompy/BalanceUnconventional/func/Inertia/lumpedmassesinertia.py
@@ -22,22 +22,15 @@
from cpacspy.cpacsfunctions import open_tigl, open_tixi
-from ceasiompy.utils.ceasiomlogger import get_logger
-log = get_logger()
+from ceasiompy import log
# =============================================================================
-# CLASSES
+# FUNCTIONS
# =============================================================================
-"""All classes are defined inside the classes and into
- the InputClasses/Unconventional folder."""
-
-# =============================================================================
-# FUNCTIONS
-# =============================================================================
def fuselage_inertia(SPACING, center_of_gravity, mass_seg_i, afg, cpacs_in):
"""Thefunction evaluates the inertia of the fuselage using the lumped
masses method.
@@ -81,11 +74,14 @@ def fuselage_inertia(SPACING, center_of_gravity, mass_seg_i, afg, cpacs_in):
fy = []
fz = []
# Number of subdivisions along the longitudinal axis
- subd_l = math.ceil((afg.fuse_seg_length[int(i) - 1][f - 1] / SPACING))
+ subd_l = math.ceil(
+ (afg.fuse_seg_length[int(i) - 1][f - 1] / SPACING))
# Number of subdivisions along the perimeter
- SUBD_C0 = math.ceil((afg.fuse_sec_per[int(i) - 1][f - 1] / SPACING))
+ SUBD_C0 = math.ceil(
+ (afg.fuse_sec_per[int(i) - 1][f - 1] / SPACING))
# Number of subdivisions along the radial axis
- subd_r = math.ceil(((afg.fuse_sec_width[int(i) - 1][f - 1] / 2) / SPACING))
+ subd_r = math.ceil(
+ ((afg.fuse_sec_width[int(i) - 1][f - 1] / 2) / SPACING))
if subd_l == 0:
subd_l = 1.0
if SUBD_C0 == 0:
@@ -193,7 +189,8 @@ def wing_inertia(subd_c, SPACING, fuse, center_of_gravity, mass_seg_i, awg, cpac
wy = []
wz = []
# Number of subdivisions along the longitudinal axis
- subd_l = math.ceil((awg.wing_seg_length[int(i) - 1][w + a - 1] / SPACING))
+ subd_l = math.ceil(
+ (awg.wing_seg_length[int(i) - 1][w + a - 1] / SPACING))
if subd_l == 0:
subd_l = 1
eta = 1.0 / subd_l
@@ -257,7 +254,8 @@ def wing_inertia(subd_c, SPACING, fuse, center_of_gravity, mass_seg_i, awg, cpac
[swx.append(x) for x in wx_t]
[swy.append(y) for y in wy_t]
[swz.append(z) for z in wz_t]
- M = mass_seg_i[int(i) - 1, fuse + w + a - 1] / np.max(np.shape(wx_t))
+ M = mass_seg_i[int(i) - 1, fuse + w + a - 1] / \
+ np.max(np.shape(wx_t))
wcx_t = wx_t - (np.zeros((np.shape(wx_t))) + center_of_gravity[0])
wcy_t = wy_t - (np.zeros((np.shape(wy_t))) + center_of_gravity[1])
wcz_t = wz_t - (np.zeros((np.shape(wz_t))) + center_of_gravity[2])
diff --git a/ceasiompy/BalanceUnconventional/func/Inertia/uncinertia.py b/ceasiompy/BalanceUnconventional/func/Inertia/uncinertia.py
index 67fb3a295..8746e2c74 100644
--- a/ceasiompy/BalanceUnconventional/func/Inertia/uncinertia.py
+++ b/ceasiompy/BalanceUnconventional/func/Inertia/uncinertia.py
@@ -24,17 +24,8 @@
from . import lumpedmassesinertia
-from ceasiompy.utils.ceasiomlogger import get_logger
-log = get_logger()
-
-
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-"""All classes are defined inside the classes and into
- the InputClasses/Unconventional folder."""
+from ceasiompy import log
# =============================================================================
diff --git a/ceasiompy/CLCalculator/ToolInput/.directory b/ceasiompy/CLCalculator/ToolInput/.directory
deleted file mode 100644
index 1608020c1..000000000
--- a/ceasiompy/CLCalculator/ToolInput/.directory
+++ /dev/null
@@ -1,6 +0,0 @@
-[Dolphin]
-Timestamp=2019,8,7,11,24,16
-Version=3
-
-[Settings]
-HiddenFilesShown=true
diff --git a/ceasiompy/CLCalculator/ToolInput/.keep b/ceasiompy/CLCalculator/ToolInput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/CLCalculator/ToolInput/.keep
+++ b/ceasiompy/CLCalculator/ToolInput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/CLCalculator/ToolOutput/.directory b/ceasiompy/CLCalculator/ToolOutput/.directory
deleted file mode 100644
index 954a4f861..000000000
--- a/ceasiompy/CLCalculator/ToolOutput/.directory
+++ /dev/null
@@ -1,6 +0,0 @@
-[Dolphin]
-Timestamp=2019,8,7,11,24,33
-Version=3
-
-[Settings]
-HiddenFilesShown=true
diff --git a/ceasiompy/CLCalculator/ToolOutput/.keep b/ceasiompy/CLCalculator/ToolOutput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/CLCalculator/ToolOutput/.keep
+++ b/ceasiompy/CLCalculator/ToolOutput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/CLCalculator/__init__.py b/ceasiompy/CLCalculator/__init__.py
index e69de29bb..809825134 100644
--- a/ceasiompy/CLCalculator/__init__.py
+++ b/ceasiompy/CLCalculator/__init__.py
@@ -0,0 +1,45 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for CLCalculator module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+from ceasiompy import log
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = True
+
+# ===== Include GUI =====
+include_gui = True
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+# ===== Add a Results Directory =====
+RES_DIR = True
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/CLCalculator/__specs__.py b/ceasiompy/CLCalculator/__specs__.py
index e404ed19b..d135af4e9 100644
--- a/ceasiompy/CLCalculator/__specs__.py
+++ b/ceasiompy/CLCalculator/__specs__.py
@@ -1,5 +1,26 @@
-from pathlib import Path
+
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+GUI Interface of CLCalculator.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
from ceasiompy.utils.moduleinterfaces import CPACSInOut
+
+from ceasiompy import log
+from ceasiompy.CLCalculator import include_gui
+
from ceasiompy.utils.commonxpath import (
REF_XPATH,
CLCALC_XPATH,
@@ -7,29 +28,25 @@
SU2_TARGET_CL_XPATH,
)
-# ===== Module Status =====
-# True if the module is active
-# False if the module is disabled (not working or not ready)
-module_status = True
-
-# ===== Results directory path =====
-
-RESULTS_DIR = Path("Results", "CLCalculator")
-
-# ===== CPACS inputs and outputs =====
+# ==============================================================================
+# VARIABLE
+# ==============================================================================
cpacs_inout = CPACSInOut()
-# ===== Input =====
+# ==============================================================================
+# GUI INPUTS
+# ==============================================================================
cpacs_inout.add_input(
var_name="mass_type",
var_type=list,
- default_value=["mTOM", "mZFM", "Custom", "% fuel mass"],
+
+ default_value=["mTOM", "mZFM", "Custom", "%% fuel mass"],
unit=None,
descr="Type of mass to use for CL calculation",
xpath=CLCALC_XPATH + "/massType",
- gui=True,
+ gui=include_gui,
gui_name="Type",
gui_group="Mass",
)
@@ -41,7 +58,7 @@
unit="kg",
descr="Mass value if Custom is selected",
xpath=CLCALC_XPATH + "/customMass",
- gui=True,
+ gui=include_gui,
gui_name="Custom mass",
gui_group="Mass",
)
@@ -50,10 +67,10 @@
var_name="percent_fuel_mass",
var_type=float,
default_value=100,
- unit="-",
+ unit=None,
descr="Percentage of fuel mass between mTOM and mZFM, if % fuel mass is selected",
xpath=CLCALC_XPATH + "/percentFuelMass",
- gui=True,
+ gui=include_gui,
gui_name="Percent fuel mass",
gui_group="Mass",
)
@@ -62,10 +79,10 @@
var_name="cruise_mach",
var_type=float,
default_value=0.78,
- unit="1",
+ unit=None,
descr="Aircraft cruise Mach number",
xpath=CLCALC_XPATH + "/cruiseMach",
- gui=True,
+ gui=include_gui,
gui_name="Mach",
gui_group="Cruise",
)
@@ -74,10 +91,10 @@
var_name="cruise_alt",
var_type=float,
default_value=12000.0,
- unit="m",
+ unit="[m]",
descr="Aircraft cruise altitude",
xpath=CLCALC_XPATH + "/cruiseAltitude",
- gui=True,
+ gui=include_gui,
gui_name="Altitude",
gui_group="Cruise",
)
@@ -86,10 +103,10 @@
var_name="load_fact",
var_type=float,
default_value=1.05,
- unit="1",
+ unit=None,
descr="Aircraft cruise altitude",
xpath=CLCALC_XPATH + "/loadFactor",
- gui=True,
+ gui=include_gui,
gui_name="Load Factor",
gui_group="Cruise",
)
@@ -98,21 +115,22 @@
var_name="ref_area",
var_type=float,
default_value=None,
- unit="m^2",
+ unit="[m^2]",
descr="Aircraft reference area",
xpath=REF_XPATH + "/area",
gui=False,
gui_name=None,
- gui_group=None,
+ gui_group="Reference Area",
)
-
-# ===== Output =====
+# ==============================================================================
+# GUI OUTPUTS
+# ==============================================================================
cpacs_inout.add_output(
var_name="target_cl",
default_value=None,
- unit="1",
+ unit=None,
descr="Value of CL to achieve to have a level flight with the given conditions",
xpath=SU2_TARGET_CL_XPATH,
)
@@ -120,7 +138,14 @@
cpacs_inout.add_output(
var_name="fixed_cl",
default_value=None,
- unit="-",
+ unit=None,
descr="FIXED_CL_MODE parameter for SU2",
xpath=SU2_FIXED_CL_XPATH,
)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/CLCalculator/clcalculator.py b/ceasiompy/CLCalculator/clcalculator.py
index ae912048c..c56038029 100644
--- a/ceasiompy/CLCalculator/clcalculator.py
+++ b/ceasiompy/CLCalculator/clcalculator.py
@@ -5,13 +5,11 @@
Calculate lift coefficient required to fly at specific alt, mach, mass and LF
-Python version: >=3.8
| Author: Aidan Jungo
| Creation: 2018-11-28
TODO:
-
* Save CruiseCL somewhere
"""
@@ -20,147 +18,55 @@
# IMPORTS
# =================================================================================================
-from pathlib import Path
+from ceasiompy.utils.ceasiompyutils import call_main
+from ceasiompy.CLCalculator.func.calculatecl import calculate_cl
-from ambiance import Atmosphere
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.ceasiompyutils import get_results_directory
-from ceasiompy.utils.commonxpath import (
- CLCALC_XPATH,
- MASSBREAKDOWN_XPATH,
- REF_XPATH,
- SU2_FIXED_CL_XPATH,
- SU2_TARGET_CL_XPATH,
- SU2_XPATH,
-)
-from ceasiompy.utils.moduleinterfaces import (
- check_cpacs_input_requirements,
- get_toolinput_file_path,
- get_tooloutput_file_path,
+from ceasiompy.CLCalculator.func.utils import (
+ retrieve_gui,
+ save_for_su2,
+ deal_with_mass,
)
-from cpacspy.cpacsfunctions import create_branch, get_value, get_value_or_default
+
+from pathlib import Path
from cpacspy.cpacspy import CPACS
from markdownpy.markdownpy import MarkdownDoc
-log = get_logger()
+from ceasiompy import log
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
+from ceasiompy.CLCalculator import MODULE_NAME
# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-
-# =================================================================================================
-# FUNCTIONS
+# MAIN
# =================================================================================================
-def calculate_cl(ref_area, alt, mach, mass, load_fact=1.05):
- """Function to calculate the lif coefficient (cl)
-
- Function 'calculate_cl' return the lift coefficient value for the given
- input (Reference Area, Altitude, Mach Number, Mass and Load Factor)
-
- Source:
- * Demonstration can be found in:
- /CEASIOMpy/lib/CLCalculator/doc/Calculate_CL.pdf
-
- Args:
- ref_area (float): Reference area [m^2]
- alt (float): Altitude [m]
- mach (float): Mach number [-]
- mass (float): Aircraft mass [kg]
- load_fact (float): Load Factor [-] (1.05 by default)
-
- Returns:
- target_cl (float): Lift coefficient [unit]
+def main(cpacs: CPACS, wkdir: Path) -> None:
"""
-
- # Get atmosphere values at this altitude
- Atm = Atmosphere(alt)
-
- GAMMA = 1.401 # Air heat capacity ratio [-]
-
- # Calculate lift coefficient
- weight = mass * Atm.grav_accel[0]
- dyn_pres = 0.5 * GAMMA * Atm.pressure[0] * mach**2
- target_cl = weight * load_fact / (dyn_pres * ref_area)
-
- log.info(f"A lift coefficient (CL) of {target_cl} has been calculated")
-
- return target_cl
-
-
-def get_cl(cpacs_path, cpacs_out_path):
- """Function to calculate CL required as a function of the parameter found
- in the CPACS file.
-
- Function 'get_cl' find input value in the CPACS file, calculate the
- required CL (with function calculate_cl) and save the CL value in the
- CPACS file.
-
- Args:
- cpacs_path (Path): Path to CPACS file
- cpacs_out_path (Path): Path to CPACS output file
+ Computes required CL in function
+ of the parameters found in the CPACS file.
"""
- cpacs = CPACS(cpacs_path)
tixi = cpacs.tixi
- results_dir = get_results_directory("CLCalculator")
- md = MarkdownDoc(Path(results_dir, "CL_Calculator.md"))
- md.h2("CLCalculator")
+ ref_area, mass_type, cruise_alt, cruise_mach, load_fact = retrieve_gui(tixi)
- # XPath definition
- ref_area_xpath = REF_XPATH + "/area"
- mass_type_xpath = CLCALC_XPATH + "/massType"
- custom_mass_xpath = CLCALC_XPATH + "/customMass"
- percent_fuel_mass_xpath = CLCALC_XPATH + "/percentFuelMass"
- cruise_alt_xpath = CLCALC_XPATH + "/cruiseAltitude"
- cruise_mach_xpath = CLCALC_XPATH + "/cruiseMach"
- load_fact_xpath = CLCALC_XPATH + "/loadFactor"
+ # Create Markdown file
+ md = MarkdownDoc(Path(wkdir, "CL_Calculator.md"))
+ md.h2("CLCalculator")
# Required input data from CPACS
- ref_area = get_value(tixi, ref_area_xpath)
- log.info(f"Aircraft reference area is {ref_area} [m^2]")
- md.p(f"Aircraft reference area is {ref_area} [m^2]")
+ ref_msg = f"Aircraft reference area is {ref_area} [m^2]"
+ log.info(ref_msg)
+ md.p(ref_msg)
# Mass
- mass = None
- mass_type = get_value_or_default(tixi, mass_type_xpath, "mTOM")
- md.p(f"The mass used for the calculation is {mass_type}")
-
- if mass_type == "Custom":
- mass = get_value(tixi, custom_mass_xpath)
-
- elif mass_type == "% fuel mass":
- percent_fuel_mass = get_value(tixi, percent_fuel_mass_xpath)
- md.p(f"Percentage of fuel mass: {percent_fuel_mass}%")
- mtom = get_value(tixi, MASSBREAKDOWN_XPATH + "/designMasses/mTOM/mass")
- mzfm = get_value(tixi, MASSBREAKDOWN_XPATH + "/designMasses/mZFM/mass")
- if mzfm > mtom:
- raise ValueError("mZFM is bigger than mTOM!")
- mass = (mtom - mzfm) * percent_fuel_mass / 100 + mzfm
-
- else:
- mass_xpath = MASSBREAKDOWN_XPATH + f"/designMasses/{mass_type}/mass"
- mass = get_value(tixi, mass_xpath)
-
- if mass:
- log.info(f"Aircraft mass use for this analysis is {mass} [kg]")
- else:
- raise ValueError("The chosen aircraft mass has not been found!")
+ mass = deal_with_mass(md, tixi, mass_type)
md.p(f"Mass: {mass}[kg]")
# Required input data that could be replace by a default value if missing
md.h3("Flight condition")
- cruise_alt = get_value_or_default(tixi, cruise_alt_xpath, 12000.0)
md.p(f"Cruise altitude: {cruise_alt} [m]")
- cruise_mach = get_value_or_default(tixi, cruise_mach_xpath, 0.78)
md.p(f"Cruise Mach number: {cruise_mach} [-]")
- load_fact = get_value_or_default(tixi, load_fact_xpath, 1.05)
md.p(f"Cruise load factor: {load_fact} [-]")
# CL calculation
@@ -169,35 +75,10 @@ def get_cl(cpacs_path, cpacs_out_path):
md.p(f"CL: {target_cl:.4f} [-]")
# Save TargetCL and fixedCL option
- create_branch(tixi, SU2_XPATH)
- create_branch(tixi, SU2_TARGET_CL_XPATH)
- create_branch(tixi, SU2_FIXED_CL_XPATH)
- tixi.updateDoubleElement(SU2_TARGET_CL_XPATH, target_cl, "%g")
- tixi.updateTextElement(SU2_FIXED_CL_XPATH, "YES")
- log.info("Target CL has been saved in the CPACS file")
+ save_for_su2(tixi, target_cl)
md.save()
- cpacs.save_cpacs(cpacs_out_path, overwrite=True)
-
-
-# =================================================================================================
-# MAIN
-# =================================================================================================
-
-
-def main(cpacs_path, cpacs_out_path):
-
- log.info("----- Start of " + MODULE_NAME + " -----")
-
- check_cpacs_input_requirements(cpacs_path)
- get_cl(cpacs_path, cpacs_out_path)
-
- log.info("----- End of " + MODULE_NAME + " -----")
if __name__ == "__main__":
-
- cpacs_path = get_toolinput_file_path(MODULE_NAME)
- cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
-
- main(cpacs_path, cpacs_out_path)
+ call_main(main, MODULE_NAME)
diff --git a/ceasiompy/CLCalculator/doc/Calculate_CL.pdf b/ceasiompy/CLCalculator/doc/Calculate_CL.pdf
index 6fa06ffa6..c7e492564 100644
Binary files a/ceasiompy/CLCalculator/doc/Calculate_CL.pdf and b/ceasiompy/CLCalculator/doc/Calculate_CL.pdf differ
diff --git a/ceasiompy/CLCalculator/func/calculatecl.py b/ceasiompy/CLCalculator/func/calculatecl.py
new file mode 100644
index 000000000..047f95369
--- /dev/null
+++ b/ceasiompy/CLCalculator/func/calculatecl.py
@@ -0,0 +1,77 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Calculate CL script.
+
+
+| Author: Leon Deligny
+| Creation: 25 March 2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from ambiance import Atmosphere
+
+from ceasiompy import log
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def calculate_cl(
+ ref_area: float,
+ alt: float,
+ mach: float,
+ mass: float,
+ load_fact: float = 1.05,
+) -> float:
+ """
+ Computes the lift coefficient value for the given set of inputs:
+ - Reference Area,
+ - Altitude,
+ - Mach Number,
+ - Mass,
+ - Load Factor.
+
+ Source:
+ /CEASIOMpy/lib/CLCalculator/doc/Calculate_CL.pdf
+
+ Args:
+ ref_area (float): Reference area [m^2].
+ alt (float): Altitude [m].
+ mach (float): Mach number [-].
+ mass (float): Aircraft mass [kg].
+ load_fact (float = 1.05): Load Factor [-].
+
+ Returns:
+ (float): Lift coefficient [???].
+
+ """
+
+ # Get atmosphere values at this altitude
+ Atm = Atmosphere(alt)
+
+ GAMMA = 1.401 # Air heat capacity ratio [-]
+
+ # Calculate lift coefficient
+ weight = mass * Atm.grav_accel[0]
+ dyn_pres = 0.5 * GAMMA * Atm.pressure[0] * mach**2
+ target_cl = weight * load_fact / (dyn_pres * ref_area)
+
+ log.info(f"A lift coefficient (CL) of {target_cl} has been calculated.")
+
+ return float(target_cl)
+
+# ==============================================================================
+# MAIN
+# ==============================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/CLCalculator/func/utils.py b/ceasiompy/CLCalculator/func/utils.py
new file mode 100644
index 000000000..879be8aa0
--- /dev/null
+++ b/ceasiompy/CLCalculator/func/utils.py
@@ -0,0 +1,109 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Utils for CLCalculator.
+
+
+| Author: Leon Deligny
+| Creation: 25 March 2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from cpacspy.cpacsfunctions import (
+ get_value,
+ create_branch,
+)
+
+from tixi3.tixi3wrapper import Tixi3
+from markdownpy.markdownpy import MarkdownDoc
+
+from ceasiompy.utils.commonxpath import (
+ SU2_XPATH,
+ REF_XPATH,
+ CLCALC_XPATH,
+ SU2_FIXED_CL_XPATH,
+ SU2_TARGET_CL_XPATH,
+ MASSBREAKDOWN_XPATH,
+)
+
+from ceasiompy import log
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def save_for_su2(tixi: Tixi3, target_cl: float) -> None:
+ create_branch(tixi, SU2_XPATH)
+ create_branch(tixi, SU2_TARGET_CL_XPATH)
+ create_branch(tixi, SU2_FIXED_CL_XPATH)
+ tixi.updateDoubleElement(SU2_TARGET_CL_XPATH, target_cl, "%g")
+ tixi.updateTextElement(SU2_FIXED_CL_XPATH, "YES")
+ log.info("Target CL has been saved in the CPACS file")
+
+
+def retrieve_gui(tixi: Tixi3):
+ """
+ Retrieve values from CPACS file.
+ """
+ # XPath definition
+ # TODO: Put these xpaths in commonxpaths
+ ref_area_xpath = REF_XPATH + "/area"
+ mass_type_xpath = CLCALC_XPATH + "/massType"
+ cruise_alt_xpath = CLCALC_XPATH + "/cruiseAltitude"
+ cruise_mach_xpath = CLCALC_XPATH + "/cruiseMach"
+ load_fact_xpath = CLCALC_XPATH + "/loadFactor"
+
+ ref_area = get_value(tixi, ref_area_xpath)
+ mass_type = get_value(tixi, mass_type_xpath)
+
+ cruise_alt = get_value(tixi, cruise_alt_xpath)
+ cruise_mach = get_value(tixi, cruise_mach_xpath)
+ load_fact = get_value(tixi, load_fact_xpath)
+
+ return ref_area, mass_type, cruise_alt, cruise_mach, load_fact
+
+
+def deal_with_mass(md: MarkdownDoc, tixi: Tixi3, mass_type: str) -> float:
+ mass = None
+ md.p(f"The mass used for the calculation is {mass_type}")
+
+ percent_fuel_mass_xpath = CLCALC_XPATH + "/percentFuelMass"
+ custom_mass_xpath = CLCALC_XPATH + "/customMass"
+
+ if mass_type == "Custom":
+ mass = get_value(tixi, custom_mass_xpath)
+
+ elif mass_type == "% fuel mass":
+ percent_fuel_mass = get_value(tixi, percent_fuel_mass_xpath)
+ md.p(f"Percentage of fuel mass: {percent_fuel_mass}%")
+ mtom = get_value(tixi, MASSBREAKDOWN_XPATH + "/designMasses/mTOM/mass")
+ mzfm = get_value(tixi, MASSBREAKDOWN_XPATH + "/designMasses/mZFM/mass")
+ if mzfm > mtom:
+ raise ValueError("mZFM is bigger than mTOM!")
+ mass = (mtom - mzfm) * percent_fuel_mass / 100 + mzfm
+
+ else:
+ mass_xpath = MASSBREAKDOWN_XPATH + f"/designMasses/{mass_type}/mass"
+ mass = get_value(tixi, mass_xpath)
+
+ if mass:
+ log.info(f"Aircraft mass use for this analysis is {mass} [kg]")
+ else:
+ raise ValueError("The chosen aircraft mass has not been found!")
+
+ return mass
+
+
+# ==============================================================================
+# MAIN
+# ==============================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/CLCalculator/tests/test_clcalculator.py b/ceasiompy/CLCalculator/tests/test_clcalculator.py
index e723a6ae1..afeea4177 100644
--- a/ceasiompy/CLCalculator/tests/test_clcalculator.py
+++ b/ceasiompy/CLCalculator/tests/test_clcalculator.py
@@ -5,25 +5,30 @@
Test functions for 'lib/CLCalculator/clcalculator.py'
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2019-07-24
+| Author: Leon Deligny
+| Creation: 25 March 2025
+
"""
# =================================================================================================
# IMPORTS
# =================================================================================================
+from ceasiompy.CLCalculator.clcalculator import calculate_cl
from pathlib import Path
-
-from ceasiompy.CLCalculator.clcalculator import calculate_cl, get_cl
-from ceasiompy.utils.commonxpath import SU2_TARGET_CL_XPATH
-from cpacspy.cpacsfunctions import open_tixi
from pytest import approx
-MODULE_DIR = Path(__file__).parent
+from ceasiompy.utils.decorators import log_test
+
+from unittest import main
+from ceasiompy.utils.ceasiompytest import CeasiompyTest
+
+from ceasiompy.CLCalculator import MODULE_DIR
+
CPACS_IN_PATH = Path(MODULE_DIR, "D150_simple.xml")
CPACS_OUT_PATH = Path(MODULE_DIR, "D150_simple_clcalulator_test.xml")
@@ -32,6 +37,16 @@
# =================================================================================================
+class TestModuleTemplate(CeasiompyTest):
+
+ @log_test
+ def test_calculate_cl(self):
+ self.assert_equal_function(
+ f=calculate_cl,
+ input_args=(122, 12_000, 0.78, 50_000, 1.0, ),
+ expected=(approx(0.48429196151547343), ),
+ )
+
# =================================================================================================
# FUNCTIONS
# =================================================================================================
@@ -50,27 +65,10 @@ def test_calculate_cl():
assert cl == approx(0.48429196151547343)
-
-def test_get_cl():
- """Test function 'get_cl'"""
-
- get_cl(CPACS_IN_PATH, CPACS_OUT_PATH)
-
- tixi = open_tixi(CPACS_OUT_PATH)
-
- cl_to_check = tixi.getDoubleElement(SU2_TARGET_CL_XPATH)
- assert cl_to_check == approx(0.791955)
-
- if CPACS_OUT_PATH.exists():
- CPACS_OUT_PATH.unlink()
-
-
# =================================================================================================
# MAIN
# =================================================================================================
-if __name__ == "__main__":
- print("Running Test CL Calulator")
- print("To run test use the following command:")
- print(">> pytest -v")
+if __name__ == "__main__":
+ main(verbosity=0)
diff --git a/ceasiompy/CPACS2GMSH/README.md b/ceasiompy/CPACS2GMSH/README.md
index 2480decb3..f440e602d 100644
--- a/ceasiompy/CPACS2GMSH/README.md
+++ b/ceasiompy/CPACS2GMSH/README.md
@@ -10,9 +10,9 @@
-`CPACS2GMSH` is an automatic mesh generator module for a [CPACS](https://www.cpacs.de) aircraft geometry [[1]](#Alder20) using [GMSH](https://gmsh.info/) ,a finite element mesh generator.
+`CPACS2GMSH` is an automatic mesh generator module for a [CPACS](https://www.cpacs.de) aircraft geometry [[1]](#Alder20) using [GMSH](https://gmsh.info/) ,a finite element mesh generator.
-It's currently possible to choose between two options for 3D meshing of the external domain.
+It's currently possible to choose between two options for 3D meshing of the external domain.
Selecting the 'Euler' an unstructured mesh is automatically generated in a spherical domain surrounding the aircraft.
Instead, selecting the 'RANS' option Gmsh will generate only the 2D mesh of the entire aircraft, which will then be processed by the programme [Pentagrow] to generate the structured part that wraps the geometry, then [Tetgen](https://wias-berlin.de/software/tetgen/1.5/doc/manual/manual.pdf) package provides for meshing of the unstructured part. The hybrid mesh obtained will constitute the 3D domain.
@@ -77,15 +77,15 @@ Euler options:
else:
RANS options:
-*`Number of layer: 20`
+*`Number of layer: 20`
Number of prismatic element layers
-*`height of first layer: 3 e-5 mm`
+*`height of first layer: 3 e-5 mm`
Height of the first prismatic cell, touching the wall, in mesh length units.
-*`Max layer thickness: 10 cm`
+*`Max layer thickness: 10 cm`
The maximum allowed absolute thickness of the prismatic layer.
*`Growth factor: 1.2`
Growth factor between edge lengths of coincident tetrahedra
-*`Feature angle: 80 grad`
+*`Feature angle: 80 grad`
Whenever the dihedral angle of two triangle is smaller than this limit, the resulting edge is understood to represent an actual geometrical feature. Larger angles are treated as resulting from approximation of curved surfaces by linear triangles
*`Surface mesh size: 5 `
Surface mesh size factor compared to the aircraft largest dimension, omogeneus everywhere
@@ -111,8 +111,7 @@ Engine exhaust surface position from the back of the engine fan cowl in percent
## Analyses
-`CPACS2GMSH` Generate .brep files with TiGL for each part of the aircraft configuration. Then all the parts are imported into GMSH to generates a SU2 mesh file
-
+`CPACS2GMSH` Generate .brep files with TiGL for each part of the aircraft configuration. Then all the parts are imported into GMSH to generates a SU2 mesh file
for the euler case, instead a .stl file is generated to be read by pentagrow
## Outputs
diff --git a/ceasiompy/CPACS2GMSH/__init__.py b/ceasiompy/CPACS2GMSH/__init__.py
index e69de29bb..c006975b9 100644
--- a/ceasiompy/CPACS2GMSH/__init__.py
+++ b/ceasiompy/CPACS2GMSH/__init__.py
@@ -0,0 +1,48 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for CPACS2Gmsh module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+from ceasiompy import log
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = True
+
+# ===== Include GUI =====
+include_gui = True
+
+# ===== Add a Results Directory =====
+RES_DIR = True
+
+# ===== Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+# Specific to CPACS2Gmsh module
+CONTROL_SURFACES_LIST = ["aileron", "rudder", "flap"]
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/CPACS2GMSH/__specs__.py b/ceasiompy/CPACS2GMSH/__specs__.py
index 6bd2ceeb6..0b991bcc6 100644
--- a/ceasiompy/CPACS2GMSH/__specs__.py
+++ b/ceasiompy/CPACS2GMSH/__specs__.py
@@ -1,60 +1,71 @@
-from pathlib import Path
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+GUI Interface of CPACS2GMSH.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from ceasiompy.utils.moduleinterfaces import CPACSInOut
+
+from ceasiompy import log
+from ceasiompy.CPACS2GMSH import include_gui
from ceasiompy.utils.commonxpath import (
- GMSH_AUTO_REFINE_XPATH,
- GMSH_EXHAUST_PERCENT_XPATH,
- GMSH_EXPORT_PROP_XPATH,
+ GMSH_OPEN_GUI_XPATH,
+ GMSH_MESH_TYPE_XPATH,
+ GMSH_CTRLSURF_ANGLE_XPATH,
+ GMSH_SYMMETRY_XPATH,
GMSH_FARFIELD_FACTOR_XPATH,
- GMSH_N_POWER_FACTOR_XPATH,
- GMSH_N_POWER_FIELD_XPATH,
- GMSH_INTAKE_PERCENT_XPATH,
GMSH_MESH_SIZE_FARFIELD_XPATH,
- GMSH_MESH_SIZE_FUSELAGE_XPATH,
- GMSH_MESH_SIZE_WINGS_XPATH,
+ GMSH_MESH_SIZE_FACTOR_FUSELAGE_XPATH,
+ GMSH_MESH_SIZE_FACTOR_WINGS_XPATH,
GMSH_MESH_SIZE_ENGINES_XPATH,
GMSH_MESH_SIZE_PROPELLERS_XPATH,
- GMSH_OPEN_GUI_XPATH,
- GMSH_REFINE_TRUNCATED_XPATH,
+ GMSH_N_POWER_FACTOR_XPATH,
+ GMSH_N_POWER_FIELD_XPATH,
GMSH_REFINE_FACTOR_XPATH,
- GMSH_SYMMETRY_XPATH,
- SU2MESH_XPATH,
- GMSH_MESH_SIZE_FACTOR_FUSELAGE_XPATH,
- GMSH_MESH_SIZE_FACTOR_WINGS_XPATH,
- GMSH_MESH_TYPE_XPATH,
+ GMSH_REFINE_TRUNCATED_XPATH,
+ GMSH_AUTO_REFINE_XPATH,
GMSH_NUMBER_LAYER_XPATH,
GMSH_H_FIRST_LAYER_XPATH,
GMSH_MAX_THICKNESS_LAYER_XPATH,
- GMSH_GROWTH_FACTOR_XPATH,
GMSH_GROWTH_RATIO_XPATH,
- GMSH_SURFACE_MESH_SIZE_XPATH,
+ GMSH_GROWTH_FACTOR_XPATH,
GMSH_FEATURE_ANGLE_XPATH,
+ GMSH_SURFACE_MESH_SIZE_XPATH,
+ GMSH_EXPORT_PROP_XPATH,
+ GMSH_INTAKE_PERCENT_XPATH,
+ GMSH_EXHAUST_PERCENT_XPATH,
+ SU2MESH_XPATH,
)
-from ceasiompy.utils.moduleinterfaces import CPACSInOut
-
-# ===== Module Status =====
-# True if the module is active
-# False if the module is disabled (not working or not ready)
-module_status = True
-
-# ===== Results directory path =====
-
-RESULTS_DIR = Path("Results", "GMSH")
-
-
-# ===== CPACS inputs and outputs =====
+# ==============================================================================
+# VARIABLE
+# ==============================================================================
cpacs_inout = CPACSInOut()
-# ----- Input -----
+# ==============================================================================
+# GUI INPUTS
+# ==============================================================================
cpacs_inout.add_input(
var_name="open_gmsh",
var_type=bool,
default_value=False,
- unit="1",
+ unit=None,
descr="Open GMSH GUI when the mesh is created",
xpath=GMSH_OPEN_GUI_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Open GMSH GUI",
gui_group="General options",
)
@@ -63,22 +74,34 @@
var_name="type_mesh",
var_type=list,
default_value=["Euler", "RANS"],
- unit="1",
- descr="Chose between Euler and RANS mesh",
+ unit=None,
+ descr="Choose between Euler and RANS mesh",
xpath=GMSH_MESH_TYPE_XPATH,
- gui=True,
- gui_name="Chose the mesh type",
+ gui=include_gui,
+ gui_name="Choose the mesh type",
gui_group="Mesh type",
)
+cpacs_inout.add_input(
+ var_name="aileron",
+ var_type="multiselect",
+ default_value=[0.0],
+ unit="[deg]",
+ descr="List of Aileron, Elevator, Rudder angles",
+ xpath=GMSH_CTRLSURF_ANGLE_XPATH,
+ gui=include_gui,
+ gui_name="Aileron/Elevator/Rudder Angles",
+ gui_group="Control surface settings",
+)
+
cpacs_inout.add_input(
var_name="symmetry",
var_type=bool,
default_value=False,
- unit="1",
+ unit=None,
descr="Create a symmetry condition",
xpath=GMSH_SYMMETRY_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Use Symmetry",
gui_group="Domain",
)
@@ -87,10 +110,10 @@
var_name="farfield_factor",
var_type=float,
default_value=10,
- unit="[-]",
+ unit=None,
descr="Farfield size factor compare to the aircraft largest dimension",
xpath=GMSH_FARFIELD_FACTOR_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Farfield size factor",
gui_group="Domain",
)
@@ -99,11 +122,11 @@
var_name="farfield_mesh_size_factor",
var_type=float,
default_value=10,
- unit="[-]",
+ unit=None,
descr="""Factor proportional to the biggest cell on the plane
to obtain cell size on the farfield(just for Euler)""",
xpath=GMSH_MESH_SIZE_FARFIELD_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Farfield mesh size factor",
gui_group="Euler options",
)
@@ -111,11 +134,11 @@
cpacs_inout.add_input(
var_name="fuselage_mesh_size_factor",
var_type=float,
- default_value=1,
- unit="[-]",
+ default_value=1.0,
+ unit=None,
descr="Factor proportional to fuselage radius of curvature to obtain cell size on it",
xpath=GMSH_MESH_SIZE_FACTOR_FUSELAGE_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Fuselage mesh size factor",
gui_group="Euler options",
)
@@ -123,11 +146,11 @@
cpacs_inout.add_input(
var_name="wing_mesh_size_factor",
var_type=float,
- default_value=1.5,
- unit="[-]",
+ default_value=1.0,
+ unit=None,
descr="Factor proportional to wing radius of curvature to obtain cell size on it",
xpath=GMSH_MESH_SIZE_FACTOR_WINGS_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Wings mesh size factor",
gui_group="Euler options",
)
@@ -139,7 +162,7 @@
unit="[m]",
descr="Value assigned for the engine surfaces mesh size",
xpath=GMSH_MESH_SIZE_ENGINES_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Engines",
gui_group="Euler options",
)
@@ -151,7 +174,7 @@
unit="[m]",
descr="Value assigned for the propeller surfaces mesh size",
xpath=GMSH_MESH_SIZE_PROPELLERS_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Propellers",
gui_group="Euler options",
)
@@ -160,10 +183,10 @@
var_name="n_power_factor",
var_type=float,
default_value=2,
- unit="1",
+ unit=None,
descr="Value that changes the number of cells near the aircraft parts",
xpath=GMSH_N_POWER_FACTOR_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="n power factor",
gui_group="Advanced Euler mesh parameters",
)
@@ -172,10 +195,10 @@
var_name="n_power_field",
var_type=float,
default_value=0.9,
- unit="1",
+ unit=None,
descr="Value that changes the measure of fist cells near aircraft parts",
xpath=GMSH_N_POWER_FIELD_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="n power field",
gui_group="Advanced Euler mesh parameters",
)
@@ -184,10 +207,10 @@
var_name="refine_factor",
var_type=float,
default_value=2.0,
- unit="1",
+ unit=None,
descr="Refinement factor of wing leading/trailing edge mesh",
xpath=GMSH_REFINE_FACTOR_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="LE/TE refinement factor",
gui_group="Advanced Euler mesh parameters",
)
@@ -196,10 +219,10 @@
var_name="refine_truncated",
var_type=bool,
default_value=False,
- unit="1",
+ unit=None,
descr="Enable the refinement of truncated trailing edge",
xpath=GMSH_REFINE_TRUNCATED_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Refine truncated TE",
gui_group="Advanced Euler mesh parameters",
)
@@ -208,11 +231,11 @@
var_name="auto_refine",
var_type=bool,
default_value=False,
- unit="1",
+ unit=None,
descr="Automatically refine the mesh on surfaces that are small compare to the chosen mesh"
"size, this option increase the mesh generation time",
xpath=GMSH_AUTO_REFINE_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Auto refine",
gui_group="Advanced Euler mesh parameters",
)
@@ -221,10 +244,10 @@
var_name="n_layer",
var_type=int,
default_value=20,
- unit="[-]",
+ unit=None,
descr="Number of prismatic element layers.",
xpath=GMSH_NUMBER_LAYER_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Number of layer",
gui_group="RANS options",
)
@@ -236,7 +259,7 @@
unit="[\u03BCm]",
descr="is the height of the first prismatic cell, touching the wall, in mesh length units.",
xpath=GMSH_H_FIRST_LAYER_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Height of first layer",
gui_group="RANS options",
)
@@ -248,7 +271,7 @@
unit="[mm]",
descr="The maximum allowed absolute thickness of the prismatic layer.",
xpath=GMSH_MAX_THICKNESS_LAYER_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Max layer thickness",
gui_group="RANS options",
)
@@ -257,10 +280,10 @@
var_name="growth_ratio",
var_type=float,
default_value=1.2,
- unit="[-]",
+ unit=None,
descr="the largest allowed ratio between the wall-normal edge lengths of consecutive cells",
xpath=GMSH_GROWTH_RATIO_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Growth ratio",
gui_group="RANS options",
)
@@ -269,10 +292,10 @@
var_name="growth_factor",
var_type=float,
default_value=1.4,
- unit="[-]",
+ unit=None,
descr="Desired growth factor between edge lengths of coincident tetrahedra",
xpath=GMSH_GROWTH_FACTOR_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Growth factor",
gui_group="RANS options",
)
@@ -284,7 +307,7 @@
unit="[grad]",
descr="Larger angles are treated as resulting from approximation of curved surfaces",
xpath=GMSH_FEATURE_ANGLE_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Feature Angle",
gui_group="RANS options",
)
@@ -296,7 +319,7 @@
unit="[10^-3]",
descr="Surface mesh size factor compared to aircraft largest dimension (omogeneus everywhere)",
xpath=GMSH_SURFACE_MESH_SIZE_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Surface mesh size",
gui_group="RANS options",
)
@@ -305,10 +328,10 @@
var_name="export_propellers",
var_type=bool,
default_value=False,
- unit="1",
+ unit=None,
descr="Export propeller(s) to be use as disk actuator",
xpath=GMSH_EXPORT_PROP_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Export propeller(s)",
gui_group="General options",
)
@@ -321,7 +344,7 @@
descr="Position of the intake surface boundary condition in percentage of"
" the engine length from the beginning of the engine",
xpath=GMSH_INTAKE_PERCENT_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Engine intake position",
gui_group="Engines",
)
@@ -333,19 +356,27 @@
descr="Position of the exhaust surface boundary condition in percentage of"
" the engine length from the end of the engine",
xpath=GMSH_EXHAUST_PERCENT_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Engine exhaust position",
gui_group="Engines",
)
-
-# ----- Output -----
+# ==============================================================================
+# GUI OUTPUTS
+# ==============================================================================
cpacs_inout.add_output(
var_name="su2_mesh_path",
var_type="pathtype",
default_value=None,
- unit="1",
+ unit=None,
descr="Absolute path of the SU2 mesh",
xpath=SU2MESH_XPATH,
)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/CPACS2GMSH/cpacs2gmsh.py b/ceasiompy/CPACS2GMSH/cpacs2gmsh.py
index 92bf67828..6250da1e7 100644
--- a/ceasiompy/CPACS2GMSH/cpacs2gmsh.py
+++ b/ceasiompy/CPACS2GMSH/cpacs2gmsh.py
@@ -5,14 +5,13 @@
Small description of the script
-Python version: >=3.8
| Author:Tony Govoni
| Creation: 2022-03-22
| Modified by: Giacomo Benedetti, Guido Vallifuoco
-| Last modification: 2024-02-01
-
-TODO:
+| Date: 2024-02-01
+| Modified by: Leon Deligny
+| Date: 06 March 2025
"""
@@ -20,112 +19,87 @@
# IMPORTS
# =================================================================================================
-from pathlib import Path
-
+from ceasiompy.utils.ceasiompyutils import call_main
from ceasiompy.CPACS2GMSH.func.exportbrep import export_brep
+from ceasiompy.utils.geometryfunctions import return_uidwings
from ceasiompy.CPACS2GMSH.func.generategmesh import generate_gmsh
-from ceasiompy.CPACS2GMSH.func.RANS_mesh_generator import (
- generate_2d_mesh_for_pentagrow,
- pentagrow_3d_mesh,
+from ceasiompy.CPACSUpdater.func.controlsurfaces import deflection_angle
+
+from cpacspy.cpacsfunctions import (
+ get_value,
+ create_branch,
)
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.ceasiompyutils import get_results_directory
-from ceasiompy.utils.moduleinterfaces import (
- check_cpacs_input_requirements,
- get_toolinput_file_path,
- get_tooloutput_file_path,
+from ceasiompy.CPACS2GMSH.func.utils import (
+ retrieve_gui_values,
+ load_rans_cgf_params,
)
-from ceasiompy.utils.commonxpath import (
- GMSH_AUTO_REFINE_XPATH,
- GMSH_EXHAUST_PERCENT_XPATH,
- GMSH_FARFIELD_FACTOR_XPATH,
- GMSH_N_POWER_FACTOR_XPATH,
- GMSH_N_POWER_FIELD_XPATH,
- GMSH_INTAKE_PERCENT_XPATH,
- GMSH_MESH_SIZE_FARFIELD_XPATH,
- GMSH_MESH_SIZE_FACTOR_FUSELAGE_XPATH,
- GMSH_MESH_SIZE_FACTOR_WINGS_XPATH,
- GMSH_MESH_SIZE_ENGINES_XPATH,
- GMSH_MESH_SIZE_PROPELLERS_XPATH,
- GMSH_OPEN_GUI_XPATH,
- GMSH_REFINE_FACTOR_XPATH,
- GMSH_REFINE_TRUNCATED_XPATH,
- GMSH_SYMMETRY_XPATH,
- SU2MESH_XPATH,
- GMSH_MESH_TYPE_XPATH,
- GMSH_NUMBER_LAYER_XPATH,
- GMSH_H_FIRST_LAYER_XPATH,
- GMSH_MAX_THICKNESS_LAYER_XPATH,
- GMSH_GROWTH_FACTOR_XPATH,
- GMSH_GROWTH_RATIO_XPATH,
- GMSH_SURFACE_MESH_SIZE_XPATH,
- GMSH_FEATURE_ANGLE_XPATH,
+from ceasiompy.CPACS2GMSH.func.rans_mesh_generator import (
+ pentagrow_3d_mesh,
+ generate_2d_mesh_for_pentagrow,
)
-from cpacspy.cpacsfunctions import create_branch, get_value_or_default
+
+from typing import List
+from pathlib import Path
from cpacspy.cpacspy import CPACS
-log = get_logger()
+from ceasiompy import log
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
+from ceasiompy.utils.commonxpath import (
+ SU2MESH_XPATH,
+ GMSH_CTRLSURF_ANGLE_XPATH,
+)
+from ceasiompy.CPACS2GMSH import (
+ MODULE_NAME,
+ CONTROL_SURFACES_LIST,
+)
# =================================================================================================
-# CLASSES
+# FUNCTIONS
# =================================================================================================
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
+def run_cpacs2gmsh(cpacs: CPACS, wkdir: Path, surf: str = None, angle: str = None) -> None:
+ """
+ Starts meshing with gmsh.
+
+ Args:
+ cpacs (CPACS): CPACS file.
+ surf (str = None): Deflected control surface.
+ angle (str = None): Angle of deflection.
+ """
+ tixi = cpacs.tixi
+
+ # Create corresponding brep directory.
+ if surf is None:
+ brep_dir = Path(wkdir, "brep_files")
+ else:
+ brep_dir = Path(wkdir, f"brep_files_{surf}_{angle}")
+
+ # Retrieve GUI values
+ (
+ open_gmsh, type_mesh, symmetry,
+ farfield_factor, farfield_size_factor,
+ n_power_factor, n_power_field,
+ fuselage_mesh_size_factor, wing_mesh_size_factor,
+ mesh_size_engines, mesh_size_propellers,
+ refine_factor, refine_truncated, auto_refine,
+ intake_percent, exhaust_percent,
+ n_layer, h_first_layer, max_layer_thickness,
+ growth_factor, growth_ratio,
+ min_max_mesh_factor, feature_angle,
+
+ ) = retrieve_gui_values(tixi)
+
+ # Export airplane's part in .brep format
+ export_brep(cpacs, brep_dir, (intake_percent, exhaust_percent))
-def cpacs2gmsh(cpacs_path, cpacs_out_path):
- # Get option from the CPACS file
- cpacs = CPACS(cpacs_path)
-
- # Create results directory
- results_dir = get_results_directory("CPACS2GMSH")
- brep_dir = Path(results_dir, "brep_files")
- brep_dir.mkdir()
-
- # Retrieve value from the GUI Setting
- open_gmsh = get_value_or_default(cpacs.tixi, GMSH_OPEN_GUI_XPATH, False)
- type_mesh = get_value_or_default(cpacs.tixi, GMSH_MESH_TYPE_XPATH, "Euler")
- farfield_factor = get_value_or_default(cpacs.tixi, GMSH_FARFIELD_FACTOR_XPATH, 10)
- symmetry = get_value_or_default(cpacs.tixi, GMSH_SYMMETRY_XPATH, False)
- farfield_size_factor = get_value_or_default(cpacs.tixi, GMSH_MESH_SIZE_FARFIELD_XPATH, 17)
- n_power_factor = get_value_or_default(cpacs.tixi, GMSH_N_POWER_FACTOR_XPATH, 2)
- n_power_field = get_value_or_default(cpacs.tixi, GMSH_N_POWER_FIELD_XPATH, 0.9)
- fuselage_mesh_size_factor = get_value_or_default(
- cpacs.tixi,
- GMSH_MESH_SIZE_FACTOR_FUSELAGE_XPATH,
- 1,
- )
- wing_mesh_size_factor = get_value_or_default(cpacs.tixi, GMSH_MESH_SIZE_FACTOR_WINGS_XPATH, 1)
- mesh_size_engines = get_value_or_default(cpacs.tixi, GMSH_MESH_SIZE_ENGINES_XPATH, 0.23)
- mesh_size_propellers = get_value_or_default(cpacs.tixi, GMSH_MESH_SIZE_PROPELLERS_XPATH, 0.23)
- refine_factor = get_value_or_default(cpacs.tixi, GMSH_REFINE_FACTOR_XPATH, 7.0)
- refine_truncated = get_value_or_default(cpacs.tixi, GMSH_REFINE_TRUNCATED_XPATH, False)
- auto_refine = get_value_or_default(cpacs.tixi, GMSH_AUTO_REFINE_XPATH, True)
- intake_percent = get_value_or_default(cpacs.tixi, GMSH_INTAKE_PERCENT_XPATH, 20)
- exhaust_percent = get_value_or_default(cpacs.tixi, GMSH_EXHAUST_PERCENT_XPATH, 20)
- n_layer = get_value_or_default(cpacs.tixi, GMSH_NUMBER_LAYER_XPATH, 20)
- h_first_layer = get_value_or_default(cpacs.tixi, GMSH_H_FIRST_LAYER_XPATH, 3)
- max_layer_thickness = get_value_or_default(cpacs.tixi, GMSH_MAX_THICKNESS_LAYER_XPATH, 10)
- growth_factor = get_value_or_default(cpacs.tixi, GMSH_GROWTH_FACTOR_XPATH, 1.4)
- growth_ratio = get_value_or_default(cpacs.tixi, GMSH_GROWTH_RATIO_XPATH, 1.2)
- min_max_mesh_factor = get_value_or_default(cpacs.tixi, GMSH_SURFACE_MESH_SIZE_XPATH, 5)
- feature_angle = get_value_or_default(cpacs.tixi, GMSH_FEATURE_ANGLE_XPATH, 40)
-
- # Run mesh generation
if type_mesh == "Euler":
- export_brep(cpacs, brep_dir, (intake_percent, exhaust_percent))
- mesh_path, _ = generate_gmsh(
- cpacs,
- cpacs_path,
+ su2mesh_path = generate_gmsh(
+ tixi,
brep_dir,
- results_dir,
+ wkdir,
open_gmsh=open_gmsh,
farfield_factor=farfield_factor,
symmetry=symmetry,
@@ -140,28 +114,23 @@ def cpacs2gmsh(cpacs_path, cpacs_out_path):
refine_truncated=refine_truncated,
auto_refine=auto_refine,
testing_gmsh=False,
+ surf=surf,
+ angle=angle,
)
- if mesh_path.exists():
- create_branch(cpacs.tixi, SU2MESH_XPATH)
- cpacs.tixi.updateTextElement(SU2MESH_XPATH, str(mesh_path))
- log.info("SU2 Mesh has been correctly generated.")
-
else:
- export_brep(cpacs, brep_dir, (intake_percent, exhaust_percent))
gmesh_path, fuselage_maxlen = generate_2d_mesh_for_pentagrow(
- cpacs,
- cpacs_path,
+ tixi,
brep_dir,
- results_dir,
+ wkdir,
open_gmsh=open_gmsh,
min_max_mesh_factor=min_max_mesh_factor,
)
if gmesh_path.exists():
- log.info("Mesh file exists. Proceeding to 3D mesh generation")
- mesh_path = pentagrow_3d_mesh(
- results_dir,
- fuselage_maxlen,
+ log.info("Mesh file exists. Proceeding to 3D mesh generation.")
+
+ rans_cfg_params = load_rans_cgf_params(
+ fuselage_maxlen=fuselage_maxlen,
farfield_factor=farfield_factor,
n_layer=n_layer,
h_first_layer=h_first_layer,
@@ -170,34 +139,120 @@ def cpacs2gmsh(cpacs_path, cpacs_out_path):
growth_ratio=growth_ratio,
feature_angle=feature_angle,
)
- if mesh_path.exists():
- create_branch(cpacs.tixi, SU2MESH_XPATH)
- cpacs.tixi.updateTextElement(SU2MESH_XPATH, str(mesh_path))
- log.info("SU2 Mesh has been correctly generated.")
+
+ pentagrow_3d_mesh(
+ wkdir,
+ cfg_params=rans_cfg_params,
+ surf=surf,
+ angle=angle,
+ )
else:
- log.error("Error in generating SU2 mesh")
+ log.error("Error in generating SU2 mesh.")
+
+ # Update SU2 mesh xPath
+ if su2mesh_path.exists():
+ mesh_path = str(su2mesh_path)
+ if tixi.checkElement(SU2MESH_XPATH):
+ meshes = tixi.getTextElement(SU2MESH_XPATH)
+ if meshes != "":
+ mesh_path = meshes + ";" + mesh_path
+ else:
+ # Create the branch if it does not exist.
+ create_branch(tixi, SU2MESH_XPATH)
- # Save CPACS
- cpacs.save_cpacs(cpacs_out_path, overwrite=True)
+ tixi.updateTextElement(SU2MESH_XPATH, mesh_path)
+ log.info(f"SU2 Mesh at {mesh_path} has been correctly generated. \n")
+ else:
+ log.warning(f"Mesh path {mesh_path} does not exist. \n")
-# =================================================================================================
-# MAIN
-# =================================================================================================
+def deform_surf(cpacs: CPACS, wkdir: Path, surf: str, angle: float, wing_names: List) -> None:
+ """
+ Deform the surface surf by angle angle,
+ and run run_cpacs2gmsh with this modified CPACS.
-def main(cpacs_path, cpacs_out_path):
- log.info("----- Start of " + MODULE_NAME + " -----")
+ Args:
+ cpacs (CPACS): CPACS file to modify.
+ surf (str): Specific control surface.
+ angle (float): Deflection angle.
+ wing_names (List): Wings of aircraft.
- check_cpacs_input_requirements(cpacs_path)
+ """
+ cpacs_in = Path(cpacs.cpacs_file)
+ tmp_cpacs = CPACS(cpacs_in)
- cpacs2gmsh(cpacs_path, cpacs_out_path)
+ tixi = tmp_cpacs.tixi
- log.info("----- End of " + MODULE_NAME + " -----")
+ filtered_wing_names = [wing for wing in wing_names if surf in wing]
+ # Deform the correct wing accordingly.
+ for wing in filtered_wing_names:
+ updated_angle = angle if "right_" in wing else -angle
+ deflection_angle(tixi, wing_uid=wing, angle=updated_angle)
+ log.info(
+ f"Deforming control surface {wing} "
+ f"of type {surf} by angle {updated_angle}."
+ )
-if __name__ == "__main__":
- cpacs_path = get_toolinput_file_path(MODULE_NAME)
- cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
+ # Upload the change in angles to the temporary CPACS
+ new_file_name = cpacs_in.stem + f"_surf{surf}_angle{angle}.xml"
+ new_file_path = cpacs_in.with_name(new_file_name)
+ tmp_cpacs.save_cpacs(new_file_path, overwrite=False)
+
+ # Upload saved temporary CPACS file
+ run_cpacs2gmsh(CPACS(new_file_path), wkdir, surf, str(angle))
+
+
+def main(cpacs: CPACS, wkdir: Path) -> None:
+ """
+ Main function.
+ Defines setup for gmsh.
+
+ Args:
+ cpacs_path (str): Input CPACS path.
+ cpacs_out_path (str): Modified output CPACS path.
+
+ """
+
+ tixi = cpacs.tixi
- main(cpacs_path, cpacs_out_path)
+ angles = get_value(tixi, GMSH_CTRLSURF_ANGLE_XPATH)
+
+ # Unique angles list
+ angles_list = list(set([float(x) for x in str(angles).split(';')]))
+
+ log.info(f"List of deflection angles {angles_list}.")
+
+ # Check if angles_list is empty
+ if angles_list:
+ for angle in reversed(angles_list):
+ if angle != 0.0:
+ wing_names = return_uidwings(tixi)
+
+ # Flap deformation has no utily in stability derivatives
+ for surf in CONTROL_SURFACES_LIST:
+ # Check if control surface exists through name of wings
+ if not any(surf in wing for wing in wing_names):
+ log.warning(
+ f"No control surface {surf}. "
+ f"It can not be deflected by angle {angle}."
+ )
+ else:
+ # If control Surface exists, deform the correct wings
+ deform_surf(cpacs, wkdir, surf, angle, wing_names)
+ else:
+ # No deformation for angle 0
+ run_cpacs2gmsh(cpacs, wkdir)
+
+ else:
+ # No specified angles: run as usual
+ run_cpacs2gmsh(cpacs, wkdir)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ call_main(main, MODULE_NAME)
diff --git a/ceasiompy/CPACS2GMSH/files/GMSH_D150_sym.png b/ceasiompy/CPACS2GMSH/files/GMSH_D150_sym.png
index 0de5363ec..7f1eb79cf 100644
Binary files a/ceasiompy/CPACS2GMSH/files/GMSH_D150_sym.png and b/ceasiompy/CPACS2GMSH/files/GMSH_D150_sym.png differ
diff --git a/ceasiompy/CPACS2GMSH/files/GMSH_propeller_aircraft.png b/ceasiompy/CPACS2GMSH/files/GMSH_propeller_aircraft.png
index 010cd3c37..7fd0fc21c 100644
Binary files a/ceasiompy/CPACS2GMSH/files/GMSH_propeller_aircraft.png and b/ceasiompy/CPACS2GMSH/files/GMSH_propeller_aircraft.png differ
diff --git a/ceasiompy/CPACS2GMSH/func/.gitignore b/ceasiompy/CPACS2GMSH/func/.gitignore
deleted file mode 100644
index 9a1a6a2da..000000000
--- a/ceasiompy/CPACS2GMSH/func/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-test_files/
diff --git a/ceasiompy/CPACS2GMSH/func/RANS_mesh_generator.py b/ceasiompy/CPACS2GMSH/func/RANS_mesh_generator.py
deleted file mode 100644
index 97b564866..000000000
--- a/ceasiompy/CPACS2GMSH/func/RANS_mesh_generator.py
+++ /dev/null
@@ -1,334 +0,0 @@
-"""
-CEASIOMpy: Conceptual Aircraft Design Software
-
-Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-
-Use .brep files parts of an airplane to generate a fused airplane in GMSH with
-the OCC kernel. Then Spherical farfield is created around the airplane and the
-resulting domain is meshed using gmsh
-
-Python version: >=3.8
-
-| Author: Guido Vallifuoco
-| Creation: 2024-02-01
-
-TODO:
-
- - It may be good to move all the function and some of the code in generategmsh()
- that are related to disk actuator to another python script and import it here
-
- - Add mesh sizing for each aircraft part and as consequence add marker
-
- - Integrate other parts during fragmentation
-
- - Use run software function instead subprocess.call
-
-"""
-
-
-# =================================================================================================
-# IMPORTS
-# =================================================================================================
-
-import os
-import subprocess
-from pathlib import Path
-
-import gmsh
-from ceasiompy.CPACS2GMSH.func.generategmesh import (
- # duplicate_disk_actuator_surfaces,
- # control_disk_actuator_normal,
- # get_entities_from_volume,
- ModelPart,
- add_disk_actuator,
- fuselage_size,
- process_gmsh_log,
-)
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-# from ceasiompy.utils.commonnames import (
-# ACTUATOR_DISK_OUTLET_SUFFIX,
-# ENGINE_EXHAUST_SUFFIX,
-# ENGINE_INTAKE_SUFFIX,
-# GMSH_ENGINE_CONFIG_NAME,
-# )
-from ceasiompy.utils.ceasiompyutils import get_part_type
-
-# from ceasiompy.utils.commonxpath import GMSH_MESH_SIZE_WINGS_XPATH
-from ceasiompy.utils.configfiles import ConfigFile
-
-log = get_logger()
-
-
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
-
-
-def generate_2d_mesh_for_pentagrow(
- cpacs, cpacs_path, brep_dir, results_dir, open_gmsh, min_max_mesh_factor, symmetry=False
-):
- """
- Function to generate a mesh from brep files forming an airplane
- Function 'generate_gmsh' is a subfunction of CPACS2GMSH which return a
- mesh file useful for pentagrow.
- The airplane is fused with the different brep files : fuselage, wings and
- other parts are identified and fused together in order to obtain a watertight volume.
- Args:
- ----------
- cpacs : CPACS
- CPACS object
- brep_dir : Path
- Path to the directory containing the brep files
- results_dir : Path
- Path to the directory containing the result (mesh) files
- open_gmsh : bool
- Open gmsh GUI after the mesh generation if set to true
- symmetry : bool
- If set to true, the mesh will be generated with symmetry wrt the x,z plane
- mesh_size_fuselage : float
- Size of the fuselage mesh
- mesh_size_wings : float
- Size of the wing mesh
- mesh_size_engines : float
- Size of the engine mesh
- mesh_size_propellers : float
- Size of the propeller mesh
- advance_mesh : bool
- If set to true, the mesh will be generated with advanced meshing options
- refine_factor : float
- refine factor for the mesh le and te edge
- refine_truncated : bool
- If set to true, the refinement can change to match the truncated te thickness
- auto_refine : bool
- If set to true, the mesh will be checked for quality
- testing_gmsh : bool
- If set to true, the gmsh sessions will not be clear and killed at the end of
- the function, this allow to test the gmsh feature after the call of generate_gmsh()
- ...
- Returns:
- ----------
- mesh_file : Path
- Path to the mesh file generated by gmsh
-
-
- """
- # Determine if rotor are present in the aircraft model
- rotor_model = False
- if Path(brep_dir, "config_rotors.cfg").exists():
- rotor_model = True
-
- if rotor_model:
- log.info("Adding disk actuator")
- config_file = ConfigFile(Path(brep_dir, "config_rotors.cfg"))
- add_disk_actuator(brep_dir, config_file)
-
- # Retrieve all brep
- brep_files = list(brep_dir.glob("*.brep"))
- brep_files.sort()
-
- # initialize gmsh
- gmsh.initialize()
- # Stop gmsh output log in the terminal
- gmsh.option.setNumber("General.Terminal", 0)
- # Log complexity
- gmsh.option.setNumber("General.Verbosity", 5)
-
- # Import each aircraft original parts / parent parts
- fuselage_volume_dimtags = []
- wings_volume_dimtags = []
- enginePylons_enginePylon_volume_dimtags = []
- engine_nacelle_fanCowl_volume_dimtags = []
- engine_nacelle_coreCowl_volume_dimtags = []
- vehicles_engines_engine_volume_dimtags = []
- vehicles_rotorcraft_model_rotors_rotor_volume_dimtags = []
-
- log.info(f"Importing files from {brep_dir}")
-
- for brep_file in brep_files:
- # Import the part and create the aircraft part object
- part_entities = gmsh.model.occ.importShapes(str(brep_file), highestDimOnly=False)
- gmsh.model.occ.synchronize()
-
- # Create the aircraft part object
- part_obj = ModelPart(uid=brep_file.stem)
- # maybe to cut off -->
- part_obj.part_type = get_part_type(cpacs.tixi, part_obj.uid)
-
- if part_obj.part_type == "fuselage":
- fuselage_volume_dimtags.append(part_entities[0])
- model_bb = gmsh.model.get_bounding_box(
- fuselage_volume_dimtags[0][0], fuselage_volume_dimtags[0][1]
- )
-
- elif part_obj.part_type == "wing":
- wings_volume_dimtags.append(part_entities[0])
- # return wings_volume_dimtags
-
- elif part_obj.part_type == "enginePylons/enginePylon":
- enginePylons_enginePylon_volume_dimtags.append(part_entities[0])
- # return enginePylons_enginePylon_volume_dimtags
-
- elif part_obj.part_type == "engine/nacelle/fanCowl":
- engine_nacelle_fanCowl_volume_dimtags.append(part_entities[0])
-
- elif part_obj.part_type == "engine/nacelle/coreCowl":
- engine_nacelle_coreCowl_volume_dimtags.append(part_entities[0])
-
- elif part_obj.part_type == "vehicles/engines/engine":
- vehicles_engines_engine_volume_dimtags.append(part_entities[0])
-
- elif part_obj.part_type == "vehicles/rotorcraft/model/rotors/rotor":
- vehicles_rotorcraft_model_rotors_rotor_volume_dimtags.append(part_entities[0])
- else:
- log.warning(f"'{brep_file}' cannot be categorized!")
- return None
- gmsh.model.occ.synchronize()
- log.info("Start manipulation to obtain a watertight volume")
- # we have to obtain a wathertight volume
- gmsh.model.occ.cut(wings_volume_dimtags, fuselage_volume_dimtags, -1, True, False)
-
- gmsh.model.occ.synchronize()
-
- gmsh.model.occ.fuse(wings_volume_dimtags, fuselage_volume_dimtags, -1, True, True)
-
- gmsh.model.occ.synchronize()
-
- model_dimensions = [
- abs(model_bb[0] - model_bb[3]),
- abs(model_bb[1] - model_bb[4]),
- abs(model_bb[2] - model_bb[5]),
- ]
-
- fuselage_maxlen, _ = fuselage_size(cpacs_path)
-
- gmsh.model.occ.translate(
- [(3, 1)],
- -((model_bb[0]) + (model_dimensions[0] / 2)),
- -((model_bb[1]) + (model_dimensions[1] / 2)),
- -((model_bb[2]) + (model_dimensions[2] / 2)),
- )
-
- gmsh.model.occ.synchronize()
- log.info("Manipulation finished")
-
- aircraft_surface_dimtags = gmsh.model.get_entities(2)
- len_aircraft_surface = len(aircraft_surface_dimtags)
- surface = []
-
- for i in range(len_aircraft_surface):
- tags = aircraft_surface_dimtags[i][1]
- surface.append(tags)
-
- gmsh.model.add_physical_group(2, surface, -1, name="aircraft_surface")
-
- # Mesh generation
- log.info("Start of gmsh 2D surface meshing process")
-
- gmsh.option.setNumber("Mesh.Algorithm", 6)
- gmsh.option.setNumber("Mesh.LcIntegrationPrecision", 1e-6)
- mesh_size = model_dimensions[0] * float(min_max_mesh_factor) * (10**-3)
- gmsh.option.set_number("Mesh.MeshSizeMin", mesh_size)
- gmsh.option.set_number("Mesh.MeshSizeMax", mesh_size)
- gmsh.option.setNumber("Mesh.StlOneSolidPerSurface", 2)
-
- gmsh.model.occ.synchronize()
- gmsh.logger.start()
- gmsh.model.mesh.generate(1)
- gmsh.model.mesh.generate(2)
- if open_gmsh:
- log.info("Result of 2D surface mesh")
- log.info("GMSH GUI is open, close it to continue...")
- gmsh.fltk.run()
-
- gmsh.model.occ.synchronize()
-
- gmesh_path = Path(results_dir, "mesh_2d.stl")
- gmsh.write(str(gmesh_path))
-
- process_gmsh_log(gmsh.logger.get())
-
- return gmesh_path, fuselage_maxlen
-
-
-def pentagrow_3d_mesh(
- result_dir,
- fuselage_maxlen,
- farfield_factor,
- n_layer,
- h_first_layer,
- max_layer_thickness,
- growth_factor,
- growth_ratio,
- feature_angle,
-) -> None:
- # create the config file for pentagrow
- config_penta_path = Path(result_dir, "config.cfg")
- # Variables
- InputFormat = "stl"
- NLayers = n_layer
- FeatureAngle = feature_angle
- InitialHeight = h_first_layer * (10**-5)
- MaxGrowthRatio = growth_ratio
- MaxLayerThickness = max_layer_thickness / 10
- FarfieldRadius = fuselage_maxlen * farfield_factor * 100
- FarfieldCenter = "0.0 0.0 0.0"
- OutputFormat = "su2"
- HolePosition = "0.0 0.0 0.0"
- TetgenOptions = "-pq1.3VY"
- TetGrowthFactor = growth_factor
- HeightIterations = 8
- NormalIterations = 8
- MaxCritIterations = 128
- LaplaceIterations = 8
-
- # writing to file
- file = open(config_penta_path, "w")
- file.write(f"InputFormat = {InputFormat}\n")
- file.write(f"NLayers = {NLayers}\n")
- file.write(f"FeatureAngle = {FeatureAngle}\n")
- file.write(f"InitialHeight = {InitialHeight}\n")
- file.write(f"MaxGrowthRatio = {MaxGrowthRatio}\n")
- file.write(f"MaxLayerThickness = {MaxLayerThickness}\n")
- file.write(f"FarfieldRadius = {FarfieldRadius}\n")
- file.write(f"OutputFormat = {OutputFormat}\n")
- file.write(f"HolePosition = {HolePosition}\n")
- file.write(f"FarfieldCenter = {FarfieldCenter}\n")
- file.write(f"TetgenOptions = {TetgenOptions}\n")
- file.write(f"TetGrowthFactor = {TetGrowthFactor}\n")
- file.write(f"HeightIterations = {HeightIterations}\n")
- file.write(f"NormalIterations = {NormalIterations}\n")
- file.write(f"MaxCritIterations = {MaxCritIterations}\n")
- file.write(f"LaplaceIterations = {LaplaceIterations}\n")
-
- os.chdir("Results/GMSH")
-
- if os.path.exists("mesh_2d.stl"):
- log.info("mesh_2d.stl exists")
- else:
- log.warning("mesh_2d.stl does not exist")
-
- if os.path.exists("config.cfg"):
- log.info("config.cfg exists")
- else:
- log.warning("config.cfg does not exist")
-
- current_dir = os.getcwd()
- os.chdir(current_dir)
-
- # command = "pentagrow mesh_2d.stl config.cfg"
- command = ["pentagrow", "mesh_2d.stl", "config.cfg"]
- # Specify the file path
- file_path = "command.txt"
-
- command_str = " ".join(command)
-
- with open(file_path, "w") as file:
- file.write(command_str)
-
- subprocess.call(command, cwd=current_dir, start_new_session=False)
-
- mesh_path = Path(result_dir, "hybrid.su2")
- log.info(f"Mesh path:{mesh_path}")
-
- return mesh_path
diff --git a/ceasiompy/CPACS2GMSH/func/advancemeshing.py b/ceasiompy/CPACS2GMSH/func/advancemeshing.py
index 2382af026..fe8a62355 100644
--- a/ceasiompy/CPACS2GMSH/func/advancemeshing.py
+++ b/ceasiompy/CPACS2GMSH/func/advancemeshing.py
@@ -5,13 +5,11 @@
This script contains different functions to classify and manipulate wing elements
-Python version: >=3.8
| Author: Tony Govoni
| Creation: 2022-04-07
TODO:
-
-Add a parameter to let the user tune the powerlaw for the wing surface mesh
"""
@@ -21,13 +19,13 @@
# IMPORTS
# =================================================================================================
-from ceasiompy.CPACS2GMSH.func.gmsh_utils import MESH_COLORS
import gmsh
import numpy as np
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
+from ceasiompy.CPACS2GMSH.func.wingclassification import ModelPart
+from ceasiompy.CPACS2GMSH.func.utils import MESH_COLORS
-log = get_logger()
# =================================================================================================
# FUNCTIONS
@@ -189,7 +187,7 @@ def refine_wing_section(
mesh_fields,
final_domain_volume_tag,
aircraft,
- wing_part,
+ wing_part: ModelPart,
mesh_size_wings,
refine,
refine_truncated,
@@ -511,10 +509,10 @@ def refine_small_surfaces(
return refined_surfaces, mesh_fields
-
# =================================================================================================
# MAIN
# =================================================================================================
+
if __name__ == "__main__":
- print("Nothing to execute!")
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/CPACS2GMSH/func/engineconversion.py b/ceasiompy/CPACS2GMSH/func/engineconversion.py
index ce711af67..5518154a2 100644
--- a/ceasiompy/CPACS2GMSH/func/engineconversion.py
+++ b/ceasiompy/CPACS2GMSH/func/engineconversion.py
@@ -5,7 +5,6 @@
This script contains different functions to convert engine
-Python version: >=3.8
| Author: Tony Govoni
| Creation: 2022-05-12
@@ -13,26 +12,24 @@
TODO:
- When TIGL new version is incorporated, check all the function of this script
and make the modification written in the function docstring
-"""
+"""
# ==============================================================================
# IMPORTS
# ==============================================================================
+
from pathlib import Path
from ceasiompy.utils.commonxpath import ENGINES_XPATH
import gmsh
import numpy as np
from ceasiompy.CPACS2SUMO.func.engineclasses import Engine
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from ceasiompy.utils.ceasiompyutils import get_part_type
from ceasiompy.utils.configfiles import ConfigFile
from scipy.spatial.transform import Rotation as R
-log = get_logger()
-
-# ==============================================================================
# FUNCTIONS
# ==============================================================================
@@ -167,11 +164,13 @@ def close_engine(nacelle_parts, engine_uids, brep_dir, engines_cfg_file, engine_
# Import all the parts
for brep_file in engine_files_path:
+ log.info(f"Importing brep file {brep_file}")
part_entities = gmsh.model.occ.importShapes(str(brep_file), highestDimOnly=False)
gmsh.model.occ.synchronize()
part_to_fuse.append(part_entities[0])
# Fuse them
+ log.info(f"Fusing brep file {part_to_fuse[:1]} with the rest")
gmsh.model.occ.fuse(part_to_fuse[:1], part_to_fuse[1:], removeObject=True, removeTool=True)
gmsh.model.occ.synchronize()
@@ -471,11 +470,10 @@ def reposition_engine(cpacs, engine_path, engine_uids, engines_cfg_file):
# Save this info in the engines config file
config_file.write_file(engines_cfg_file, overwrite=True)
-
# =================================================================================================
# MAIN
# =================================================================================================
-if __name__ == "__main__":
- print("Nothing to execute!")
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/CPACS2GMSH/func/exportbrep.py b/ceasiompy/CPACS2GMSH/func/exportbrep.py
index e6ca356a8..cb5253664 100644
--- a/ceasiompy/CPACS2GMSH/func/exportbrep.py
+++ b/ceasiompy/CPACS2GMSH/func/exportbrep.py
@@ -5,7 +5,6 @@
Small description of the script
-Python version: >=3.8
| Author: Tony Govoni
| Creation: 2022-03-22
@@ -24,7 +23,7 @@
from pathlib import Path
from ceasiompy.CPACS2GMSH.func.engineconversion import engine_conversion
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from ceasiompy.utils.commonnames import GMSH_ENGINE_CONFIG_NAME
from ceasiompy.utils.commonxpath import GMSH_EXPORT_PROP_XPATH
from ceasiompy.utils.configfiles import ConfigFile
@@ -32,7 +31,6 @@
from tigl3.import_export_helper import export_shapes
-log = get_logger()
# =================================================================================================
# FUNCTIONS
@@ -179,15 +177,12 @@ def export_brep(cpacs, brep_dir, engine_surface_percent=(20, 20)):
engine_surface_percent : tuple
Tuple containing the position percentage of the surface intake and exhaust bc
for the engine
-
- Returns
- -------
- None
-
"""
+ tixi = cpacs.tixi
+
# Get rotor config
- if get_value_or_default(cpacs.tixi, GMSH_EXPORT_PROP_XPATH, False):
+ if get_value_or_default(tixi, GMSH_EXPORT_PROP_XPATH, False):
try:
rotorcraft_config = cpacs.rotorcraft.configuration
rotor_config(rotorcraft_config, brep_dir)
@@ -256,11 +251,10 @@ def export_brep(cpacs, brep_dir, engine_surface_percent=(20, 20)):
engine = engines_config.get_engine(k)
engine_export(cpacs, engine, brep_dir, engines_cfg_file, engine_surface_percent)
-
# =================================================================================================
# MAIN
# =================================================================================================
-if __name__ == "__main__":
- print("Nothing to execute!")
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/CPACS2GMSH/func/generategmesh.py b/ceasiompy/CPACS2GMSH/func/generategmesh.py
index b6b32ce8c..1f234508d 100644
--- a/ceasiompy/CPACS2GMSH/func/generategmesh.py
+++ b/ceasiompy/CPACS2GMSH/func/generategmesh.py
@@ -7,7 +7,6 @@
the OCC kernel. Then Spherical farfield is created around the airplane and the
resulting domain is meshed using gmsh
-Python version: >=3.8
| Author: Tony Govoni
| Creation: 2022-03-22
@@ -26,234 +25,55 @@
"""
-
# =================================================================================================
# IMPORTS
# =================================================================================================
-from pathlib import Path
-from ceasiompy.CPACS2GMSH.func.gmsh_utils import MESH_COLORS
-from ceasiompy.utils.commonnames import (
- ACTUATOR_DISK_INLET_SUFFIX,
- ACTUATOR_DISK_OUTLET_SUFFIX,
- ENGINE_EXHAUST_SUFFIX,
- ENGINE_INTAKE_SUFFIX,
- GMSH_ENGINE_CONFIG_NAME,
-)
-from ceasiompy.utils.commonxpath import GMSH_MESH_SIZE_FUSELAGE_XPATH, GMSH_MESH_SIZE_WINGS_XPATH
-from ceasiompy.utils.configfiles import ConfigFile
+import re
import gmsh
+
import numpy as np
+from ceasiompy.CPACS2GMSH.func.wingclassification import classify_wing
+from ceasiompy.utils.ceasiompyutils import bool_, get_part_type
+from cpacspy.cpacsfunctions import create_branch
+
+from ceasiompy.CPACS2GMSH.func.mesh_sizing import fuselage_size, wings_size
+from ceasiompy.CPACS2GMSH.func.utils import initialize_gmsh, write_gmsh, cfg_rotors
from ceasiompy.CPACS2GMSH.func.advancemeshing import (
refine_wing_section,
set_domain_mesh,
refine_small_surfaces,
min_fields,
)
-from ceasiompy.CPACS2GMSH.func.wingclassification import classify_wing
-
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.ceasiompyutils import get_part_type
-
-from cpacspy.cpacsfunctions import create_branch
-
-from ceasiompy.CPACS2GMSH.func.mesh_sizing import fuselage_size, wings_size
-log = get_logger()
-
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-
-class ModelPart:
- """
- A class to represent part of the aircraft or other part of the gmsh model
- in order to classify its entities and dimension tags
- ...
-
- Attributes
- ----------
- uid : str
- name of the part which correspond to its .brep file name for aircraft parts
- or a simple name describing the part function in the model
-
- """
-
- def __init__(self, uid):
- self.uid = uid
- self.part_type = ""
-
- # dimtag
- self.points = []
- self.lines = []
- self.surfaces = []
- self.volume = []
- # tag only
- self.points_tags = []
- self.lines_tags = []
- self.surfaces_tags = []
- self.volume_tag = []
- # children
- self.children_dimtag = set()
- # Physical groups
- self.physical_groups = []
-
- def associate_child_to_parent(self, child_dimtag):
- """
- Function to associate a child to a parent.
- all the entities belonging to a child (volume generated by the fragment operation)
- are associated to their parent part (volume before the fragment operation)
-
- Args:
- ----------
- child_dimtag : tuple (dim,tag)
- dimtag of the child volume
- """
-
- # Detect 2D or 3D child
- if child_dimtag[0] == 3:
- # Associate a 3D the child to the parent
- child_volume = [child_dimtag]
-
- child_surfaces, child_lines, child_points = get_entities_from_volume(child_volume)
-
- # Get the dimtags (3D only)
- child_volume_tag = [dimtag[1] for dimtag in child_volume]
- # Store in parent parts for latter use (3D only)
- self.volume.extend(child_volume)
- self.volume_tag.extend(child_volume_tag)
-
- # 2D child and parent
- elif child_dimtag[0] == 2:
- child_surfaces = [child_dimtag]
- child_lines = list(
- gmsh.model.getBoundary(
- child_surfaces, combined=True, oriented=False, recursive=False
- )
- )
- child_lines.sort()
-
- child_points = list(
- gmsh.model.getBoundary(
- child_surfaces, combined=True, oriented=False, recursive=True
- )
- )
- child_points.sort()
-
- elif child_dimtag[0] != 2 and child_dimtag[0] != 3:
- raise ValueError("Dimension of the child is not 2 or 3")
-
- # Get the dimtags
- child_surfaces_tags = [dimtag[1] for dimtag in child_surfaces]
- child_lines_tags = [dimtag[1] for dimtag in child_lines]
- child_points_tags = [dimtag[1] for dimtag in child_points]
-
- # Store in parent parts for latter use
- self.points.extend(child_points)
- self.lines.extend(child_lines)
- self.surfaces.extend(child_surfaces)
- self.points_tags.extend(child_points_tags)
- self.lines_tags.extend(child_lines_tags)
- self.surfaces_tags.extend(child_surfaces_tags)
-
- def clean_inside_entities(self, final_domain):
- """
- Function to clean inside entities of the part.
- Inside entities are entities that are not part of the final domain.
-
- Args:
- ----------
- final_domain : ModelPart
- final_domain part
- """
- if self.part_type == "rotor":
- # Detect all the entities in the domain with gmsh functions
- self.surfaces = sorted(
- list(set(self.surfaces).intersection(set(gmsh.model.getEntities(dim=2))))
- )
- self.lines = sorted(
- list(set(self.lines).intersection(set(gmsh.model.getEntities(dim=1))))
- )
- self.points = sorted(
- list(set(self.points).intersection(set(gmsh.model.getEntities(dim=0))))
- )
-
- # Update the dimtags
- self.surfaces_tags = [dimtag[1] for dimtag in self.surfaces]
- self.lines_tags = [dimtag[1] for dimtag in self.lines]
- self.points_tags = [dimtag[1] for dimtag in self.points]
- return
- # if not rotor part
- # Detect only shared entities with the final domain
- self.surfaces = sorted(list(set(self.surfaces).intersection(set(final_domain.surfaces))))
- self.lines = sorted(list(set(self.lines).intersection(set(final_domain.lines))))
- self.points = sorted(list(set(self.points).intersection(set(final_domain.points))))
+from pathlib import Path
+from ceasiompy.CPACS2GMSH.func.wingclassification import ModelPart
+from ceasiompy.utils.configfiles import ConfigFile
+from tixi3.tixi3wrapper import Tixi3
+from typing import List, Dict, Tuple
- # Update the dimtags
- self.surfaces_tags = sorted([dimtag[1] for dimtag in self.surfaces])
- self.lines_tags = sorted([dimtag[1] for dimtag in self.lines])
- self.points_tags = sorted([dimtag[1] for dimtag in self.points])
+from ceasiompy import log
+from ceasiompy.CPACS2GMSH.func.utils import MESH_COLORS
+from ceasiompy.utils.commonxpath import (
+ GMSH_MESH_SIZE_FUSELAGE_XPATH,
+ GMSH_MESH_SIZE_WINGS_XPATH,
+ GMSH_MESH_SIZE_CTRLSURFS_XPATH,
+)
+from ceasiompy.utils.commonnames import (
+ ACTUATOR_DISK_INLET_SUFFIX,
+ ACTUATOR_DISK_OUTLET_SUFFIX,
+ ENGINE_EXHAUST_SUFFIX,
+ ENGINE_INTAKE_SUFFIX,
+ GMSH_ENGINE_CONFIG_NAME,
+)
# =================================================================================================
# FUNCTIONS
# =================================================================================================
-def get_entities_from_volume(volume_dimtag):
- """
- Function to get the entities belonging to a volume.
- Surfaces and lines are found with the gmsh.model.getBoundary() function.
- Points are found with the gmsh.model.getEntities() function using recursive set to True.
- This choice seems the most efficient and robust at the state of development of Gmsh
-
- Args:
- ----------
- volume_dimtag : list
- a list containing the dimtag of the volume [(dim,tag)]
- which is a standard input format for other gmsh function
- ...
-
- Returns:
- ----------
- surfaces_dimtags : list(tuple)
- a list of tuples containing the dimtag of the surfaces
- lines_dimtags : list(tuple)
- a list of tuples containing the dimtag of the lines
- points_dimtags : list(tuple)
- a list of tuples containing the dimtag of the points
- """
-
- surfaces_dimtags = gmsh.model.getBoundary(
- volume_dimtag, combined=True, oriented=False, recursive=False
- )
-
- lines_dimtags = list(
- set().union(
- *[
- gmsh.model.getBoundary([surface], combined=True, oriented=False, recursive=False)
- for surface in surfaces_dimtags
- ]
- )
- )
- lines_dimtags.sort()
-
- points_dimtags = list(
- set().union(
- *[
- gmsh.model.getBoundary([surface], combined=True, oriented=False, recursive=True)
- for surface in surfaces_dimtags
- ]
- )
- )
- points_dimtags.sort()
-
- return surfaces_dimtags, lines_dimtags, points_dimtags
-
-
def define_engine_bc(engine_part, brep_dir):
"""
Function to define the boundary conditions for the engine part.
@@ -396,112 +216,18 @@ def process_gmsh_log(gmsh_log):
log.info(f"Total meshing time : {round(total_time, 2)}s")
-def add_disk_actuator(brep_dir, config_file):
+def duplicate_disk_actuator_surfaces(part: ModelPart):
"""
- Function to create a 2D disk in a given location to represent a rotor as a
- disk actuator
- ...
+ Duplicate the surfaces of a disk actuator.
Args:
- ----------
- brep_dir : Path
- path to the brep files of the aircraft that also contains the rotor config file
- config_file : Configfile
- config file of the propellers configuration on the aircraft
+ part (ModelPart): Part object of the rotor modeled as a disk actuator.
"""
- nb_rotor = int(config_file["NB_ROTOR"])
-
- for k in range(1, nb_rotor + 1):
- # get the rotor configuration from cfg file
- rotor_uid = config_file[f"UID_{k}"]
- radius = float(config_file[f"{rotor_uid}_ROTOR_RADIUS"])
- sym = int(config_file[f"{rotor_uid}_SYMMETRIC"])
- trans_vector = (
- float(config_file[f"{rotor_uid}_TRANS_X"]),
- float(config_file[f"{rotor_uid}_TRANS_Y"]),
- float(config_file[f"{rotor_uid}_TRANS_Z"]),
- )
- rot_vector = (
- float(config_file[f"{rotor_uid}_ROT_X"]),
- float(config_file[f"{rotor_uid}_ROT_Y"]),
- float(config_file[f"{rotor_uid}_ROT_Z"]),
- )
-
- # Adding rotating disk
- gmsh.initialize()
- # generate the inlet_disk (gmsh always create a disk in the xy plane)
- disk_tag = gmsh.model.occ.addDisk(*trans_vector, radius, radius)
- disk_dimtag = (2, disk_tag)
-
- # y axis 180deg flip to make the inlet of the disk face forward
- gmsh.model.occ.rotate([disk_dimtag], *trans_vector, 0, 1, 0, np.radians(180))
- gmsh.model.occ.synchronize()
-
- # rotation given in the cpacs file
- # x axis
- gmsh.model.occ.rotate([disk_dimtag], *trans_vector, 1, 0, 0, np.radians(rot_vector[0]))
- # y axis
- gmsh.model.occ.rotate([disk_dimtag], *trans_vector, 0, 1, 0, np.radians(rot_vector[1]))
- # z axis
- gmsh.model.occ.rotate([disk_dimtag], *trans_vector, 0, 0, 1, np.radians(rot_vector[2]))
-
- gmsh.model.occ.synchronize()
-
- path_disk = Path(brep_dir, f"{rotor_uid}.brep")
- gmsh.write(str(path_disk))
-
- gmsh.clear()
- gmsh.finalize()
-
- if sym == 2:
- # Adding the symmetric
- gmsh.initialize()
- # generate the inlet_disk (gmsh always create a disk in the xy plane)
- disk_tag = gmsh.model.occ.addDisk(*trans_vector, radius, radius)
- disk_dimtag = (2, disk_tag)
-
- # y axis 180deg flip to make the inlet of the disk face forward is not necessary for
- # the mirrored part, and for now the symmetry is not implemented correctly since
- # the symmetry does not take into account the orientation of the rotor and the plane
- # of symmetry is assume to be the xz plane
- # When the face of the disk actuator are not oriented well the simulation shows
- # increasing cd and will probably diverge
-
- # rotation given in the cpacs file
- # x axis
- gmsh.model.occ.rotate([disk_dimtag], *trans_vector, 1, 0, 0, np.radians(rot_vector[0]))
- # y axis
- gmsh.model.occ.rotate([disk_dimtag], *trans_vector, 0, 1, 0, np.radians(rot_vector[1]))
- # z axis
- gmsh.model.occ.rotate([disk_dimtag], *trans_vector, 0, 0, 1, np.radians(rot_vector[2]))
-
- gmsh.model.occ.synchronize()
-
- gmsh.model.occ.mirror([disk_dimtag], 0, 1, 0, 0)
- gmsh.model.occ.synchronize()
- path_disk = Path(brep_dir, f"{rotor_uid}_mirrored.brep")
- gmsh.write(str(path_disk))
-
- gmsh.clear()
- gmsh.finalize()
-
-
-def duplicate_disk_actuator_surfaces(part):
- """
- Function to duplicate the surfaces of a disk actuator
- ...
-
- Args:
- ----------
- part : ModelPart
- Part object of the rotor modeled as a disk actuator
- """
# "crack" the mesh by duplicating the elements and nodes on the disk surface
if len(part.physical_groups) > 1:
- # raise error since disk actuator can only have one surface before duplicating
- raise ValueError("Disk actuator can only have one surface")
+ raise ValueError("Disk actuator can only have one surface.")
gmsh.plugin.setNumber("Crack", "Dimension", 2)
gmsh.plugin.setNumber("Crack", "PhysicalGroup", part.physical_groups[0])
@@ -512,6 +238,7 @@ def duplicate_disk_actuator_surfaces(part):
# Set the new physical group for the back surface
gmsh.model.setPhysicalName(2, new_tag, f"{part.uid}{ACTUATOR_DISK_OUTLET_SUFFIX}")
gmsh.plugin.run("Crack")
+ log.info("Created outlet disk actuator surface")
def control_disk_actuator_normal():
@@ -561,124 +288,104 @@ def control_disk_actuator_normal():
def generate_gmsh(
- cpacs,
- cpacs_path,
- brep_dir,
- results_dir,
- open_gmsh=False,
- farfield_factor=6,
- symmetry=False,
+ tixi: Tixi3,
+ brep_dir: Path,
+ results_dir: Path,
+ open_gmsh: bool = False,
+ farfield_factor: float = 6.0,
+ symmetry: bool = False,
farfield_size_factor=10,
n_power_factor=2,
n_power_field=0.9,
fuselage_mesh_size_factor=1,
- wing_mesh_size_factor=1.5,
- mesh_size_engines=0.23,
- mesh_size_propellers=0.23,
- refine_factor=2.0,
- refine_truncated=False,
- auto_refine=True,
- testing_gmsh=False,
-):
+ wing_mesh_size_factor=0.5,
+ mesh_size_engines: float = 0.23,
+ mesh_size_propellers: float = 0.23,
+ refine_factor: float = 2.0,
+ refine_truncated: bool = False,
+ auto_refine: bool = True,
+ testing_gmsh: bool = False,
+ surf: str = None,
+ angle: str = None,
+) -> Path:
"""
- Function to generate a mesh from brep files forming an airplane
- Function 'generate_gmsh' is a subfunction of CPACS2GMSH which return a
- mesh file.
- The airplane is fused with the different brep files : fuselage, wings and
- other parts are identified anf fused together, then a farfield is generated
- and the airplane is subtracted to him to generate the final fluid domain
- marker of each airplane part and farfield surfaces is reported in the mesh
- file.
+ Generates a mesh from brep files forming an airplane.
+
+ 1. Airplane is fused with the different brep files: fuselage, wings etc.
+ 2. Farfield is generated.
+ 3. Airplane is subtracted to farfield to generate the final fluid domain.
+
+ A marker for each airplane part and farfield surfaces is reported in the mesh file.
+
Args:
- ----------
- cpacs : CPACS
- CPACS object
- brep_dir : Path
- Path to the directory containing the brep files
- results_dir : Path
- Path to the directory containing the result (mesh) files
- open_gmsh : bool
- Open gmsh GUI after the mesh generation if set to true
- farfield_factor = float
- Factor to enlarge the farfield : factor times the largest dimension(x,y,z)
- of the aircraft
- symmetry : bool
- If set to true, the mesh will be generated with symmetry wrt the x,z plane
- mesh_size_farfield : float
- Size of the farfield mesh
- mesh_size_fuselage : float
- Size of the fuselage mesh
- mesh_size_wings : float
- Size of the wing mesh
- mesh_size_engines : float
- Size of the engine mesh
- mesh_size_propellers : float
- Size of the propeller mesh
- advance_mesh : bool
- If set to true, the mesh will be generated with advanced meshing options
- refine_factor : float
- refine factor for the mesh le and te edge
- refine_truncated : bool
- If set to true, the refinement can change to match the truncated te thickness
- auto_refine : bool
- If set to true, the mesh will be checked for quality
- testing_gmsh : bool
- If set to true, the gmsh sessions will not be clear and killed at the end of
- the function, this allow to test the gmsh feature after the call of generate_gmsh()
- ...
+ cpacs (CPACS): CPACS file.
+ brep_dir (Path): Path to the directory containing the brep files.
+ results_dir (Path): Path to the directory containing the result mesh files.
+ open_gmsh (bool = False): Opens gmsh's GUI after the mesh generation if set to true.
+ farfield_factor (float = 6): Factor to enlarge the farfield.
+ symmetry (bool = False): Mesh will be generated with symmetry wrt to the x,z plane.
+
+ #mesh_size_farfield (float): Size of the farfield mesh.
+ #mesh_size_fuselage (float): Size of the fuselage mesh.
+ #mesh_size_wings (float): Size of the wing mesh.
+
+ mesh_size_engines (float = 0.23): Size of the engine mesh.
+ mesh_size_propellers (float = 0.23): Size of the propeller mesh.
+
+ #advance_mesh (bool):
+ # If set to true,
+ # the mesh will be generated with advanced meshing options.
+
+ refine_factor (float = 2.0): Refine factor for the mesh's LE and TE.
+ refine_truncated (bool = False): Refinement can change to match the truncated te thickness.
+ auto_refine (bool = False): Mesh will be checked for quality.
+ testing_gmsh (bool = False): Gmsh sessions will not be clear and killed at the end of
+ the function, this allow to test the gmsh feature after the call of generate_gmsh()
+
Returns:
- ----------
- mesh_file : Path
- Path to the mesh file generated by gmsh
- aircraft_parts : list(ModelPart)
- List of the aircraft parts in the model
+ (Path, List[ModelPart]):
+ - Path to the mesh file generated by gmsh.
+ - List of the aircraft parts in the model.
"""
+ # Determine if rotors are present in the aircraft model
+ rotor_model = cfg_rotors(brep_dir)
- # Determine if rotor are present in the aircraft model
- rotor_model = False
- if Path(brep_dir, "config_rotors.cfg").exists():
- rotor_model = True
+ # Retrieve all breps
+ brep_files = sorted(brep_dir.glob("*.brep"))
- if rotor_model:
- log.info("Adding disk actuator")
- config_file = ConfigFile(Path(brep_dir, "config_rotors.cfg"))
- add_disk_actuator(brep_dir, config_file)
-
- # Retrieve all brep
- brep_files = list(brep_dir.glob("*.brep"))
- brep_files.sort()
-
- # initialize gmsh
- gmsh.initialize()
- # Stop gmsh output log in the terminal
- gmsh.option.setNumber("General.Terminal", 0)
- # Log complexity
- gmsh.option.setNumber("General.Verbosity", 5)
+ # Initialize gmsh
+ initialize_gmsh()
+ gmsh.logger.start()
+ # gmsh.option.setNumber("Geometry.Tolerance", 1e-3) # Adjust as needed
# Import each aircraft original parts / parent parts
- aircraft_parts = []
+ aircraft_parts: List[ModelPart] = []
parts_parent_dimtag = []
- log.info(f"Importing files from {brep_dir}")
+
+ log.info(f"Importing files from {brep_dir}.")
+
+ # Fuse correctly
for brep_file in brep_files:
# Import the part and create the aircraft part object
part_entities = gmsh.model.occ.importShapes(str(brep_file), highestDimOnly=False)
- gmsh.model.occ.synchronize()
# Create the aircraft part object
part_obj = ModelPart(uid=brep_file.stem)
- part_obj.part_type = get_part_type(cpacs.tixi, part_obj.uid)
+ part_obj.part_type = get_part_type(tixi, part_obj.uid)
# Add to the list of aircraft parts
aircraft_parts.append(part_obj)
parts_parent_dimtag.append(part_entities[0])
- log.info(f"Part : {part_obj.uid} imported")
+ log.info(f"Part: {part_obj.uid} imported.")
+ gmsh.model.occ.synchronize()
- gmsh.model.occ.synchronize()
+ ###########################################
+ # Create external domain for the farfield #
+ ###########################################
- # Create external domain for the farfield
- model_bb = gmsh.model.getBoundingBox(-1, -1)
+ model_bb = gmsh.model.getBoundingBox(-1, -1) # Get bounding box of whole model
model_dimensions = [
abs(model_bb[0] - model_bb[3]),
abs(model_bb[1] - model_bb[4]),
@@ -692,54 +399,62 @@ def generate_gmsh(
]
domain_length = farfield_factor * max(model_dimensions)
+
farfield = gmsh.model.occ.addSphere(*model_center, domain_length)
- gmsh.model.occ.synchronize()
+ gmsh.model.occ.synchronize()
ext_domain = [(3, farfield)]
+ ##################################################
+ # Ensure domain is symmetric if symmetry == True #
+ ##################################################
+
if symmetry:
- log.info("Preparing: symmetry operation")
+ log.info("Preparing: symmetry operation.")
sym_plane = gmsh.model.occ.addDisk(*model_center, domain_length, domain_length)
sym_vector = [0, 1, 0]
plane_vector = [0, 0, 1]
if sym_vector != plane_vector:
rotation_axis = np.cross(sym_vector, plane_vector)
- gmsh.model.occ.rotate([(2, sym_plane)], *model_center, *rotation_axis, np.pi / 2)
+ gmsh.model.occ.rotate(
+ [(2, sym_plane)],
+ *model_center,
+ *rotation_axis,
+ np.pi / 2,
+ )
sym_box = gmsh.model.occ.extrude(
[(2, sym_plane)], *(np.multiply(sym_vector, -domain_length * 1.1))
)
parts_parent_dimtag.append(sym_box[1])
- log.info("Start fragment operation between the aircraft and the farfield")
-
- _, children_dimtag = gmsh.model.occ.fragment(ext_domain, parts_parent_dimtag)
- gmsh.model.occ.synchronize()
-
- log.info("Fragment operation finished")
+ ###########################################
+ # Fragment operation aircraft to farfield #
+ ###########################################
- # fragment produce fragments_dimtag and children_dimtag
+ # gmsh.model.occ.fragmentfragment produces fragments_dimtag and children_dimtag
- # fragments_dimtag is a list of tuples (dimtag, tag) of all the volumes in the model
- # the first fragment is the entire domain, each other fragment are sub volume of the domain
+ # 1. fragments_dimtag:
+ # fragments_dimtag is a list of tuples (dimtag, tag) of all the volumes in the model.
+ # The first fragment is the entire domain, each fragments are sub-volumes of the domain.
+ # Since it is associated to the entire domain we don't need it.
- # children_dimtag is a list list of tuples (dimtag, tag)
- # the first list is associated to the entire domain as for fragments_dimtag, we don't need it
- # so for the following we work with children_dimtag[1:]
-
- # The rest of children_dimtag are list of tuples (dimtag, tag) that represent volumes in the
- # model children_dimtag is "sorted" according to the order of importation of the parent parts.
- # for example : if the first part imported was "fuselage1" then the first children_dimtag
+ # 2. children_dimtag:
+ # children_dimtag is a list list of tuples (dimtag, tag).
+ # The rest of children_dimtag are list of tuples (dimtag, tag)
+ # that represent volumes in the model.
+ # children_dimtag is "sorted" according to the order of importation of the parent parts.
+ # Ror example: if the first part imported was "fuselage1" then the first children_dimtag
# is a list of all the "child" volumes in the model that are from the "parent" "fuselage1"
- # we can then associate each entities in the model to their parent origin
+ # we can then associate each entities in the model to their parent origin.
# When two parents part ex. a fuselage and a wing intersect each other
# two children are generated for both parts, thus if a child is shared by
# two parent parts (or more), then this child is a volume given
# by the intersection of the two parent parts, we don't need them and some
- # of its surfaces, lines and point in the final models
+ # of its surfaces, lines and point in the final models.
# Thus we need to find those unwanted child and their entities that don't belong
- # to the final model, and remove them
+ # to the final model, and remove them.
# afterward the entities of each child will be associated with their parent part names
# then we can delete all the child in the model, and only keep the final domain
@@ -748,18 +463,26 @@ def generate_gmsh(
# to the original parent parts imported at the begging of the function
# If symmetry is applied the last children_dimtag is all the volume in the symmetry cylinder
- # thus the we can easily remove them and only keep the volumes of half domain
+ # thus the we can easily remove them and only keep the volumes of half domain.
+
+ log.info("Start fragment operation between the aircraft and the farfield.")
+
+ _, children_dimtag = gmsh.model.occ.fragment(ext_domain, parts_parent_dimtag)
+
+ gmsh.model.occ.synchronize()
+ log.info("Fragment operation finished.")
unwanted_children = []
if symmetry:
- # take the unwanted children from symmetry
+ # Take the unwanted children from symmetry
unwanted_children = children_dimtag[-1]
- # Attention this only take into account volumes elements in the symmetry
+ # Careful: this only take into account volumes elements in the symmetry
# Disk actuator that are 2D element are not taken into account
# and will be removed latter
# remove them from the model
gmsh.model.occ.remove(unwanted_children, recursive=True)
+ # gmsh.model.mesh.removeDuplicateNodes()
gmsh.model.occ.synchronize()
# Get the children of the aircraft parts
@@ -786,10 +509,11 @@ def generate_gmsh(
parent.children_dimtag.remove(dimtag)
if not parent.children_dimtag:
- log.info(f"{parent.uid} has no more children due to symmetry, it will be deleted")
+ log.info(f"{parent.uid} has no more children due to symmetry, it will be deleted.")
unwanted_parents.append(parent)
# Remove unwanted parents
+ # Redefine aircraft_parts
if unwanted_parents:
aircraft_parts = [part for part in aircraft_parts if part not in unwanted_parents]
@@ -809,12 +533,14 @@ def generate_gmsh(
unwanted_children = unwanted_children.union(shared_children)
+ # Convert set to list
unwanted_children = list(unwanted_children)
# Remove unwanted children from the model
gmsh.model.occ.remove(unwanted_children, recursive=True)
+
gmsh.model.occ.synchronize()
- log.info(f"Unwanted children {unwanted_children} removed from model")
+ log.info(f"Unwanted children {unwanted_children} removed from model.")
# Associate good child with their parent
good_children = []
@@ -827,24 +553,34 @@ def generate_gmsh(
parent.associate_child_to_parent(child_dimtag)
# Now that its clear which child entities in the model are from which parent part,
- # we can delete the child volumes and only keep the final domain
+ # we can delete the child volumes and only keep the final domain.
gmsh.model.occ.remove(good_children, recursive=True)
- gmsh.model.occ.synchronize()
+ gmsh.model.occ.synchronize()
# Now only the final domain is left, in the model, we can find its entities
# we will use the ModelPart class to store the entities of the final domain
final_domain = ModelPart("fluid")
left_volume = gmsh.model.getEntities(dim=3)
+
+ len_left_volume = len(left_volume)
+ if len_left_volume != 1:
+ ValueError("Issue with intersection of two volumes.")
+
+ log.info(f"Left volume {left_volume}.")
final_domain.associate_child_to_parent(*left_volume)
+ ###############
+ # Clean model #
+ ###############
+
# As already discussed, it is often that two parts intersect each other,
# it can also happened that some parts create holes inside other parts
# for example a fuselage and 2 wings defined in the center of the fuselage
- # will create a holed fragment of the fuselage
+ # will create a holed fragment of the fuselage.
# This is not a problem since this hole is not in the final domain volume
- # but they may be some lines and surfaces from the hole in the fuselage
+ # but there might be some lines and surfaces from the hole in the fuselage
# that were not eliminated since they were shared by the unwanted children
- # and those lines and surfaces were assigned to the fuselage part
+ # and those lines and surfaces were assigned to the fuselage part.
# thus we need to clean a bit the associated entities by the function
# associate_child_to_parent() by comparing them with the entities of the
@@ -878,11 +614,11 @@ def generate_gmsh(
gmsh.model.setPhysicalName(2, surfaces_group, f"{part.uid}")
part.physical_groups.append(surfaces_group)
- log.info("Model has been cleaned")
+ log.info("Model has been cleaned.")
# Farfield
- # farfield entities are simply the entities left in the final domain
- # that don't belong to the aircraft
+ # farfield entities are simply the entities left in the final domain that
+ # don't belong to the aircraft.
farfield_surfaces = list(set(final_domain.surfaces) - set(aircraft.surfaces))
farfield_points = list(set(final_domain.points) - set(aircraft.points))
@@ -930,37 +666,38 @@ def generate_gmsh(
# Thus be sure to define mesh size in a certain order to control
# the size of the points on boundaries.
- fuselage_maxlen, fuselage_minlen = fuselage_size(cpacs_path)
+ fuselage_maxlen, fuselage_minlen = fuselage_size(tixi)
mesh_size_fuselage = ((fuselage_maxlen + fuselage_minlen) / 2) / fuselage_mesh_size_factor
log.info(f"Mesh size fuselage={mesh_size_fuselage:.3f} m")
- create_branch(cpacs.tixi, GMSH_MESH_SIZE_FUSELAGE_XPATH)
- cpacs.tixi.updateDoubleElement(GMSH_MESH_SIZE_FUSELAGE_XPATH, mesh_size_fuselage, "%.3f")
+ create_branch(tixi, GMSH_MESH_SIZE_FUSELAGE_XPATH)
+ tixi.updateDoubleElement(GMSH_MESH_SIZE_FUSELAGE_XPATH, mesh_size_fuselage, "%.3f")
- wing_maxlen, wing_minlen = wings_size(cpacs_path)
+ wing_maxlen, wing_minlen = wings_size(tixi)
mesh_size_wing = ((wing_maxlen * 0.8 + wing_minlen) / 2) / wing_mesh_size_factor
+
log.info(f"Mesh size wing={mesh_size_wing:.3f} m")
- create_branch(cpacs.tixi, GMSH_MESH_SIZE_WINGS_XPATH)
- cpacs.tixi.updateDoubleElement(GMSH_MESH_SIZE_WINGS_XPATH, mesh_size_wing, "%.3f")
+ create_branch(tixi, GMSH_MESH_SIZE_WINGS_XPATH)
+ create_branch(tixi, GMSH_MESH_SIZE_CTRLSURFS_XPATH)
+
+ tixi.updateDoubleElement(GMSH_MESH_SIZE_WINGS_XPATH, mesh_size_wing, "%.3f")
+
+ AIRCRAFT_DICT = {
+ "fuselage": mesh_size_fuselage,
+ "wing": mesh_size_wing,
+ "pylon": mesh_size_wing,
+ "engine": mesh_size_engines,
+ "rotor": mesh_size_propellers,
+ }
for part in aircraft_parts:
- if part.part_type == "fuselage":
- part.mesh_size = mesh_size_fuselage
- gmsh.model.mesh.setSize(part.points, part.mesh_size)
- gmsh.model.setColor(part.surfaces, *MESH_COLORS[part.part_type], recursive=False)
- elif part.part_type in ["wing", "pylon"]:
- part.mesh_size = mesh_size_wing
- gmsh.model.mesh.setSize(part.points, part.mesh_size)
- gmsh.model.setColor(part.surfaces, *MESH_COLORS[part.part_type], recursive=False)
- elif part.part_type == "engine":
- part.mesh_size = mesh_size_engines
- gmsh.model.mesh.setSize(part.points, part.mesh_size)
- gmsh.model.setColor(part.surfaces, *MESH_COLORS[part.part_type], recursive=False)
- elif part.part_type == "rotor":
- part.mesh_size = mesh_size_propellers
- gmsh.model.mesh.setSize(part.points, part.mesh_size)
+ if part.part_type in AIRCRAFT_DICT:
+ part.mesh_size = AIRCRAFT_DICT[part.part_type]
+ gmsh.model.mesh.setSize(part.points, AIRCRAFT_DICT[part.part_type])
gmsh.model.setColor(part.surfaces, *MESH_COLORS[part.part_type], recursive=False)
+ else:
+ log.warning(f"Incorrect part.part_type {part.part_type} in generategmesh.py.")
# Set mesh size and color of the farfield
h_max_model = max(wing_maxlen, fuselage_maxlen)
@@ -976,7 +713,7 @@ def generate_gmsh(
# Wing leading edge and trailing edge detection
for part in aircraft_parts:
- if part.part_type == "wing":
+ if part.part_type in ["wing", "ctrlsurf"]:
classify_wing(part, aircraft_parts)
log.info(
f"Classification of {part.uid} done"
@@ -984,8 +721,11 @@ def generate_gmsh(
)
# Generate advance meshing features
+ mesh_fields = {"nbfields": 0, "restrict_fields": []}
if refine_factor != 1:
- mesh_fields = {"nbfields": 0, "restrict_fields": []}
+ log.info(f"Refining wings with factor {refine_factor}")
+
+ # Refine wings
for part in aircraft_parts:
if part.part_type == "wing":
refine_wing_section(
@@ -1017,13 +757,13 @@ def generate_gmsh(
gmsh.option.setNumber("Mesh.Algorithm", 6)
gmsh.option.setNumber("Mesh.LcIntegrationPrecision", 1e-6)
-
gmsh.model.occ.synchronize()
- gmsh.logger.start()
gmsh.model.mesh.generate(1)
gmsh.model.mesh.generate(2)
+ gmsh.model.occ.synchronize()
+
# Control of the mesh quality
if refine_factor != 1 and auto_refine:
bad_surfaces = []
@@ -1038,13 +778,15 @@ def generate_gmsh(
)
bad_surfaces.extend(refined_surfaces)
+ log.info("Refining small surfaces.")
+
if bad_surfaces:
- log.info(f"{len(bad_surfaces)} surface(s) need to be refined")
+ log.info(f"{len(bad_surfaces)} surface(s) needs to be refined")
# Reset the background mesh
mesh_fields = min_fields(mesh_fields)
- if open_gmsh:
+ if bool_(open_gmsh):
log.info("Insufficient mesh size surfaces are displayed in red")
log.info("GMSH GUI is open, close it to continue...")
gmsh.fltk.run()
@@ -1056,22 +798,85 @@ def generate_gmsh(
for surface in bad_surfaces:
gmsh.model.setColor([(2, surface)], *MESH_COLORS["good_surface"], recursive=False)
+ gmsh.model.occ.synchronize()
log.info("Remeshing process finished")
- if open_gmsh:
+ if bool_(open_gmsh):
log.info("Corrected mesh surfaces are displayed in green")
+ gmsh.model.occ.removeAllDuplicates()
+ gmsh.model.occ.synchronize()
+
+ # Fuse surfaces
+ fusings: Dict[str, List] = {}
+ tags_dict: Dict[str, List] = {}
+ tags = []
+ # Get all physical groups
+ # TODO: Remap getPhysicalGroups
+ surfaces: List[Tuple[int, int]] = gmsh.model.getPhysicalGroups(dim=2)
+
+ fusing = True
+ if fusing:
+ for dim, tag in surfaces:
+ name = gmsh.model.getPhysicalName(dim, tag)
+ log.info(f"Dimension: {dim}, Tag: {tag}, Name: {name}")
+ # Remove '_Seg{i}'
+ root_name = re.sub(r"_Seg\d+", "", name)
+ if root_name not in fusings:
+ fusings[root_name] = []
+ tags_dict[root_name] = []
+
+ fusings[root_name].append(gmsh.model.getEntitiesForPhysicalGroup(dim, tag))
+ tags_dict[root_name].append(tag)
+ tags.append(tag)
+
+ for fusing in fusings:
+ fused_len = len(fusings[fusing])
+ if fused_len > 1:
+ fused_entities = list(set(
+ [entity for group in fusings[fusing] for entity in group]
+ ))
+ fused_tags = list(set(
+ [tag for tag in tags_dict[fusing]]
+ ))
+ log.info(f"Fusing {fused_len} wings named {fusing}")
+ new_tag = max(tags) + 1
+ tags.append(new_tag)
+ gmsh.model.addPhysicalGroup(2, fused_entities, new_tag)
+ gmsh.model.setPhysicalName(dim, new_tag, fusing)
+ gmsh.model.removePhysicalGroups([(2, tag) for tag in fused_tags])
+ gmsh.model.occ.synchronize()
+
+ # Necessary for after fusing back wings
+ gmsh.model.occ.removeAllDuplicates()
+ gmsh.model.occ.synchronize()
+
# Apply smoothing
log.info("2D mesh smoothing process started")
gmsh.model.mesh.optimize("Laplace2D", niter=10)
log.info("Smoothing process finished")
+ # gmsh.model.occ.removeAllDuplicates()
+
+ # Synchronize again to update the model after removing duplicates
gmsh.model.occ.synchronize()
- mesh_2d_path = Path(results_dir, "2d_mesh.msh")
- gmsh.write(str(mesh_2d_path))
+ surfaces: List[Tuple[int, int]] = gmsh.model.getPhysicalGroups(dim=2)
+
+ for dim, tag in surfaces:
+ name = gmsh.model.getPhysicalName(dim, tag)
+ log.info(f"New Dimension: {dim}, Tag: {tag}, Name: {name}")
+
+ if surf is None:
+ surface_mesh_path = Path(results_dir, "surface_mesh.msh")
+ gmsh.write(str(surface_mesh_path))
+ # cgnsmesh_path = Path(results_dir, "mesh.cgns")
+ # gmsh.write(str(cgnsmesh_path))
+ else:
+ surface_mesh_path = Path(results_dir, f"surface_mesh_{surf}_{angle}.msh")
+ gmsh.write(str(surface_mesh_path))
- if open_gmsh:
+ if bool_(open_gmsh):
log.info("Result of 2D surface mesh")
log.info("GMSH GUI is open, close it to continue...")
gmsh.fltk.run()
@@ -1092,18 +897,15 @@ def generate_gmsh(
# Control surface orientation
control_disk_actuator_normal()
- su2mesh_path = Path(results_dir, "mesh.su2")
- gmsh.write(str(su2mesh_path))
-
- cgnsmesh_path = Path(results_dir, "mesh.cgns")
- gmsh.write(str(cgnsmesh_path))
+ if surf is None:
+ su2mesh_path = write_gmsh(results_dir, "mesh.su2")
+ else:
+ mesh_name = f"mesh_{surf}_{angle}"
+ su2mesh_path = write_gmsh(results_dir, f"{mesh_name}.su2")
process_gmsh_log(gmsh.logger.get())
- if open_gmsh:
- log.info("Result of the 3D volume mesh")
- log.info("GMSH GUI is open, close it to continue...")
- gmsh.fltk.run()
+ gmsh.model.occ.synchronize()
log.info("Mesh generation finished")
# Create duplicated mesh surface for disk actuator
@@ -1111,12 +913,13 @@ def generate_gmsh(
if not testing_gmsh:
gmsh.clear()
gmsh.finalize()
- return su2mesh_path, aircraft_parts
+ return su2mesh_path
# =================================================================================================
# MAIN
# =================================================================================================
+
if __name__ == "__main__":
- print("Nothing to execute!")
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/CPACS2GMSH/func/gmsh_utils.py b/ceasiompy/CPACS2GMSH/func/gmsh_utils.py
deleted file mode 100644
index 4c10a9eff..000000000
--- a/ceasiompy/CPACS2GMSH/func/gmsh_utils.py
+++ /dev/null
@@ -1,28 +0,0 @@
-"""
-CEASIOMpy: Conceptual Aircraft Design Software
-
-Developed for CFS ENGINEERING, 1015 Lausanne, Switzerland
-
-Functions and variables for CPACS2GMSH
-
-Python version: >=3.8
-
-| Author : Tony Govoni
-| Creation: 2022-02-04
-
-TODO:
-
-"""
-
-# Define mesh color for GMSH, only use in GUI (red, green, blue, brightness)
-MESH_COLORS = {
- "farfield": (255, 200, 0, 100),
- "symmetry": (153, 255, 255, 100),
- "wing": (0, 200, 200, 100),
- "fuselage": (255, 215, 0, 100),
- "pylon": (255, 15, 255, 100),
- "engine": (127, 0, 255, 100),
- "rotor": (0, 0, 0, 100),
- "bad_surface": (255, 0, 0, 255),
- "good_surface": (0, 255, 0, 100),
-}
diff --git a/ceasiompy/CPACS2GMSH/func/mesh_sizing.py b/ceasiompy/CPACS2GMSH/func/mesh_sizing.py
index 287cbc0c9..8e9664f51 100644
--- a/ceasiompy/CPACS2GMSH/func/mesh_sizing.py
+++ b/ceasiompy/CPACS2GMSH/func/mesh_sizing.py
@@ -3,9 +3,8 @@
Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-This script contains different functions to classify and manipulate wing elements
+This script contains different functions to classify and manipulate wing elements.
-Python version: >=3.8
| Author: Giacomo Benedetti
| Creation: 2023-11-20
@@ -13,29 +12,29 @@
"""
-
# =================================================================================================
# IMPORTS
# =================================================================================================
import math
+
from ceasiompy.CPACS2SUMO.func.getprofile import get_profile_coord
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy.utils.geometryfunctions import get_positionings
+from typing import Tuple
+from tixi3.tixi3wrapper import Tixi3
from ceasiompy.utils.generalclasses import Transformation
+from ceasiompy import log
from ceasiompy.utils.commonxpath import FUSELAGES_XPATH, WINGS_XPATH
-from cpacspy.cpacsfunctions import open_tixi
-log = get_logger()
# =================================================================================================
# FUNCTIONS
# =================================================================================================
-def fuselage_size(cpacs_path):
- tixi = open_tixi(cpacs_path)
+def fuselage_size(tixi: Tixi3) -> None:
if tixi.checkElement(FUSELAGES_XPATH):
fus_cnt = tixi.getNamedChildrenCount(FUSELAGES_XPATH, "fuselage")
for i_fus in range(fus_cnt):
@@ -43,72 +42,11 @@ def fuselage_size(cpacs_path):
fus_transf = Transformation()
fus_transf.get_cpacs_transf(tixi, fus_xpath)
- # Positionings
- if tixi.checkElement(fus_xpath + "/positionings"):
- pos_cnt = tixi.getNamedChildrenCount(fus_xpath + "/positionings", "positioning")
-
- pos_x_list = []
- pos_y_list = []
- pos_z_list = []
- from_sec_list = []
- to_sec_list = []
-
- for i_pos in range(pos_cnt):
- pos_xpath = fus_xpath + "/positionings/positioning[" + str(i_pos + 1) + "]"
-
- length = tixi.getDoubleElement(pos_xpath + "/length")
- sweep_deg = tixi.getDoubleElement(pos_xpath + "/sweepAngle")
- sweep = math.radians(sweep_deg)
- dihedral_deg = tixi.getDoubleElement(pos_xpath + "/dihedralAngle")
- dihedral = math.radians(dihedral_deg)
-
- # Get the corresponding translation of each positioning
- pos_x_list.append(length * math.sin(sweep))
- pos_y_list.append(length * math.cos(dihedral) * math.cos(sweep))
- pos_z_list.append(length * math.sin(dihedral) * math.cos(sweep))
- # Get which section are connected by the positioning
- if tixi.checkElement(pos_xpath + "/fromSectionUID"):
- from_sec = tixi.getTextElement(pos_xpath + "/fromSectionUID")
- else:
- from_sec = ""
- from_sec_list.append(from_sec)
- if tixi.checkElement(pos_xpath + "/toSectionUID"):
- to_sec = tixi.getTextElement(pos_xpath + "/toSectionUID")
- else:
- to_sec = ""
- to_sec_list.append(to_sec)
-
- for j_pos in range(pos_cnt):
- if from_sec_list[j_pos] == "":
- prev_pos_x = 0
- prev_pos_y = 0
- prev_pos_z = 0
- elif from_sec_list[j_pos] == to_sec_list[j_pos - 1]:
- prev_pos_x = pos_x_list[j_pos - 1]
- prev_pos_y = pos_y_list[j_pos - 1]
- prev_pos_z = pos_z_list[j_pos - 1]
- else:
- index_prev = to_sec_list.index(from_sec_list[j_pos])
- prev_pos_x = pos_x_list[index_prev]
- prev_pos_y = pos_y_list[index_prev]
- prev_pos_z = pos_z_list[index_prev]
-
- if j_pos < len(pos_y_list):
- pos_x_list[j_pos] += prev_pos_x
- pos_y_list[j_pos] += prev_pos_y
- pos_z_list[j_pos] += prev_pos_z
-
- else:
- log.error('No "positionings" have been found!')
- pos_cnt = 0
-
- # Sections
- sec_cnt = tixi.getNamedChildrenCount(fus_xpath + "/sections", "section")
-
- if pos_cnt == 0:
- pos_x_list = [0.0] * sec_cnt
- pos_y_list = [0.0] * sec_cnt
- pos_z_list = [0.0] * sec_cnt
+ sec_cnt, _, pos_y_list, pos_z_list = get_positionings(
+ tixi=tixi,
+ xpath=fus_xpath,
+ element="fuselage",
+ )
# print(pos_cnt)
body_frm_height_values = []
@@ -121,10 +59,12 @@ def fuselage_size(cpacs_path):
sec_transf.get_cpacs_transf(tixi, sec_xpath)
# Elements
- elem_cnt = tixi.getNamedChildrenCount(sec_xpath + "/elements", "element")
+ elem_cnt = tixi.getNamedChildrenCount(
+ sec_xpath + "/elements", "element")
for i_elem in range(elem_cnt):
- elem_xpath = sec_xpath + "/elements/element[" + str(i_elem + 1) + "]"
+ elem_xpath = sec_xpath + \
+ "/elements/element[" + str(i_elem + 1) + "]"
elem_transf = Transformation()
elem_transf.get_cpacs_transf(tixi, elem_xpath)
@@ -145,9 +85,13 @@ def fuselage_size(cpacs_path):
prof_vect_z[:] = [z - 1 - prof_min_z for z in prof_vect_z]
if i_sec < len(pos_y_list):
- pos_y_list[i_sec] += ((1 + prof_min_y) * prof_size_y) * elem_transf.scaling.y
+ pos_y_list[i_sec] += (
+ (1 + prof_min_y) * prof_size_y
+ ) * elem_transf.scaling.y
if i_sec < len(pos_z_list):
- pos_z_list[i_sec] += ((1 + prof_min_z) * prof_size_z) * elem_transf.scaling.z
+ pos_z_list[i_sec] += (
+ (1 + prof_min_z) * prof_size_z
+ ) * elem_transf.scaling.z
body_frm_height = (
prof_size_z
@@ -193,98 +137,48 @@ def fuselage_size(cpacs_path):
return fuselage_maxlen, fuselage_minlen
-def wings_size(cpacs_path):
- tixi = open_tixi(cpacs_path)
+def wings_size(tixi: Tixi3) -> Tuple[float, float]:
+
if tixi.checkElement(WINGS_XPATH):
wing_cnt = tixi.getNamedChildrenCount(WINGS_XPATH, "wing")
- chord_list = []
-
- for i_wing in range(wing_cnt):
- wing_xpath = WINGS_XPATH + "/wing[" + str(i_wing + 1) + "]"
- wing_transf = Transformation()
- wing_transf.get_cpacs_transf(tixi, wing_xpath)
-
- # Positionings
- if tixi.checkElement(wing_xpath + "/positionings"):
- pos_cnt = tixi.getNamedChildrenCount(wing_xpath + "/positionings", "positioning")
-
- pos_x_list = []
- pos_y_list = []
- pos_z_list = []
- from_sec_list = []
- to_sec_list = []
-
- for i_pos in range(pos_cnt):
- pos_xpath = wing_xpath + "/positionings/positioning[" + str(i_pos + 1) + "]"
-
- length = tixi.getDoubleElement(pos_xpath + "/length")
- sweep_deg = tixi.getDoubleElement(pos_xpath + "/sweepAngle")
- sweep = math.radians(sweep_deg)
- dihedral_deg = tixi.getDoubleElement(pos_xpath + "/dihedralAngle")
- dihedral = math.radians(dihedral_deg)
-
- # Get the corresponding translation of each positioning
- pos_x_list.append(length * math.sin(sweep))
- pos_y_list.append(length * math.cos(dihedral) * math.cos(sweep))
- pos_z_list.append(length * math.sin(dihedral) * math.cos(sweep))
- # Get which section are connected by the positioning
- if tixi.checkElement(pos_xpath + "/fromSectionUID"):
- from_sec = tixi.getTextElement(pos_xpath + "/fromSectionUID")
- else:
- from_sec = ""
- from_sec_list.append(from_sec)
- if tixi.checkElement(pos_xpath + "/toSectionUID"):
- to_sec = tixi.getTextElement(pos_xpath + "/toSectionUID")
- else:
- to_sec = ""
- to_sec_list.append(to_sec)
-
- for j_pos in range(pos_cnt):
- if from_sec_list[j_pos] == "":
- prev_pos_x = 0
- prev_pos_y = 0
- prev_pos_z = 0
- elif from_sec_list[j_pos] == to_sec_list[j_pos - 1]:
- prev_pos_x = pos_x_list[j_pos - 1]
- prev_pos_y = pos_y_list[j_pos - 1]
- prev_pos_z = pos_z_list[j_pos - 1]
- else:
- index_prev = to_sec_list.index(from_sec_list[j_pos])
- prev_pos_x = pos_x_list[index_prev]
- prev_pos_y = pos_y_list[index_prev]
- prev_pos_z = pos_z_list[index_prev]
- pos_x_list[j_pos] += prev_pos_x
- pos_y_list[j_pos] += prev_pos_y
- pos_z_list[j_pos] += prev_pos_z
+ chord_list = []
- else:
- log.error('No "positionings" have been found!')
- pos_cnt = 0
+ for i_wing in range(wing_cnt):
+ wing_xpath = WINGS_XPATH + "/wing[" + str(i_wing + 1) + "]"
- # Sections
- sec_cnt = tixi.getNamedChildrenCount(wing_xpath + "/sections", "section")
+ # Sections
+ sec_cnt = tixi.getNamedChildrenCount(
+ wing_xpath + "/sections", "section")
- if pos_cnt == 0:
- pos_x_list = [0.0] * sec_cnt
- pos_y_list = [0.0] * sec_cnt
- pos_z_list = [0.0] * sec_cnt
+ for i_sec in range(sec_cnt):
+ sec_xpath = wing_xpath + \
+ "/sections/section[" + str(i_sec + 1) + "]"
- for i_sec in range(sec_cnt):
- sec_xpath = wing_xpath + "/sections/section[" + str(i_sec + 1) + "]"
+ sec_transf = Transformation()
+ sec_transf.get_cpacs_transf(tixi, sec_xpath)
- sec_transf = Transformation()
- sec_transf.get_cpacs_transf(tixi, sec_xpath)
+ chord_list.append(sec_transf.scaling.x)
+
+ ref_chord = sum(chord_list) / len(chord_list)
+
+ # Calculate mesh parameter from inputs and geometry
+ wing_maxlen = 0.15 * ref_chord
+ wing_minlen = 0.08 * wing_maxlen
- chord_list.append(sec_transf.scaling.x)
+ log.info(f"Wing maxlen={wing_maxlen:.3f} m")
+ log.info(f"Wing minlen={wing_minlen:.3f} m")
- ref_chord = sum(chord_list) / len(chord_list)
+ return wing_maxlen, wing_minlen
- # Calculate mesh parameter from inputs and geometry
- wing_maxlen = 0.15 * ref_chord
- wing_minlen = 0.08 * wing_maxlen
+ else:
+ ValueError(f"No wings found at {WINGS_XPATH}.")
+ return 0.0, 0.0
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
- log.info(f"Wing maxlen={wing_maxlen:.3f} m")
- log.info(f"Wing minlen={wing_minlen:.3f} m")
- return wing_maxlen, wing_minlen
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/CPACS2GMSH/func/rans_mesh_generator.py b/ceasiompy/CPACS2GMSH/func/rans_mesh_generator.py
new file mode 100644
index 000000000..3b8b27ad1
--- /dev/null
+++ b/ceasiompy/CPACS2GMSH/func/rans_mesh_generator.py
@@ -0,0 +1,311 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Use .brep files parts of an airplane to generate a fused airplane in GMSH with
+the OCC kernel. Then Spherical farfield is created around the airplane and the
+resulting domain is meshed using gmsh
+
+
+| Author: Guido Vallifuoco
+| Creation: 2024-02-01
+| Modified: Leon Deligny
+| Date: 2025-Feb-28
+
+TODO:
+
+ - It may be good to move all the function and some of the code in generategmsh()
+ that are related to disk actuator to another python script and import it here
+
+ - Add mesh sizing for each aircraft part and as consequence add marker
+
+ - Integrate other parts during fragmentation
+
+"""
+
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+import os
+
+import gmsh
+from ceasiompy.CPACS2GMSH.func.generategmesh import (
+ # duplicate_disk_actuator_surfaces,
+ # control_disk_actuator_normal,
+ # get_entities_from_volume,
+ fuselage_size,
+ process_gmsh_log,
+)
+from ceasiompy import log
+# from ceasiompy.utils.commonnames import (
+# ACTUATOR_DISK_OUTLET_SUFFIX,
+# ENGINE_EXHAUST_SUFFIX,
+# ENGINE_INTAKE_SUFFIX,
+# GMSH_ENGINE_CONFIG_NAME,
+# )
+from ceasiompy.utils.ceasiompyutils import (
+ bool_,
+ get_reasonable_nb_cpu,
+ get_part_type,
+ run_software,
+)
+from ceasiompy.CPACS2GMSH.func.utils import check_path, initialize_gmsh
+from pathlib import Path
+from ceasiompy.CPACS2GMSH.func.generategmesh import ModelPart
+from typing import Dict
+from cpacspy.cpacspy import CPACS
+
+# from ceasiompy.utils.commonxpath import GMSH_MESH_SIZE_WINGS_XPATH
+
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def generate_2d_mesh_for_pentagrow(
+ cpacs: CPACS,
+ brep_dir: Path,
+ results_dir: Path,
+ open_gmsh: bool,
+ min_max_mesh_factor,
+ symmetry: bool = False,
+ farfield_size_factor=10,
+ n_power_factor=2,
+ n_power_field=0.9,
+ fuselage_mesh_size_factor=1,
+ wing_mesh_size_factor=0.5,
+ mesh_size_engines: float = 0.23,
+ mesh_size_propellers: float = 0.23,
+ refine_factor: float = 2.0,
+ refine_truncated: bool = False,
+ auto_refine: bool = True,
+ testing_gmsh: bool = False,
+):
+ """
+ Returns a mesh file useful for pentagrow.
+ The airplane is fused with the different brep files: fuselage, wings etc,
+ in order to obtain a watertight volume.
+
+ Args:
+ cpacs (CPACS): CPACS file.
+ brep_dir (Path): Path to the directory containing the brep files.
+ results_dir (Path): Path to the directory containing the result mesh files.
+ open_gmsh (bool): Open gmsh GUI after the mesh generation.
+ symmetry (bool): Mesh will be generated with symmetry wrt to the x,z plane.
+
+ mesh_size_fuselage : float
+ Size of the fuselage mesh
+ mesh_size_wings : float
+ Size of the wing mesh
+ mesh_size_engines : float
+ Size of the engine mesh
+ mesh_size_propellers : float
+ Size of the propeller mesh
+ advance_mesh : bool
+ If set to true, the mesh will be generated with advanced meshing options
+ refine_factor : float
+ refine factor for the mesh le and te edge
+ refine_truncated : bool
+ If set to true, the refinement can change to match the truncated te thickness
+ auto_refine : bool
+ If set to true, the mesh will be checked for quality
+ testing_gmsh : bool
+ If set to true, the gmsh sessions will not be clear and killed at the end of
+ the function, this allow to test the gmsh feature after the call of generate_gmsh()
+
+ Returns:
+ Path: Path to the mesh file generated by gmsh.
+
+ """
+
+ tixi = cpacs.tixi
+
+ # Determine if rotor are present in the aircraft model
+ # TODO: add rotor model ?
+ # rotor_model = cfg_rotors(brep_dir)
+
+ # Retrieve all brep
+ brep_files = sorted(brep_dir.glob("*.brep"))
+
+ # Initialize gmsh
+ initialize_gmsh()
+
+ # Import each aircraft original parts / parent parts
+ fuselage_volume_dimtags = []
+ wings_volume_dimtags = []
+
+ # enginePylons_enginePylon_volume_dimtags = []
+ # engine_nacelle_fanCowl_volume_dimtags = []
+ # engine_nacelle_coreCowl_volume_dimtags = []
+ # vehicles_engines_engine_volume_dimtags = []
+ # vehicles_rotorcraft_model_rotors_rotor_volume_dimtags = []
+
+ log.info(f"Importing files from {brep_dir}")
+ for brep_file in brep_files:
+ # Import the part and create the aircraft part object
+ part_entities = gmsh.model.occ.importShapes(str(brep_file), highestDimOnly=False)
+ gmsh.model.occ.synchronize()
+
+ # Create the aircraft part object
+ part_obj = ModelPart(uid=brep_file.stem)
+ part_obj.part_type = get_part_type(cpacs.tixi, part_obj.uid)
+
+ if part_obj.part_type == "fuselage":
+ fuselage_volume_dimtags.append(part_entities[0])
+
+ elif part_obj.part_type == "wing":
+ wings_volume_dimtags.append(part_entities[0])
+
+ # elif part_obj.part_type == "enginePylons/enginePylon":
+ # enginePylons_enginePylon_volume_dimtags.append(part_entities[0])
+
+ # elif part_obj.part_type == "engine/nacelle/fanCowl":
+ # engine_nacelle_fanCowl_volume_dimtags.append(part_entities[0])
+
+ # elif part_obj.part_type == "engine/nacelle/coreCowl":
+ # engine_nacelle_coreCowl_volume_dimtags.append(part_entities[0])
+
+ # elif part_obj.part_type == "vehicles/engines/engine":
+ # vehicles_engines_engine_volume_dimtags.append(part_entities[0])
+
+ # elif part_obj.part_type == "vehicles/rotorcraft/model/rotors/rotor":
+ # vehicles_rotorcraft_model_rotors_rotor_volume_dimtags.append(part_entities[0])
+ # else:
+ # log.warning(f"'{brep_file}' cannot be categorized!")
+ # return None
+
+ gmsh.model.occ.synchronize()
+
+ log.info("Start manipulation to obtain a watertight volume.")
+ # we have to obtain a wathertight volume
+ gmsh.model.occ.cut(wings_volume_dimtags, fuselage_volume_dimtags, -1, True, False)
+
+ gmsh.model.occ.synchronize()
+
+ gmsh.model.occ.fuse(wings_volume_dimtags, fuselage_volume_dimtags, -1, True, True)
+
+ gmsh.model.occ.synchronize()
+
+ model_bb = gmsh.model.get_bounding_box(
+ fuselage_volume_dimtags[0][0], fuselage_volume_dimtags[0][1]
+ )
+
+ model_dimensions = [
+ abs(model_bb[0] - model_bb[3]),
+ abs(model_bb[1] - model_bb[4]),
+ abs(model_bb[2] - model_bb[5]),
+ ]
+
+ gmsh.model.occ.translate(
+ [(3, 1)],
+ -((model_bb[0]) + (model_dimensions[0] / 2)),
+ -(0), # the y coordinate is set to zero because sometimes (when act disk
+ # actuator is present) the coordinate of the model is not exact
+ -((model_bb[2]) + (model_dimensions[2] / 2)),
+ )
+
+ gmsh.model.occ.synchronize()
+ log.info("Manipulation finished.")
+
+ aircraft_surface_dimtags = gmsh.model.get_entities(2)
+ len_aircraft_surface = len(aircraft_surface_dimtags)
+ surface = []
+
+ for i in range(len_aircraft_surface):
+ tags = aircraft_surface_dimtags[i][1]
+ surface.append(tags)
+
+ gmsh.model.add_physical_group(2, surface, -1, name="aircraft_surface")
+
+ # Mesh generation
+ log.info("Start of gmsh 2D surface meshing process.")
+
+ gmsh.option.setNumber("Mesh.Algorithm", 6)
+ gmsh.option.setNumber("Mesh.LcIntegrationPrecision", 1e-6)
+ mesh_size = model_dimensions[0] * float(min_max_mesh_factor) * (10**-3)
+ gmsh.option.set_number("Mesh.MeshSizeMin", mesh_size)
+ gmsh.option.set_number("Mesh.MeshSizeMax", mesh_size)
+ gmsh.option.setNumber("Mesh.StlOneSolidPerSurface", 2)
+
+ gmsh.model.occ.synchronize()
+ gmsh.logger.start()
+ gmsh.model.mesh.generate(1)
+ gmsh.model.mesh.generate(2)
+
+ if bool_(open_gmsh):
+ log.info("Result of 2D surface mesh.")
+ log.info("GMSH GUI is open, close it to continue...")
+ gmsh.fltk.run()
+
+ gmsh.model.occ.synchronize()
+
+ # Add GMSH path
+ gmesh_path = Path(results_dir, "surface_mesh.stl")
+ gmsh.write(str(gmesh_path))
+
+ process_gmsh_log(gmsh.logger.get())
+
+ fuselage_maxlen, _ = fuselage_size(tixi)
+
+ return gmesh_path, fuselage_maxlen
+
+
+def pentagrow_3d_mesh(
+ result_dir: str,
+ cfg_params: Dict,
+ surf: str = None,
+ angle: str = None,
+) -> Path:
+ """
+ Runs pentagrow.
+
+ Args:
+ result_dir (str): Results directory.
+ cfg_params (Dict): Configuration parameters for ConfigFile.
+
+ """
+
+ # Create the config file for pentagrow
+ config_penta_path = Path(result_dir, "config.cfg")
+
+ # Add cfg_params in config file
+ with open(config_penta_path, "w") as file:
+ for key, value in cfg_params.items():
+ file.write(f"{key} = {value}\n")
+
+ os.chdir("Results/GMSH")
+
+ check_path("surface_mesh.stl")
+ check_path("config.cfg")
+
+ current_dir = os.getcwd()
+ os.chdir(current_dir)
+
+ command = ["surface_mesh.stl", "config.cfg"]
+
+ # Specify the file path
+ file_path = "command.txt"
+
+ with open(file_path, "w") as file:
+ file.write(" ".join(command))
+
+ # Running command = "pentagrow surface_mesh.stl config.cfg"
+ run_software(
+ software_name="pentagrow",
+ arguments=command,
+ wkdir=current_dir,
+ with_mpi=False,
+ nb_cpu=get_reasonable_nb_cpu(),
+ )
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/CPACS2GMSH/func/utils.py b/ceasiompy/CPACS2GMSH/func/utils.py
new file mode 100644
index 000000000..56b060568
--- /dev/null
+++ b/ceasiompy/CPACS2GMSH/func/utils.py
@@ -0,0 +1,300 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed for CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Functions and constants for CPACS2GMSH module.
+
+| Author : Leon Deligny
+| Creation: 2025-Feb-25
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+import os
+import gmsh
+
+import numpy as np
+
+from cpacspy.cpacsfunctions import get_value
+from ceasiompy.utils.ceasiompyutils import bool_
+
+from typing import Dict
+from pathlib import Path
+from tixi3.tixi3wrapper import Tixi3
+from ceasiompy.utils.configfiles import ConfigFile
+
+from ceasiompy import log
+
+from ceasiompy.utils.commonxpath import (
+ GMSH_AUTO_REFINE_XPATH,
+ GMSH_EXHAUST_PERCENT_XPATH,
+ GMSH_FARFIELD_FACTOR_XPATH,
+ GMSH_N_POWER_FACTOR_XPATH,
+ GMSH_N_POWER_FIELD_XPATH,
+ GMSH_INTAKE_PERCENT_XPATH,
+ GMSH_MESH_SIZE_FARFIELD_XPATH,
+ GMSH_MESH_SIZE_FACTOR_FUSELAGE_XPATH,
+ GMSH_MESH_SIZE_FACTOR_WINGS_XPATH,
+ GMSH_MESH_SIZE_ENGINES_XPATH,
+ GMSH_MESH_SIZE_PROPELLERS_XPATH,
+ GMSH_OPEN_GUI_XPATH,
+ GMSH_REFINE_FACTOR_XPATH,
+ GMSH_REFINE_TRUNCATED_XPATH,
+ GMSH_SYMMETRY_XPATH,
+ GMSH_MESH_TYPE_XPATH,
+ GMSH_NUMBER_LAYER_XPATH,
+ GMSH_H_FIRST_LAYER_XPATH,
+ GMSH_MAX_THICKNESS_LAYER_XPATH,
+ GMSH_GROWTH_FACTOR_XPATH,
+ GMSH_GROWTH_RATIO_XPATH,
+ GMSH_SURFACE_MESH_SIZE_XPATH,
+ GMSH_FEATURE_ANGLE_XPATH,
+)
+
+
+# =================================================================================================
+# CONSTANTS
+# =================================================================================================
+
+# Define mesh color for GMSH, only use in GUI (red, green, blue, brightness)
+MESH_COLORS = {
+ "farfield": (255, 200, 0, 100),
+ "symmetry": (153, 255, 255, 100),
+ "wing": (0, 200, 200, 100),
+ "fuselage": (255, 215, 0, 100),
+ "pylon": (255, 15, 255, 100),
+ "engine": (127, 0, 255, 100),
+ "rotor": (0, 0, 0, 100),
+ "bad_surface": (255, 0, 0, 255),
+ "good_surface": (0, 255, 0, 100),
+}
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def cfg_rotors(brep_dir: Path) -> bool:
+ rotor_model = False
+ if Path(brep_dir, "config_rotors.cfg").exists():
+ rotor_model = True
+
+ log.info("Adding disk actuator")
+ config_file = ConfigFile(Path(brep_dir, "config_rotors.cfg"))
+
+ add_disk_actuator(brep_dir, config_file)
+
+ return rotor_model
+
+
+def add_disk_actuator(brep_dir: Path, config_file: ConfigFile):
+ """
+ Creates a 2D disk in a given location to represent a rotor as a disk actuator.
+
+ Args:
+ brep_dir (Path): brep files of the aircraft that contains the rotor config file.
+ config_file (Configfile): Propellers configuration on the aircraft.
+
+ """
+
+ nb_rotor = int(config_file["NB_ROTOR"])
+
+ for k in range(1, nb_rotor + 1):
+ # get the rotor configuration from cfg file
+ rotor_uid = config_file[f"UID_{k}"]
+ radius = float(config_file[f"{rotor_uid}_ROTOR_RADIUS"])
+ sym = int(config_file[f"{rotor_uid}_SYMMETRIC"])
+ trans_vector = (
+ float(config_file[f"{rotor_uid}_TRANS_X"]),
+ float(config_file[f"{rotor_uid}_TRANS_Y"]),
+ float(config_file[f"{rotor_uid}_TRANS_Z"]),
+ )
+ rot_vector = (
+ float(config_file[f"{rotor_uid}_ROT_X"]),
+ float(config_file[f"{rotor_uid}_ROT_Y"]),
+ float(config_file[f"{rotor_uid}_ROT_Z"]),
+ )
+
+ # Adding rotating disk
+ gmsh.initialize()
+ # generate the inlet_disk (gmsh always create a disk in the xy plane)
+ disk_tag = gmsh.model.occ.addDisk(*trans_vector, radius, radius)
+ disk_dimtag = (2, disk_tag)
+
+ # y axis 180deg flip to make the inlet of the disk face forward
+ gmsh.model.occ.rotate([disk_dimtag], *trans_vector, 0, 1, 0, np.radians(180))
+ gmsh.model.occ.synchronize()
+
+ # rotation given in the cpacs file
+ gmsh.model.occ.rotate([disk_dimtag], *trans_vector, 1, 0, 0, np.radians(rot_vector[0]))
+ # y axis
+ gmsh.model.occ.rotate([disk_dimtag], *trans_vector, 0, 1, 0, np.radians(rot_vector[1]))
+ # z axis
+ gmsh.model.occ.rotate([disk_dimtag], *trans_vector, 0, 0, 1, np.radians(rot_vector[2]))
+
+ gmsh.model.occ.synchronize()
+
+ path_disk = Path(brep_dir, f"{rotor_uid}.brep")
+ gmsh.write(str(path_disk))
+
+ gmsh.clear()
+ gmsh.finalize()
+
+ if sym == 2:
+ # Adding the symmetric
+ gmsh.initialize()
+ # generate the inlet_disk (gmsh always create a disk in the xy plane)
+ disk_tag = gmsh.model.occ.addDisk(*trans_vector, radius, radius)
+ disk_dimtag = (2, disk_tag)
+
+ # y axis 180deg flip to make the inlet of the disk face forward is not necessary for
+ # the mirrored part, and for now the symmetry is not implemented correctly since
+ # the symmetry does not take into account the orientation of the rotor and the plane
+ # of symmetry is assume to be the xz plane
+ # When the face of the disk actuator are not oriented well the simulation shows
+ # increasing cd and will probably diverge
+
+ # rotation given in the cpacs file
+ gmsh.model.occ.rotate([disk_dimtag], *trans_vector, 1, 0, 0, np.radians(rot_vector[0]))
+ # y axis
+ gmsh.model.occ.rotate([disk_dimtag], *trans_vector, 0, 1, 0, np.radians(rot_vector[1]))
+ # z axis
+ gmsh.model.occ.rotate([disk_dimtag], *trans_vector, 0, 0, 1, np.radians(rot_vector[2]))
+
+ gmsh.model.occ.synchronize()
+
+ gmsh.model.occ.mirror([disk_dimtag], 0, 1, 0, 0)
+ gmsh.model.occ.synchronize()
+ path_disk = Path(brep_dir, f"{rotor_uid}_mirrored.brep")
+ gmsh.write(str(path_disk))
+
+ gmsh.clear()
+ gmsh.finalize()
+
+
+def write_gmsh(results_dir: str, file: str) -> Path:
+ su2mesh_path = Path(results_dir, file)
+ gmsh.write(str(su2mesh_path))
+ return Path(su2mesh_path)
+
+
+def initialize_gmsh():
+ # Initialize gmsh
+ gmsh.initialize()
+ # Stop gmsh output log in the terminal
+ gmsh.option.setNumber("General.Terminal", 0)
+ # Log complexity
+ gmsh.option.setNumber("General.Verbosity", 5)
+
+
+def check_path(file: str):
+ if os.path.exists(file):
+ log.info(f"{file} exists")
+ else:
+ log.warning(f"{file} does not exist")
+
+
+def load_rans_cgf_params(
+ fuselage_maxlen: float,
+ farfield_factor: float,
+ n_layer: float,
+ h_first_layer: float,
+ max_layer_thickness: float,
+ growth_factor: float,
+ growth_ratio: float,
+ feature_angle: float,
+) -> Dict:
+
+ InitialHeight = h_first_layer * (10**-5)
+ MaxLayerThickness = max_layer_thickness / 10
+ FarfieldRadius = fuselage_maxlen * farfield_factor * 100
+ HeightIterations = 8
+ NormalIterations = 8
+ MaxCritIterations = 128
+ LaplaceIterations = 8
+
+ return {
+ "InputFormat": "stl",
+ "NLayers": n_layer,
+ "FeatureAngle": feature_angle,
+ "InitialHeight": InitialHeight,
+ "MaxGrowthRatio": growth_ratio,
+ "MaxLayerThickness": MaxLayerThickness,
+ "FarfieldRadius": FarfieldRadius,
+ "OutputFormat": "su2", # fixed
+ "HolePosition": "0.0 0.0 0.0",
+ "FarfieldCenter": "0.0 0.0 0.0",
+ "TetgenOptions": "-pq1.3VY",
+ "TetGrowthFactor": growth_factor,
+ "HeightIterations": HeightIterations,
+ "NormalIterations": NormalIterations,
+ "MaxCritIterations": MaxCritIterations,
+ "LaplaceIterations": LaplaceIterations,
+ }
+
+
+def retrieve_gui_values(tixi: Tixi3):
+ """
+ Returns input values from CEASIOMpy's GUI interface.
+ """
+
+ # Retrieve value from the GUI Setting
+ open_gmsh = bool_(get_value(tixi, GMSH_OPEN_GUI_XPATH))
+ type_mesh = get_value(tixi, GMSH_MESH_TYPE_XPATH)
+ symmetry = get_value(tixi, GMSH_SYMMETRY_XPATH)
+
+ farfield_factor = get_value(tixi, GMSH_FARFIELD_FACTOR_XPATH)
+ farfield_size_factor = get_value(tixi, GMSH_MESH_SIZE_FARFIELD_XPATH)
+
+ n_power_factor = get_value(tixi, GMSH_N_POWER_FACTOR_XPATH)
+ n_power_field = get_value(tixi, GMSH_N_POWER_FIELD_XPATH)
+
+ fuselage_mesh_size_factor = get_value(tixi, GMSH_MESH_SIZE_FACTOR_FUSELAGE_XPATH)
+ wing_mesh_size_factor = get_value(tixi, GMSH_MESH_SIZE_FACTOR_WINGS_XPATH)
+
+ mesh_size_engines = get_value(tixi, GMSH_MESH_SIZE_ENGINES_XPATH)
+ mesh_size_propellers = get_value(tixi, GMSH_MESH_SIZE_PROPELLERS_XPATH)
+
+ refine_factor = get_value(tixi, GMSH_REFINE_FACTOR_XPATH)
+ refine_truncated = get_value(tixi, GMSH_REFINE_TRUNCATED_XPATH)
+ auto_refine = get_value(tixi, GMSH_AUTO_REFINE_XPATH)
+
+ intake_percent = get_value(tixi, GMSH_INTAKE_PERCENT_XPATH)
+ exhaust_percent = get_value(tixi, GMSH_EXHAUST_PERCENT_XPATH)
+
+ n_layer = get_value(tixi, GMSH_NUMBER_LAYER_XPATH)
+ h_first_layer = get_value(tixi, GMSH_H_FIRST_LAYER_XPATH)
+ max_layer_thickness = get_value(tixi, GMSH_MAX_THICKNESS_LAYER_XPATH)
+
+ growth_factor = get_value(tixi, GMSH_GROWTH_FACTOR_XPATH)
+ growth_ratio = get_value(tixi, GMSH_GROWTH_RATIO_XPATH)
+
+ min_max_mesh_factor = get_value(tixi, GMSH_SURFACE_MESH_SIZE_XPATH)
+
+ feature_angle = get_value(tixi, GMSH_FEATURE_ANGLE_XPATH)
+
+ return (
+ open_gmsh, type_mesh, symmetry,
+ farfield_factor, farfield_size_factor,
+ n_power_factor, n_power_field,
+ fuselage_mesh_size_factor, wing_mesh_size_factor,
+ mesh_size_engines, mesh_size_propellers,
+ refine_factor, refine_truncated, auto_refine,
+ intake_percent, exhaust_percent,
+ n_layer, h_first_layer, max_layer_thickness,
+ growth_factor, growth_ratio,
+ min_max_mesh_factor, feature_angle,
+
+ )
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/CPACS2GMSH/func/wingclassification.py b/ceasiompy/CPACS2GMSH/func/wingclassification.py
index 9e4f6039d..9923e2374 100644
--- a/ceasiompy/CPACS2GMSH/func/wingclassification.py
+++ b/ceasiompy/CPACS2GMSH/func/wingclassification.py
@@ -5,7 +5,6 @@
This script contains different functions to classify and manipulate wing elements
-Python version: >=3.8
| Author: Tony Govoni
| Creation: 2022-04-05
@@ -17,7 +16,6 @@
function detect_normal_profile() in order to prevent this.
"""
-
# =================================================================================================
# IMPORTS
# =================================================================================================
@@ -25,12 +23,156 @@
import gmsh
import numpy as np
+from typing import List, Dict, Tuple
+
+from ceasiompy import log
+
+
+# =================================================================================================
+# CLASSES
+# =================================================================================================
+
+
+class ModelPart:
+ """
+ A class to represent part of the aircraft or other part of the gmsh model
+ in order to classify its entities and dimension tags
+ ...
+
+ Attributes
+ ----------
+ uid : str
+ name of the part which correspond to its .brep file name for aircraft parts
+ or a simple name describing the part function in the model
+
+ """
+
+ def __init__(self, uid):
+ self.uid = uid
+ self.part_type = ""
+
+ # dimtag
+ self.points = []
+ self.lines = []
+ self.surfaces = []
+ self.volume = []
+ # tag only
+ self.points_tags = []
+ self.lines_tags = []
+ self.surfaces_tags = []
+ self.volume_tag = []
+ # children
+ self.children_dimtag = set()
+ # Physical groups
+ self.physical_groups = []
+
+ self.mesh_size = None
+
+ self.wing_sections = []
+
+ def associate_child_to_parent(self, child_dimtag):
+ """
+ Function to associate a child to a parent.
+ all the entities belonging to a child (volume generated by the fragment operation)
+ are associated to their parent part (volume before the fragment operation)
+
+ Args:
+ ----------
+ child_dimtag : tuple (dim,tag)
+ dimtag of the child volume
+ """
+
+ # Detect 2D or 3D child
+ if child_dimtag[0] == 3:
+ # Associate a 3D the child to the parent
+ child_volume = [child_dimtag]
+
+ child_surfaces, child_lines, child_points = get_entities_from_volume(child_volume)
+
+ # Get the dimtags (3D only)
+ child_volume_tag = [dimtag[1] for dimtag in child_volume]
+ # Store in parent parts for latter use (3D only)
+ self.volume.extend(child_volume)
+ self.volume_tag.extend(child_volume_tag)
+
+ # 2D child and parent
+ elif child_dimtag[0] == 2:
+ child_surfaces = [child_dimtag]
+ child_lines = sorted(
+ gmsh.model.getBoundary(
+ child_surfaces, combined=True, oriented=False, recursive=False
+ )
+ )
+
+ child_points = list(
+ gmsh.model.getBoundary(
+ child_surfaces, combined=True, oriented=False, recursive=True
+ )
+ )
+ child_points.sort()
+
+ elif child_dimtag[0] != 2 and child_dimtag[0] != 3:
+ raise ValueError("Dimension of the child is not 2 or 3")
+
+ # Get the dimtags
+ child_surfaces_tags = [dimtag[1] for dimtag in child_surfaces]
+ child_lines_tags = [dimtag[1] for dimtag in child_lines]
+ child_points_tags = [dimtag[1] for dimtag in child_points]
+
+ # Store in parent parts for latter use
+ self.points.extend(child_points)
+ self.lines.extend(child_lines)
+ self.surfaces.extend(child_surfaces)
+ self.points_tags.extend(child_points_tags)
+ self.lines_tags.extend(child_lines_tags)
+ self.surfaces_tags.extend(child_surfaces_tags)
+
+ def clean_inside_entities(self, final_domain):
+ """
+ Function to clean inside entities of the part.
+ Inside entities are entities that are not part of the final domain.
+
+ Args:
+ ----------
+ final_domain : ModelPart
+ final_domain part
+ """
+ if self.part_type == "rotor":
+ # Detect all the entities in the domain with gmsh functions
+ self.surfaces = sorted(
+ list(set(self.surfaces).intersection(set(gmsh.model.getEntities(dim=2))))
+ )
+ self.lines = sorted(
+ list(set(self.lines).intersection(set(gmsh.model.getEntities(dim=1))))
+ )
+ self.points = sorted(
+ list(set(self.points).intersection(set(gmsh.model.getEntities(dim=0))))
+ )
+
+ # Update the dimtags
+ self.surfaces_tags = [dimtag[1] for dimtag in self.surfaces]
+ self.lines_tags = [dimtag[1] for dimtag in self.lines]
+ self.points_tags = [dimtag[1] for dimtag in self.points]
+ return
+
+ # if not rotor part
+ # Detect only shared entities with the final domain
+ self.surfaces = sorted(list(set(self.surfaces).intersection(set(final_domain.surfaces))))
+ self.lines = sorted(list(set(self.lines).intersection(set(final_domain.lines))))
+ self.points = sorted(list(set(self.points).intersection(set(final_domain.points))))
+
+ # Update the dimtags
+ self.surfaces_tags = sorted([dimtag[1] for dimtag in self.surfaces])
+ self.lines_tags = sorted([dimtag[1] for dimtag in self.lines])
+ self.points_tags = sorted([dimtag[1] for dimtag in self.points])
+
+
# =================================================================================================
# FUNCTIONS
# =================================================================================================
-def detect_normal_profile(le_te_pair, line_comp1, line_comp2):
+def detect_normal_profile(le_te_pair: List, line_comp1, line_comp2):
"""
Function to detect leading and trailing edge lines for normal profile (not truncated)
le/te lines are linked by the two same surfaces
@@ -76,41 +218,32 @@ def detect_normal_profile(le_te_pair, line_comp1, line_comp2):
return le_te_pair, True
-def detect_truncated_profile(le_te_pair, line_comp1, line_comp2, line_comp3):
+def detect_truncated_profile(
+ le_te_pair: List,
+ line_comp1: Dict,
+ line_comp2: Dict,
+ line_comp3: Dict,
+) -> Tuple[List, bool]:
"""
Function to detect leading and trailing edge lines for truncated profile
le/te lines are linked by shared surfaces
- ...
-
- Args:
- ----------
- le_te_pair : list
- list of all the previously found pair of le/te lines
- line_comp1 : dict
- dictionary containing a line and its surfaces
- line_comp2 : dict
- dictionary containing a line and its surfaces
- ...
-
- Returns:
- ----------
- True : if le/te pair found
- False : otherwise
"""
- lines = sorted(
- list(set([line_comp1["line_tag"], line_comp2["line_tag"], line_comp3["line_tag"]]))
- )
-
- # Check if the line are not the same
-
+ # Get unique sorted line tages
+ lines = sorted(list(set([
+ line_comp1["line_tag"],
+ line_comp2["line_tag"],
+ line_comp3["line_tag"],
+ ])))
+
+ # Check if the 3 lines are distinct
if len(lines) != 3:
return le_te_pair, False
- # Check if lines are not already in the founded list
+ # Check if lines are already in le_te_pair
if lines in le_te_pair:
return le_te_pair, False
- # Check if each line share only one common surface with the other two lines
+ # Check surface sharing between each lines
if len(line_comp1["surf_tags"].intersection(line_comp2["surf_tags"])) != 1:
return le_te_pair, False
if len(line_comp2["surf_tags"].intersection(line_comp3["surf_tags"])) != 1:
@@ -138,48 +271,40 @@ def detect_truncated_profile(le_te_pair, line_comp1, line_comp2, line_comp3):
return le_te_pair, True
-def find_chord_length(le_te_pair):
+def find_chord_length(le_te_pair: List):
"""
Function to find the chord length of the wing section based on the le/te pair
- ...
-
- Args:
- ----------
- le_te_pair : list (tag)
- pair of le/te lines
- ...
-
- Returns:
- ----------
- chord_length : float
- chord length
"""
- chord_length = 0
-
if len(le_te_pair) == 2:
# sharpe profile
x1, y1, z1 = gmsh.model.occ.getCenterOfMass(1, le_te_pair[0])
x2, y2, z2 = gmsh.model.occ.getCenterOfMass(1, le_te_pair[1])
chord_length = np.linalg.norm([x2 - x1, y2 - y1, z2 - z1])
- else:
- # truncated profile
- x1, y1, z1 = gmsh.model.occ.getCenterOfMass(1, le_te_pair[0])
- x2, y2, z2 = gmsh.model.occ.getCenterOfMass(1, le_te_pair[1])
- x3, y3, z3 = gmsh.model.occ.getCenterOfMass(1, le_te_pair[2])
-
- # Assuming the distance between the 2 trailing edge points is smaller than the chord length
- d12 = np.linalg.norm([x2 - x1, y2 - y1, z2 - z1])
- d13 = np.linalg.norm([x3 - x1, y3 - y1, z3 - z1])
- d23 = np.linalg.norm([x3 - x2, y3 - y2, z3 - z2])
-
- # The two trailing edge lines are the closest together
- chord_length = max([d12, d23, d13])
+ log.info(f"Using sharpe profile with computed {chord_length}.")
+ return chord_length
+
+ # truncated profile
+ x1, y1, z1 = gmsh.model.occ.getCenterOfMass(1, le_te_pair[0])
+ x2, y2, z2 = gmsh.model.occ.getCenterOfMass(1, le_te_pair[1])
+ x3, y3, z3 = gmsh.model.occ.getCenterOfMass(1, le_te_pair[2])
+
+ # Assuming the distance between the 2 trailing edge points is smaller than the chord length
+ d12 = np.linalg.norm([x2 - x1, y2 - y1, z2 - z1])
+ d13 = np.linalg.norm([x3 - x1, y3 - y1, z3 - z1])
+ d23 = np.linalg.norm([x3 - x2, y3 - y2, z3 - z2])
+
+ # The two trailing edge lines are the closest together
+ chord_length = max([d12, d23, d13])
+ log.info(
+ f"Using truncated profile with computed {chord_length} "
+ f"with length le_te_pair of {le_te_pair}."
+ )
return chord_length
-def exclude_lines(wing_part, aircraft_parts):
+def exclude_lines(wing_part: ModelPart, aircraft_parts: List) -> List:
"""
Function to exclude lines from the wing part that are common with the other aircraft_parts.
@@ -205,17 +330,63 @@ def exclude_lines(wing_part, aircraft_parts):
return list(set(wing_part.lines) - set(other_lines))
-def classify_wing(wing_part, aircraft_parts):
+def get_entities_from_volume(volume_dimtag):
"""
- Function to classify the leading and trailing edge of the wing
+ Function to get the entities belonging to a volume.
+ Surfaces and lines are found with the gmsh.model.getBoundary() function.
+ Points are found with the gmsh.model.getEntities() function using recursive set to True.
+ This choice seems the most efficient and robust at the state of development of Gmsh
Args:
----------
- wing_part : ModelPart
- aircraft part to classify
+ volume_dimtag : list
+ a list containing the dimtag of the volume [(dim,tag)]
+ which is a standard input format for other gmsh function
+ ...
- aircraft_parts : list(ModelPart)
- parts of the aircraft
+ Returns:
+ ----------
+ surfaces_dimtags : list(tuple)
+ a list of tuples containing the dimtag of the surfaces
+ lines_dimtags : list(tuple)
+ a list of tuples containing the dimtag of the lines
+ points_dimtags : list(tuple)
+ a list of tuples containing the dimtag of the points
+ """
+
+ surfaces_dimtags = gmsh.model.getBoundary(
+ volume_dimtag, combined=True, oriented=False, recursive=False
+ )
+
+ lines_dimtags = sorted(
+ set().union(
+ *[
+ gmsh.model.getBoundary([surface], combined=True, oriented=False, recursive=False)
+ for surface in surfaces_dimtags
+ ]
+ )
+ )
+
+ points_dimtags = list(
+ set().union(
+ *[
+ gmsh.model.getBoundary([surface], combined=True, oriented=False, recursive=True)
+ for surface in surfaces_dimtags
+ ]
+ )
+ )
+ points_dimtags.sort()
+
+ return surfaces_dimtags, lines_dimtags, points_dimtags
+
+
+def classify_wing(wing_part: ModelPart, aircraft_parts: List) -> None:
+ """
+ Function to classify the leading and trailing edge of the wing.
+
+ Args:
+ wing_part (ModelPart): Wing part to classify/order.
+ aircraft_parts (List(ModelPart)): Parts of the aircraft
"""
@@ -247,11 +418,10 @@ def classify_wing(wing_part, aircraft_parts):
{"lines_tags": le_te, "mean_chord": find_chord_length(le_te)}
)
-
# =================================================================================================
# MAIN
# =================================================================================================
-if __name__ == "__main__":
- print("Nothing to execute!")
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/CPACS2GMSH/tests/ToolInput/SimpleNacelle_centerCowl.brep b/ceasiompy/CPACS2GMSH/tests/ToolInput/SimpleNacelle_centerCowl.brep
index b47a90fe0..1a8ff7eed 100644
--- a/ceasiompy/CPACS2GMSH/tests/ToolInput/SimpleNacelle_centerCowl.brep
+++ b/ceasiompy/CPACS2GMSH/tests/ToolInput/SimpleNacelle_centerCowl.brep
@@ -3,22 +3,22 @@ DBRep_DrawableShape
CASCADE Topology V1, (c) Matra-Datavision
Locations 0
Curve2ds 4
-1 0 0 1 0
-1 0 1 1 0
-1 6.2831853071795862 0 0 1
-1 0 0 0 1
+1 0 0 1 0
+1 0 1 1 0
+1 6.2831853071795862 0 0 1
+1 0 0 0 1
Curves 2
2 1 0 0 1 0 0 0 -1 0 0 0 -1 0.0012600000000000001
-7 0 0 3 81 79 1 -0.0012600000000000001 0 0.99166806910433225 -0.0024290474198804652 0 0.97916907934632713 -0.0041556686870856405 0 0.9625013807263838 -0.0064065754633828211 0 0.95000132931521664 -0.0080695147108952852 0 0.93750127960794405 -0.009707852209573575 0 0.92500124260478966 -0.011322315812076994 0 0.91250118837727034 -0.012912930886184051 0 0.90000115960536642 -0.014480856552602568 0 0.88750112337475495 -0.016025977431048548 0 0.87500108849312463 -0.01754880638747152 0 0.86250106246199998 -0.019049805988652704 0 0.85000102944828482 -0.020529010547511328 0 0.83750100916791592 -0.021987024618123435 0 0.82500098575434166 -0.023423790211651842 0 0.81250096307329922 -0.024839539588589164 0 0.80000095229575618 -0.02623460040061484 0 0.7875009305699 -0.027608627134194662 0 0.77500092060707626 -0.028962080624074963 0 0.76250090701572382 -0.030294657285077038 0 0.75000090093676053 -0.031606513785123712 0 0.73750089105646288 -0.032897325203243725 0 0.72500088155224662 -0.034167037781514191 0 0.71250087862114031 -0.035415590209551026 0 0.70000087895100138 -0.036642679019159449 0 0.68750087227697532 -0.037847781327729632 0 0.6750008748552182 -0.039030894820797343 0 0.66250087359381438 -0.04019134768145604 0 0.65000087480075985 -0.04132883243621395 0 0.63750087815684497 -0.042442849673491136 0 0.625000877465687 -0.043532704745740072 0 0.61250088448934137 -0.044598076807669175 0 0.60000088431277843 -0.04563794173852994 0 0.58750089092946689 -0.046651919785977214 0 0.57500089296569545 -0.047638951624771643 0 0.5625008930402241 -0.048598256747966771 0 0.5500009010820599 -0.049529215643651064 0 0.53750089643319277 -0.050430285415768737 0 0.52500090129826238 -0.051301060818450712 0 0.51250089779487273 -0.052139902072950384 0 0.50000089749479981 -0.052945976246431804 0 0.48750088860344998 -0.053717653632727083 0 0.47500087974225197 -0.054453887628772561 0 0.4625008713526475 -0.055153292696117018 0 0.45000085526456401 -0.055814057545055237 0 0.43750083301262427 -0.056434614408511809 0 0.42500080860956885 -0.057013445108689248 0 0.41250077772299104 -0.057548589221626589 0 0.40000073803096653 -0.058038007077278866 0 0.38750069220288164 -0.058479817687152012 0 0.37500063518954613 -0.058871583983993149 0 0.3625005686566567 -0.059211135066700046 0 0.3500004886680172 -0.059495790765773687 0 0.33750039371847834 -0.059722842002354941 0 0.32500028290526284 -0.059889604063107046 0 0.31250015190983488 -0.059992723391208563 0 0.29999999813430406 -0.060029097107844702 0 0.2874998173873341 -0.059994687799052197 0 0.27499960455498923 -0.059885944767455929 0 0.26249935422640547 -0.059698303969986058 0 0.24999905865034713 -0.059427166686205624 0 0.23749870917010102 -0.059067284443917699 0 0.22499829176985606 -0.058613039953011835 0 0.21249779146755365 -0.058057937119780946 0 0.19999719409576988 -0.057394559096993807 0 0.18749645845558685 -0.056615244552909234 0 0.17499556762814103 -0.055710017225365414 0 0.16249444360960999 -0.054668793588188679 0 0.14999303957538462 -0.053478201474552511 0 0.13749122200948111 -0.052123698918757067 0 0.12498884671342025 -0.050586635395975146 0 0.11248561848475973 -0.048844854472586741 0 0.099981138068765518 -0.046869505690216813 0 0.087474479216191003 -0.044623807360225462 0 0.074964621118000355 -0.04205569685918592 0 0.062446492760321902 -0.039098544280603768 0 0.049918541725142838 -0.035624947347748813 0 0.037328260760967027 -0.031515506079397577 0 0.019422298161866122 -0.023907069430545717 0 0.0056742009741973755 -0.01660351401419858 0 0 0 0
+7 0 0 3 81 79 1 -0.0012600000000000001 0 0.99166806910433225 -0.0024290474198804652 0 0.97916907934632713 -0.0041556686870856405 0 0.9625013807263838 -0.0064065754633828211 0 0.95000132931521664 -0.0080695147108952852 0 0.93750127960794405 -0.009707852209573575 0 0.92500124260478966 -0.011322315812076994 0 0.91250118837727034 -0.012912930886184051 0 0.90000115960536642 -0.014480856552602568 0 0.88750112337475495 -0.016025977431048548 0 0.87500108849312463 -0.01754880638747152 0 0.86250106246199998 -0.019049805988652704 0 0.85000102944828482 -0.020529010547511328 0 0.83750100916791592 -0.021987024618123435 0 0.82500098575434166 -0.023423790211651842 0 0.81250096307329922 -0.024839539588589164 0 0.80000095229575618 -0.02623460040061484 0 0.7875009305699 -0.027608627134194662 0 0.77500092060707626 -0.028962080624074963 0 0.76250090701572382 -0.030294657285077038 0 0.75000090093676053 -0.031606513785123712 0 0.73750089105646288 -0.032897325203243725 0 0.72500088155224662 -0.034167037781514191 0 0.71250087862114031 -0.035415590209551026 0 0.70000087895100138 -0.036642679019159449 0 0.68750087227697532 -0.037847781327729632 0 0.6750008748552182 -0.039030894820797343 0 0.66250087359381438 -0.04019134768145604 0 0.65000087480075985 -0.04132883243621395 0 0.63750087815684497 -0.042442849673491136 0 0.625000877465687 -0.043532704745740072 0 0.61250088448934137 -0.044598076807669175 0 0.60000088431277843 -0.04563794173852994 0 0.58750089092946689 -0.046651919785977214 0 0.57500089296569545 -0.047638951624771643 0 0.5625008930402241 -0.048598256747966771 0 0.5500009010820599 -0.049529215643651064 0 0.53750089643319277 -0.050430285415768737 0 0.52500090129826238 -0.051301060818450712 0 0.51250089779487273 -0.052139902072950384 0 0.50000089749479981 -0.052945976246431804 0 0.48750088860344998 -0.053717653632727083 0 0.47500087974225197 -0.054453887628772561 0 0.4625008713526475 -0.055153292696117018 0 0.45000085526456401 -0.055814057545055237 0 0.43750083301262427 -0.056434614408511809 0 0.42500080860956885 -0.057013445108689248 0 0.41250077772299104 -0.057548589221626589 0 0.40000073803096653 -0.058038007077278866 0 0.38750069220288164 -0.058479817687152012 0 0.37500063518954613 -0.058871583983993149 0 0.3625005686566567 -0.059211135066700046 0 0.3500004886680172 -0.059495790765773687 0 0.33750039371847834 -0.059722842002354941 0 0.32500028290526284 -0.059889604063107046 0 0.31250015190983488 -0.059992723391208563 0 0.29999999813430406 -0.060029097107844702 0 0.2874998173873341 -0.059994687799052197 0 0.27499960455498923 -0.059885944767455929 0 0.26249935422640547 -0.059698303969986058 0 0.24999905865034713 -0.059427166686205624 0 0.23749870917010102 -0.059067284443917699 0 0.22499829176985606 -0.058613039953011835 0 0.21249779146755365 -0.058057937119780946 0 0.19999719409576988 -0.057394559096993807 0 0.18749645845558685 -0.056615244552909234 0 0.17499556762814103 -0.055710017225365414 0 0.16249444360960999 -0.054668793588188679 0 0.14999303957538462 -0.053478201474552511 0 0.13749122200948111 -0.052123698918757067 0 0.12498884671342025 -0.050586635395975146 0 0.11248561848475973 -0.048844854472586741 0 0.099981138068765518 -0.046869505690216813 0 0.087474479216191003 -0.044623807360225462 0 0.074964621118000355 -0.04205569685918592 0 0.062446492760321902 -0.039098544280603768 0 0.049918541725142838 -0.035624947347748813 0 0.037328260760967027 -0.031515506079397577 0 0.019422298161866122 -0.023907069430545717 0 0.0056742009741973755 -0.01660351401419858 0 0 0 0
0 4 0.024905020621093604 1 0.037355229715489859 1 0.049803787515203535 1 0.062250756693110927 1 0.074696191999208086 1 0.087140150227694502 1 0.09958267900039075 1 0.11202382629992173 1 0.12446363161647073 1 0.13690213371776525 1 0.14933937048897158 1 0.16177537330286337 1 0.1742101729986055 1 0.18664379607006579 1 0.19907626507423062 1 0.21150760225959656 1 0.22393782432649634 1 0.23636694978918213 1 0.24879499018832782 1 0.26122195732031672 1 0.27364786151859527 1 0.28607271190080846 1 0.29849651320870207 1 0.31091926917362644 1 0.32334098125439875 1 0.33576165190351764 1 0.34818128001473453 1 0.36059986435023433 1 0.37301740357171842 1 0.38543389372362652 1 0.39784933266951494 1 0.41026371484180674 1 0.42267703707553572 1 0.43508929601632645 1 0.44750048755453947 1 0.45991061060022531 1 0.47231966164774714 1 0.48472764237851379 1 0.49713455205053769 1 0.5095403958993332 1 0.52194518122552613 1 0.53434891844675636 1 0.54675162067595628 1 0.55915330642894168 1 0.57155400072841434 1 0.58395373433544895 1 0.5963525448276461 1 0.60875047830170248 1 0.62114759129271047 1 0.63354395079841141 1 0.64593963741319882 1 0.6583347467197086 1 0.67072939181111524 1 0.68312370595321636 1 0.69551784561604479 1 0.70791199495225621 1 0.72030637046871193 1 0.73270122684714922 1 0.74509686419033272 1 0.75749363652144297 1 0.76989196402029769 1 0.78229234787255519 1 0.79469538715463528 1 0.80710180706076873 1 0.81951248666902865 1 0.83192851406250057 1 0.84435124088802904 1 0.85678238569061316 1 0.86922416081563947 1 0.88167949619479558 1 0.89415237774934153 1 0.90664844127968758 1 0.91917601939989868 1 0.931748273293869 1 0.94438793081305228 1 0.95714020787863818 1 0.97012207626326408 1 1 4
Polygon3D 0
PolygonOnTriangulations 0
Surfaces 2
-7 0 0 0 1 0 0
+7 0 0 0 1 0 0
8 0 1
-7 0 0 3 81 79 1 -0.0012600000000000001 0 0.99166806910433225 -0.0024290474198804652 0 0.97916907934632713 -0.0041556686870856405 0 0.9625013807263838 -0.0064065754633828211 0 0.95000132931521664 -0.0080695147108952852 0 0.93750127960794405 -0.009707852209573575 0 0.92500124260478966 -0.011322315812076994 0 0.91250118837727034 -0.012912930886184051 0 0.90000115960536642 -0.014480856552602568 0 0.88750112337475495 -0.016025977431048548 0 0.87500108849312463 -0.01754880638747152 0 0.86250106246199998 -0.019049805988652704 0 0.85000102944828482 -0.020529010547511328 0 0.83750100916791592 -0.021987024618123435 0 0.82500098575434166 -0.023423790211651842 0 0.81250096307329922 -0.024839539588589164 0 0.80000095229575618 -0.02623460040061484 0 0.7875009305699 -0.027608627134194662 0 0.77500092060707626 -0.028962080624074963 0 0.76250090701572382 -0.030294657285077038 0 0.75000090093676053 -0.031606513785123712 0 0.73750089105646288 -0.032897325203243725 0 0.72500088155224662 -0.034167037781514191 0 0.71250087862114031 -0.035415590209551026 0 0.70000087895100138 -0.036642679019159449 0 0.68750087227697532 -0.037847781327729632 0 0.6750008748552182 -0.039030894820797343 0 0.66250087359381438 -0.04019134768145604 0 0.65000087480075985 -0.04132883243621395 0 0.63750087815684497 -0.042442849673491136 0 0.625000877465687 -0.043532704745740072 0 0.61250088448934137 -0.044598076807669175 0 0.60000088431277843 -0.04563794173852994 0 0.58750089092946689 -0.046651919785977214 0 0.57500089296569545 -0.047638951624771643 0 0.5625008930402241 -0.048598256747966771 0 0.5500009010820599 -0.049529215643651064 0 0.53750089643319277 -0.050430285415768737 0 0.52500090129826238 -0.051301060818450712 0 0.51250089779487273 -0.052139902072950384 0 0.50000089749479981 -0.052945976246431804 0 0.48750088860344998 -0.053717653632727083 0 0.47500087974225197 -0.054453887628772561 0 0.4625008713526475 -0.055153292696117018 0 0.45000085526456401 -0.055814057545055237 0 0.43750083301262427 -0.056434614408511809 0 0.42500080860956885 -0.057013445108689248 0 0.41250077772299104 -0.057548589221626589 0 0.40000073803096653 -0.058038007077278866 0 0.38750069220288164 -0.058479817687152012 0 0.37500063518954613 -0.058871583983993149 0 0.3625005686566567 -0.059211135066700046 0 0.3500004886680172 -0.059495790765773687 0 0.33750039371847834 -0.059722842002354941 0 0.32500028290526284 -0.059889604063107046 0 0.31250015190983488 -0.059992723391208563 0 0.29999999813430406 -0.060029097107844702 0 0.2874998173873341 -0.059994687799052197 0 0.27499960455498923 -0.059885944767455929 0 0.26249935422640547 -0.059698303969986058 0 0.24999905865034713 -0.059427166686205624 0 0.23749870917010102 -0.059067284443917699 0 0.22499829176985606 -0.058613039953011835 0 0.21249779146755365 -0.058057937119780946 0 0.19999719409576988 -0.057394559096993807 0 0.18749645845558685 -0.056615244552909234 0 0.17499556762814103 -0.055710017225365414 0 0.16249444360960999 -0.054668793588188679 0 0.14999303957538462 -0.053478201474552511 0 0.13749122200948111 -0.052123698918757067 0 0.12498884671342025 -0.050586635395975146 0 0.11248561848475973 -0.048844854472586741 0 0.099981138068765518 -0.046869505690216813 0 0.087474479216191003 -0.044623807360225462 0 0.074964621118000355 -0.04205569685918592 0 0.062446492760321902 -0.039098544280603768 0 0.049918541725142838 -0.035624947347748813 0 0.037328260760967027 -0.031515506079397577 0 0.019422298161866122 -0.023907069430545717 0 0.0056742009741973755 -0.01660351401419858 0 0 0 0
+7 0 0 3 81 79 1 -0.0012600000000000001 0 0.99166806910433225 -0.0024290474198804652 0 0.97916907934632713 -0.0041556686870856405 0 0.9625013807263838 -0.0064065754633828211 0 0.95000132931521664 -0.0080695147108952852 0 0.93750127960794405 -0.009707852209573575 0 0.92500124260478966 -0.011322315812076994 0 0.91250118837727034 -0.012912930886184051 0 0.90000115960536642 -0.014480856552602568 0 0.88750112337475495 -0.016025977431048548 0 0.87500108849312463 -0.01754880638747152 0 0.86250106246199998 -0.019049805988652704 0 0.85000102944828482 -0.020529010547511328 0 0.83750100916791592 -0.021987024618123435 0 0.82500098575434166 -0.023423790211651842 0 0.81250096307329922 -0.024839539588589164 0 0.80000095229575618 -0.02623460040061484 0 0.7875009305699 -0.027608627134194662 0 0.77500092060707626 -0.028962080624074963 0 0.76250090701572382 -0.030294657285077038 0 0.75000090093676053 -0.031606513785123712 0 0.73750089105646288 -0.032897325203243725 0 0.72500088155224662 -0.034167037781514191 0 0.71250087862114031 -0.035415590209551026 0 0.70000087895100138 -0.036642679019159449 0 0.68750087227697532 -0.037847781327729632 0 0.6750008748552182 -0.039030894820797343 0 0.66250087359381438 -0.04019134768145604 0 0.65000087480075985 -0.04132883243621395 0 0.63750087815684497 -0.042442849673491136 0 0.625000877465687 -0.043532704745740072 0 0.61250088448934137 -0.044598076807669175 0 0.60000088431277843 -0.04563794173852994 0 0.58750089092946689 -0.046651919785977214 0 0.57500089296569545 -0.047638951624771643 0 0.5625008930402241 -0.048598256747966771 0 0.5500009010820599 -0.049529215643651064 0 0.53750089643319277 -0.050430285415768737 0 0.52500090129826238 -0.051301060818450712 0 0.51250089779487273 -0.052139902072950384 0 0.50000089749479981 -0.052945976246431804 0 0.48750088860344998 -0.053717653632727083 0 0.47500087974225197 -0.054453887628772561 0 0.4625008713526475 -0.055153292696117018 0 0.45000085526456401 -0.055814057545055237 0 0.43750083301262427 -0.056434614408511809 0 0.42500080860956885 -0.057013445108689248 0 0.41250077772299104 -0.057548589221626589 0 0.40000073803096653 -0.058038007077278866 0 0.38750069220288164 -0.058479817687152012 0 0.37500063518954613 -0.058871583983993149 0 0.3625005686566567 -0.059211135066700046 0 0.3500004886680172 -0.059495790765773687 0 0.33750039371847834 -0.059722842002354941 0 0.32500028290526284 -0.059889604063107046 0 0.31250015190983488 -0.059992723391208563 0 0.29999999813430406 -0.060029097107844702 0 0.2874998173873341 -0.059994687799052197 0 0.27499960455498923 -0.059885944767455929 0 0.26249935422640547 -0.059698303969986058 0 0.24999905865034713 -0.059427166686205624 0 0.23749870917010102 -0.059067284443917699 0 0.22499829176985606 -0.058613039953011835 0 0.21249779146755365 -0.058057937119780946 0 0.19999719409576988 -0.057394559096993807 0 0.18749645845558685 -0.056615244552909234 0 0.17499556762814103 -0.055710017225365414 0 0.16249444360960999 -0.054668793588188679 0 0.14999303957538462 -0.053478201474552511 0 0.13749122200948111 -0.052123698918757067 0 0.12498884671342025 -0.050586635395975146 0 0.11248561848475973 -0.048844854472586741 0 0.099981138068765518 -0.046869505690216813 0 0.087474479216191003 -0.044623807360225462 0 0.074964621118000355 -0.04205569685918592 0 0.062446492760321902 -0.039098544280603768 0 0.049918541725142838 -0.035624947347748813 0 0.037328260760967027 -0.031515506079397577 0 0.019422298161866122 -0.023907069430545717 0 0.0056742009741973755 -0.01660351401419858 0 0 0 0
0 4 0.024905020621093604 1 0.037355229715489859 1 0.049803787515203535 1 0.062250756693110927 1 0.074696191999208086 1 0.087140150227694502 1 0.09958267900039075 1 0.11202382629992173 1 0.12446363161647073 1 0.13690213371776525 1 0.14933937048897158 1 0.16177537330286337 1 0.1742101729986055 1 0.18664379607006579 1 0.19907626507423062 1 0.21150760225959656 1 0.22393782432649634 1 0.23636694978918213 1 0.24879499018832782 1 0.26122195732031672 1 0.27364786151859527 1 0.28607271190080846 1 0.29849651320870207 1 0.31091926917362644 1 0.32334098125439875 1 0.33576165190351764 1 0.34818128001473453 1 0.36059986435023433 1 0.37301740357171842 1 0.38543389372362652 1 0.39784933266951494 1 0.41026371484180674 1 0.42267703707553572 1 0.43508929601632645 1 0.44750048755453947 1 0.45991061060022531 1 0.47231966164774714 1 0.48472764237851379 1 0.49713455205053769 1 0.5095403958993332 1 0.52194518122552613 1 0.53434891844675636 1 0.54675162067595628 1 0.55915330642894168 1 0.57155400072841434 1 0.58395373433544895 1 0.5963525448276461 1 0.60875047830170248 1 0.62114759129271047 1 0.63354395079841141 1 0.64593963741319882 1 0.6583347467197086 1 0.67072939181111524 1 0.68312370595321636 1 0.69551784561604479 1 0.70791199495225621 1 0.72030637046871193 1 0.73270122684714922 1 0.74509686419033272 1 0.75749363652144297 1 0.76989196402029769 1 0.78229234787255519 1 0.79469538715463528 1 0.80710180706076873 1 0.81951248666902865 1 0.83192851406250057 1 0.84435124088802904 1 0.85678238569061316 1 0.86922416081563947 1 0.88167949619479558 1 0.89415237774934153 1 0.90664844127968758 1 0.91917601939989868 1 0.931748273293869 1 0.94438793081305228 1 0.95714020787863818 1 0.97012207626326408 1 1 4
-1 1 0 0 1 0 0 0 -1 0 0 0 -1
+1 1 0 0 1 0 0 0 -1 0 0 0 -1
Triangulations 0
TShapes 11
@@ -86,4 +86,4 @@ So
1100000
-2 0 *
-+1 0
\ No newline at end of file
++1 0
\ No newline at end of file
diff --git a/ceasiompy/CPACS2GMSH/tests/ToolInput/SimpleNacelle_fanCowl.brep b/ceasiompy/CPACS2GMSH/tests/ToolInput/SimpleNacelle_fanCowl.brep
index 5db2d6496..fc842e5eb 100644
--- a/ceasiompy/CPACS2GMSH/tests/ToolInput/SimpleNacelle_fanCowl.brep
+++ b/ceasiompy/CPACS2GMSH/tests/ToolInput/SimpleNacelle_fanCowl.brep
@@ -3,83 +3,83 @@ DBRep_DrawableShape
CASCADE Topology V1, (c) Matra-Datavision
Locations 0
Curve2ds 28
-1 0 0.39936054338562016 1 0
-1 0 0.5 1 0
-1 6.2831853071795862 0 0 1
-1 0 0 0 1
-1 0 0.5 1 0
-1 0 1 1 0
-1 6.2831853071795862 0 0 1
-1 0 0 0 1
-1 0 0 1 0
-1 0 0.0025200000000000001 1 0
-1 6.2831853071795862 0 0 1
-1 0 0 0 1
-1 0 0 1 0
-1 0 0.14988159487986658 1 0
-1 6.2831853071795862 0 0 1
-1 0 0 0 1
-1 6.2831853071795862 0 0 1
-1 0 0 0 1
-1 0 -0.1988143157790461 1 0
-1 0 0 1 0
-1 6.2831853071795862 0 0 1
-1 0 0 0 1
-1 0 -0.20813508570766034 1 0
-1 0 0 1 0
-1 0 0.40000000000000002 1 0
-1 0 0.69999999999999996 1 0
-1 6.2831853071795862 0 0 1
-1 0 0 0 1
+1 0 0.39936054338562016 1 0
+1 0 0.5 1 0
+1 6.2831853071795862 0 0 1
+1 0 0 0 1
+1 0 0.5 1 0
+1 0 1 1 0
+1 6.2831853071795862 0 0 1
+1 0 0 0 1
+1 0 0 1 0
+1 0 0.0025200000000000001 1 0
+1 6.2831853071795862 0 0 1
+1 0 0 0 1
+1 0 0 1 0
+1 0 0.14988159487986658 1 0
+1 6.2831853071795862 0 0 1
+1 0 0 0 1
+1 6.2831853071795862 0 0 1
+1 0 0 0 1
+1 0 -0.1988143157790461 1 0
+1 0 0 1 0
+1 6.2831853071795862 0 0 1
+1 0 0 0 1
+1 0 -0.20813508570766034 1 0
+1 0 0 1 0
+1 0 0.40000000000000002 1 0
+1 0 0.69999999999999996 1 0
+1 6.2831853071795862 0 0 1
+1 0 0 0 1
Curves 21
2 0.39188463239115445 0 0 1 0 0 0 -1.5799471481977533e-17 1 0 -1 -1.5799471481977533e-17 0.44286490271565371
2 5.1507962050250302e-18 0 0 1 0 0 0 -4.2488471045215345e-34 1 0 -1 -4.2488471045215345e-34 0.5
-7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
+7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
0 4 0.012452510310546802 1 0.018677614857744929 1 0.024901893757601767 1 0.031125378346555464 1 0.037348095999604043 1 0.043570075113847251 1 0.049791339500195375 1 0.056011913149960867 1 0.062231815808235363 1 0.068451066858882623 1 0.074669685244485792 1 0.080887686651431687 1 0.08710508649930275 1 0.093321898035032894 1 0.099538132537115312 1 0.10575380112979828 1 0.11196891216324817 1 0.11818347489459106 1 0.12439749509416391 1 0.13061097866015836 1 0.13682393075929764 1 0.14303635595040423 1 0.14924825660435104 1 0.15545963458681322 1 0.16167049062719938 1 0.16788082595175882 1 0.17409064000736726 1 0.18029993217511717 1 0.18650870178585921 1 0.19271694686181326 1 0.19892466633475747 1 0.20513185742090337 1 0.21133851853776786 1 0.21754464800816323 1 0.22375024377726974 1 0.22995530530011266 1 0.23615983082387357 1 0.24236382118925689 1 0.24856727602526885 1 0.2547701979496666 1 0.26097259061276307 1 0.26717445922337818 1 0.27337581033797814 1 0.27957665321447084 1 0.28577700036420717 1 0.29197686716772447 1 0.29817627241382305 1 0.30437523915085124 1 0.31057379564635523 1 0.31677197539920571 1 0.32296981870659941 1 0.3291673733598543 1 0.33536469590555762 1 0.34156185297660818 1 0.3477589228080224 1 0.3539559974761281 1 0.36015318523435597 1 0.36635061342357461 1 0.37254843209516636 1 0.37874681826072149 1 0.38494598201014885 1 0.3911461739362776 1 0.39734769357731764 1 0.40355090353038436 1 0.40975624333451433 1 0.41596425703125028 1 0.42217562044401452 1 0.42839119284530658 1 0.43461208040781973 1 0.44083974809739779 1 0.44707618887467077 1 0.45332422063984379 1 0.45958800969994934 1 0.4658741366469345 1 0.47219396540652614 1 0.47857010393931909 1 0.48506103813163204 1 0.49221420845893199 1 0.5 1 0.50778579154106807 1 0.51493896186836796 1 0.52142989606068102 1 0.52780603459347386 1 0.53412586335306556 1 0.54041199030005072 1 0.54667577936015621 1 0.55292381112532929 1 0.55916025190260221 1 0.56538791959218027 1 0.57160880715469353 1 0.57782437955598553 1 0.58403574296874983 1 0.59024375666548579 1 0.59644909646961564 1 0.60265230642268242 1 0.60885382606372229 1 0.61505401798985115 1 0.62125318173927857 1 0.62745156790483381 1 0.63364938657642544 1 0.63984681476564409 1 0.64604400252387206 1 0.65224107719197777 1 0.65843814702339198 1 0.66463530409444249 1 0.67083262664014576 1 0.67703018129340053 1 0.68322802460079435 1 0.68942620435364477 1 0.69562476084914859 1 0.70182372758617684 1 0.70802313283227536 1 0.71422299963579272 1 0.72042334678552888 1 0.72662418966202169 1 0.73282554077662143 1 0.73902740938723666 1 0.74522980205033329 1 0.75143272397473082 1 0.75763617881074286 1 0.76384016917612618 1 0.77004469469988723 1 0.77624975622273007 1 0.78245535199183658 1 0.78866148146223181 1 0.79486814257909633 1 0.80107533366524242 1 0.8072830531381866 1 0.81349129821414046 1 0.81970006782488258 1 0.82590935999263237 1 0.83211917404824076 1 0.83832950937280026 1 0.84454036541318656 1 0.85075174339564885 1 0.85696364404959546 1 0.86317606924070223 1 0.86938902133984142 1 0.87560250490583569 1 0.88181652510540864 1 0.88803108783675144 1 0.8942461988702014 1 0.90046186746288448 1 0.90667810196496701 1 0.91289491350069707 1 0.91911231334856824 1 0.92533031475551419 1 0.9315489331411172 1 0.93776818419176455 1 0.9439880868500391 1 0.95020866049980468 1 0.95642992488615264 1 0.96265190400039602 1 0.9688746216534444 1 0.97509810624239812 1 0.98132238514225512 1 0.98754748968945316 1 1 4
2 5.1507962050250302e-18 0 0 1 0 0 0 -4.2488471045215345e-34 1 0 -1 -4.2488471045215345e-34 0.5
2 2 0 0 1 0 0 -0 3.0783524855876494e-19 1 0 -1 3.0783524855876494e-19 0.50126000000000004
8 0.5 1
-7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
+7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
0 4 0.012452510310546802 1 0.018677614857744929 1 0.024901893757601767 1 0.031125378346555464 1 0.037348095999604043 1 0.043570075113847251 1 0.049791339500195375 1 0.056011913149960867 1 0.062231815808235363 1 0.068451066858882623 1 0.074669685244485792 1 0.080887686651431687 1 0.08710508649930275 1 0.093321898035032894 1 0.099538132537115312 1 0.10575380112979828 1 0.11196891216324817 1 0.11818347489459106 1 0.12439749509416391 1 0.13061097866015836 1 0.13682393075929764 1 0.14303635595040423 1 0.14924825660435104 1 0.15545963458681322 1 0.16167049062719938 1 0.16788082595175882 1 0.17409064000736726 1 0.18029993217511717 1 0.18650870178585921 1 0.19271694686181326 1 0.19892466633475747 1 0.20513185742090337 1 0.21133851853776786 1 0.21754464800816323 1 0.22375024377726974 1 0.22995530530011266 1 0.23615983082387357 1 0.24236382118925689 1 0.24856727602526885 1 0.2547701979496666 1 0.26097259061276307 1 0.26717445922337818 1 0.27337581033797814 1 0.27957665321447084 1 0.28577700036420717 1 0.29197686716772447 1 0.29817627241382305 1 0.30437523915085124 1 0.31057379564635523 1 0.31677197539920571 1 0.32296981870659941 1 0.3291673733598543 1 0.33536469590555762 1 0.34156185297660818 1 0.3477589228080224 1 0.3539559974761281 1 0.36015318523435597 1 0.36635061342357461 1 0.37254843209516636 1 0.37874681826072149 1 0.38494598201014885 1 0.3911461739362776 1 0.39734769357731764 1 0.40355090353038436 1 0.40975624333451433 1 0.41596425703125028 1 0.42217562044401452 1 0.42839119284530658 1 0.43461208040781973 1 0.44083974809739779 1 0.44707618887467077 1 0.45332422063984379 1 0.45958800969994934 1 0.4658741366469345 1 0.47219396540652614 1 0.47857010393931909 1 0.48506103813163204 1 0.49221420845893199 1 0.5 1 0.50778579154106807 1 0.51493896186836796 1 0.52142989606068102 1 0.52780603459347386 1 0.53412586335306556 1 0.54041199030005072 1 0.54667577936015621 1 0.55292381112532929 1 0.55916025190260221 1 0.56538791959218027 1 0.57160880715469353 1 0.57782437955598553 1 0.58403574296874983 1 0.59024375666548579 1 0.59644909646961564 1 0.60265230642268242 1 0.60885382606372229 1 0.61505401798985115 1 0.62125318173927857 1 0.62745156790483381 1 0.63364938657642544 1 0.63984681476564409 1 0.64604400252387206 1 0.65224107719197777 1 0.65843814702339198 1 0.66463530409444249 1 0.67083262664014576 1 0.67703018129340053 1 0.68322802460079435 1 0.68942620435364477 1 0.69562476084914859 1 0.70182372758617684 1 0.70802313283227536 1 0.71422299963579272 1 0.72042334678552888 1 0.72662418966202169 1 0.73282554077662143 1 0.73902740938723666 1 0.74522980205033329 1 0.75143272397473082 1 0.75763617881074286 1 0.76384016917612618 1 0.77004469469988723 1 0.77624975622273007 1 0.78245535199183658 1 0.78866148146223181 1 0.79486814257909633 1 0.80107533366524242 1 0.8072830531381866 1 0.81349129821414046 1 0.81970006782488258 1 0.82590935999263237 1 0.83211917404824076 1 0.83832950937280026 1 0.84454036541318656 1 0.85075174339564885 1 0.85696364404959546 1 0.86317606924070223 1 0.86938902133984142 1 0.87560250490583569 1 0.88181652510540864 1 0.88803108783675144 1 0.8942461988702014 1 0.90046186746288448 1 0.90667810196496701 1 0.91289491350069707 1 0.91911231334856824 1 0.92533031475551419 1 0.9315489331411172 1 0.93776818419176455 1 0.9439880868500391 1 0.95020866049980468 1 0.95642992488615264 1 0.96265190400039602 1 0.9688746216534444 1 0.97509810624239812 1 0.98132238514225512 1 0.98754748968945316 1 1 4
2 2 0 0 1 0 0 -0 3.0783524855876494e-19 1 0 -1 3.0783524855876494e-19 0.50126000000000004
2 2 0 0 1 0 0 0 -3.0939065784289713e-19 1 0 -1 -3.0939065784289713e-19 0.49874000000000002
8 0 0.0025200000000000001
-7 0 0 1 2 2 2 1.5430549669256652e-19 0.50126000000000004 2 -1.5430549669256652e-19 0.49874000000000002
+7 0 0 1 2 2 2 1.5430549669256652e-19 0.50126000000000004 2 -1.5430549669256652e-19 0.49874000000000002
0 2 0.0025200000000000001 2
2 2 0 0 1 0 0 0 -3.0939065784289713e-19 1 0 -1 -3.0939065784289713e-19 0.49874000000000002
2 1.3974516939166512 0 0 1 0 0 0 -9.7189165565310039e-18 1 0 -1 -9.7189165565310039e-18 0.46323705506589213
-7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
+7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
0 4 0.012452510310546802 1 0.018677614857744929 1 0.024901893757601767 1 0.031125378346555464 1 0.037348095999604043 1 0.043570075113847251 1 0.049791339500195375 1 0.056011913149960867 1 0.062231815808235363 1 0.068451066858882623 1 0.074669685244485792 1 0.080887686651431687 1 0.08710508649930275 1 0.093321898035032894 1 0.099538132537115312 1 0.10575380112979828 1 0.11196891216324817 1 0.11818347489459106 1 0.12439749509416391 1 0.13061097866015836 1 0.13682393075929764 1 0.14303635595040423 1 0.14924825660435104 1 0.15545963458681322 1 0.16167049062719938 1 0.16788082595175882 1 0.17409064000736726 1 0.18029993217511717 1 0.18650870178585921 1 0.19271694686181326 1 0.19892466633475747 1 0.20513185742090337 1 0.21133851853776786 1 0.21754464800816323 1 0.22375024377726974 1 0.22995530530011266 1 0.23615983082387357 1 0.24236382118925689 1 0.24856727602526885 1 0.2547701979496666 1 0.26097259061276307 1 0.26717445922337818 1 0.27337581033797814 1 0.27957665321447084 1 0.28577700036420717 1 0.29197686716772447 1 0.29817627241382305 1 0.30437523915085124 1 0.31057379564635523 1 0.31677197539920571 1 0.32296981870659941 1 0.3291673733598543 1 0.33536469590555762 1 0.34156185297660818 1 0.3477589228080224 1 0.3539559974761281 1 0.36015318523435597 1 0.36635061342357461 1 0.37254843209516636 1 0.37874681826072149 1 0.38494598201014885 1 0.3911461739362776 1 0.39734769357731764 1 0.40355090353038436 1 0.40975624333451433 1 0.41596425703125028 1 0.42217562044401452 1 0.42839119284530658 1 0.43461208040781973 1 0.44083974809739779 1 0.44707618887467077 1 0.45332422063984379 1 0.45958800969994934 1 0.4658741366469345 1 0.47219396540652614 1 0.47857010393931909 1 0.48506103813163204 1 0.49221420845893199 1 0.5 1 0.50778579154106807 1 0.51493896186836796 1 0.52142989606068102 1 0.52780603459347386 1 0.53412586335306556 1 0.54041199030005072 1 0.54667577936015621 1 0.55292381112532929 1 0.55916025190260221 1 0.56538791959218027 1 0.57160880715469353 1 0.57782437955598553 1 0.58403574296874983 1 0.59024375666548579 1 0.59644909646961564 1 0.60265230642268242 1 0.60885382606372229 1 0.61505401798985115 1 0.62125318173927857 1 0.62745156790483381 1 0.63364938657642544 1 0.63984681476564409 1 0.64604400252387206 1 0.65224107719197777 1 0.65843814702339198 1 0.66463530409444249 1 0.67083262664014576 1 0.67703018129340053 1 0.68322802460079435 1 0.68942620435364477 1 0.69562476084914859 1 0.70182372758617684 1 0.70802313283227536 1 0.71422299963579272 1 0.72042334678552888 1 0.72662418966202169 1 0.73282554077662143 1 0.73902740938723666 1 0.74522980205033329 1 0.75143272397473082 1 0.75763617881074286 1 0.76384016917612618 1 0.77004469469988723 1 0.77624975622273007 1 0.78245535199183658 1 0.78866148146223181 1 0.79486814257909633 1 0.80107533366524242 1 0.8072830531381866 1 0.81349129821414046 1 0.81970006782488258 1 0.82590935999263237 1 0.83211917404824076 1 0.83832950937280026 1 0.84454036541318656 1 0.85075174339564885 1 0.85696364404959546 1 0.86317606924070223 1 0.86938902133984142 1 0.87560250490583569 1 0.88181652510540864 1 0.88803108783675144 1 0.8942461988702014 1 0.90046186746288448 1 0.90667810196496701 1 0.91289491350069707 1 0.91911231334856824 1 0.92533031475551419 1 0.9315489331411172 1 0.93776818419176455 1 0.9439880868500391 1 0.95020866049980468 1 0.95642992488615264 1 0.96265190400039602 1 0.9688746216534444 1 0.97509810624239812 1 0.98132238514225512 1 0.98754748968945316 1 1 4
8 -0.1988143157790461 0
-1 1.3974516939166512 -4.5021622840785632e-18 0.46323705506589213 0.99314625882419283 -1.1359292630411471e-18 0.1168781783889085
+1 1.3974516939166512 -4.5021622840785632e-18 0.46323705506589213 0.99314625882419283 -1.1359292630411471e-18 0.1168781783889085
2 1.2 0 0 1 0 0 0 -9.7189165565310039e-18 1 0 -1 -9.7189165565310039e-18 0.44
2 1.3974516939166512 0 0 1 0 0 0 -9.7189165565310039e-18 1 0 -1 -9.7189165565310039e-18 0.46323705506589213
8 -0.20813508570766034 0
-1 0.39188463239115445 -6.9970314008247258e-18 0.44286490271565371 -0.99990526297501614 -2.1747389970659228e-19 0.013764631301411877
+1 0.39188463239115445 -6.9970314008247258e-18 0.44286490271565371 -0.99990526297501614 -2.1747389970659228e-19 0.013764631301411877
2 0.60000000000000009 0 0 1 0 0 0 -1.5799471481977533e-17 1 0 -1 -1.5799471481977533e-17 0.44
2 0.39188463239115445 0 0 1 0 0 0 -1.5799471481977533e-17 1 0 -1 -1.5799471481977533e-17 0.44286490271565371
2 1.2 0 0 1 0 0 0 -1.6699729079282087e-17 1 0 -1 -1.6699729079282087e-17 0.44
2 0.60000000000000009 0 0 1 0 0 0 -1.6699729079282087e-17 1 0 -1 -1.6699729079282087e-17 0.44
-7 0 0 1 2 2 2 -7.3478807948841184e-18 0.44 0 -7.3478807948841184e-18 0.44
+7 0 0 1 2 2 2 -7.3478807948841184e-18 0.44 0 -7.3478807948841184e-18 0.44
0 2 1 2
Polygon3D 0
PolygonOnTriangulations 0
Surfaces 7
-7 0 0 0 1 0 0
+7 0 0 0 1 0 0
8 0.39936054338562016 0.5
-7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
+7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
0 4 0.012452510310546802 1 0.018677614857744929 1 0.024901893757601767 1 0.031125378346555464 1 0.037348095999604043 1 0.043570075113847251 1 0.049791339500195375 1 0.056011913149960867 1 0.062231815808235363 1 0.068451066858882623 1 0.074669685244485792 1 0.080887686651431687 1 0.08710508649930275 1 0.093321898035032894 1 0.099538132537115312 1 0.10575380112979828 1 0.11196891216324817 1 0.11818347489459106 1 0.12439749509416391 1 0.13061097866015836 1 0.13682393075929764 1 0.14303635595040423 1 0.14924825660435104 1 0.15545963458681322 1 0.16167049062719938 1 0.16788082595175882 1 0.17409064000736726 1 0.18029993217511717 1 0.18650870178585921 1 0.19271694686181326 1 0.19892466633475747 1 0.20513185742090337 1 0.21133851853776786 1 0.21754464800816323 1 0.22375024377726974 1 0.22995530530011266 1 0.23615983082387357 1 0.24236382118925689 1 0.24856727602526885 1 0.2547701979496666 1 0.26097259061276307 1 0.26717445922337818 1 0.27337581033797814 1 0.27957665321447084 1 0.28577700036420717 1 0.29197686716772447 1 0.29817627241382305 1 0.30437523915085124 1 0.31057379564635523 1 0.31677197539920571 1 0.32296981870659941 1 0.3291673733598543 1 0.33536469590555762 1 0.34156185297660818 1 0.3477589228080224 1 0.3539559974761281 1 0.36015318523435597 1 0.36635061342357461 1 0.37254843209516636 1 0.37874681826072149 1 0.38494598201014885 1 0.3911461739362776 1 0.39734769357731764 1 0.40355090353038436 1 0.40975624333451433 1 0.41596425703125028 1 0.42217562044401452 1 0.42839119284530658 1 0.43461208040781973 1 0.44083974809739779 1 0.44707618887467077 1 0.45332422063984379 1 0.45958800969994934 1 0.4658741366469345 1 0.47219396540652614 1 0.47857010393931909 1 0.48506103813163204 1 0.49221420845893199 1 0.5 1 0.50778579154106807 1 0.51493896186836796 1 0.52142989606068102 1 0.52780603459347386 1 0.53412586335306556 1 0.54041199030005072 1 0.54667577936015621 1 0.55292381112532929 1 0.55916025190260221 1 0.56538791959218027 1 0.57160880715469353 1 0.57782437955598553 1 0.58403574296874983 1 0.59024375666548579 1 0.59644909646961564 1 0.60265230642268242 1 0.60885382606372229 1 0.61505401798985115 1 0.62125318173927857 1 0.62745156790483381 1 0.63364938657642544 1 0.63984681476564409 1 0.64604400252387206 1 0.65224107719197777 1 0.65843814702339198 1 0.66463530409444249 1 0.67083262664014576 1 0.67703018129340053 1 0.68322802460079435 1 0.68942620435364477 1 0.69562476084914859 1 0.70182372758617684 1 0.70802313283227536 1 0.71422299963579272 1 0.72042334678552888 1 0.72662418966202169 1 0.73282554077662143 1 0.73902740938723666 1 0.74522980205033329 1 0.75143272397473082 1 0.75763617881074286 1 0.76384016917612618 1 0.77004469469988723 1 0.77624975622273007 1 0.78245535199183658 1 0.78866148146223181 1 0.79486814257909633 1 0.80107533366524242 1 0.8072830531381866 1 0.81349129821414046 1 0.81970006782488258 1 0.82590935999263237 1 0.83211917404824076 1 0.83832950937280026 1 0.84454036541318656 1 0.85075174339564885 1 0.85696364404959546 1 0.86317606924070223 1 0.86938902133984142 1 0.87560250490583569 1 0.88181652510540864 1 0.88803108783675144 1 0.8942461988702014 1 0.90046186746288448 1 0.90667810196496701 1 0.91289491350069707 1 0.91911231334856824 1 0.92533031475551419 1 0.9315489331411172 1 0.93776818419176455 1 0.9439880868500391 1 0.95020866049980468 1 0.95642992488615264 1 0.96265190400039602 1 0.9688746216534444 1 0.97509810624239812 1 0.98132238514225512 1 0.98754748968945316 1 1 4
-7 0 0 0 1 0 0
+7 0 0 0 1 0 0
8 0.5 1
-7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
+7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
0 4 0.012452510310546802 1 0.018677614857744929 1 0.024901893757601767 1 0.031125378346555464 1 0.037348095999604043 1 0.043570075113847251 1 0.049791339500195375 1 0.056011913149960867 1 0.062231815808235363 1 0.068451066858882623 1 0.074669685244485792 1 0.080887686651431687 1 0.08710508649930275 1 0.093321898035032894 1 0.099538132537115312 1 0.10575380112979828 1 0.11196891216324817 1 0.11818347489459106 1 0.12439749509416391 1 0.13061097866015836 1 0.13682393075929764 1 0.14303635595040423 1 0.14924825660435104 1 0.15545963458681322 1 0.16167049062719938 1 0.16788082595175882 1 0.17409064000736726 1 0.18029993217511717 1 0.18650870178585921 1 0.19271694686181326 1 0.19892466633475747 1 0.20513185742090337 1 0.21133851853776786 1 0.21754464800816323 1 0.22375024377726974 1 0.22995530530011266 1 0.23615983082387357 1 0.24236382118925689 1 0.24856727602526885 1 0.2547701979496666 1 0.26097259061276307 1 0.26717445922337818 1 0.27337581033797814 1 0.27957665321447084 1 0.28577700036420717 1 0.29197686716772447 1 0.29817627241382305 1 0.30437523915085124 1 0.31057379564635523 1 0.31677197539920571 1 0.32296981870659941 1 0.3291673733598543 1 0.33536469590555762 1 0.34156185297660818 1 0.3477589228080224 1 0.3539559974761281 1 0.36015318523435597 1 0.36635061342357461 1 0.37254843209516636 1 0.37874681826072149 1 0.38494598201014885 1 0.3911461739362776 1 0.39734769357731764 1 0.40355090353038436 1 0.40975624333451433 1 0.41596425703125028 1 0.42217562044401452 1 0.42839119284530658 1 0.43461208040781973 1 0.44083974809739779 1 0.44707618887467077 1 0.45332422063984379 1 0.45958800969994934 1 0.4658741366469345 1 0.47219396540652614 1 0.47857010393931909 1 0.48506103813163204 1 0.49221420845893199 1 0.5 1 0.50778579154106807 1 0.51493896186836796 1 0.52142989606068102 1 0.52780603459347386 1 0.53412586335306556 1 0.54041199030005072 1 0.54667577936015621 1 0.55292381112532929 1 0.55916025190260221 1 0.56538791959218027 1 0.57160880715469353 1 0.57782437955598553 1 0.58403574296874983 1 0.59024375666548579 1 0.59644909646961564 1 0.60265230642268242 1 0.60885382606372229 1 0.61505401798985115 1 0.62125318173927857 1 0.62745156790483381 1 0.63364938657642544 1 0.63984681476564409 1 0.64604400252387206 1 0.65224107719197777 1 0.65843814702339198 1 0.66463530409444249 1 0.67083262664014576 1 0.67703018129340053 1 0.68322802460079435 1 0.68942620435364477 1 0.69562476084914859 1 0.70182372758617684 1 0.70802313283227536 1 0.71422299963579272 1 0.72042334678552888 1 0.72662418966202169 1 0.73282554077662143 1 0.73902740938723666 1 0.74522980205033329 1 0.75143272397473082 1 0.75763617881074286 1 0.76384016917612618 1 0.77004469469988723 1 0.77624975622273007 1 0.78245535199183658 1 0.78866148146223181 1 0.79486814257909633 1 0.80107533366524242 1 0.8072830531381866 1 0.81349129821414046 1 0.81970006782488258 1 0.82590935999263237 1 0.83211917404824076 1 0.83832950937280026 1 0.84454036541318656 1 0.85075174339564885 1 0.85696364404959546 1 0.86317606924070223 1 0.86938902133984142 1 0.87560250490583569 1 0.88181652510540864 1 0.88803108783675144 1 0.8942461988702014 1 0.90046186746288448 1 0.90667810196496701 1 0.91289491350069707 1 0.91911231334856824 1 0.92533031475551419 1 0.9315489331411172 1 0.93776818419176455 1 0.9439880868500391 1 0.95020866049980468 1 0.95642992488615264 1 0.96265190400039602 1 0.9688746216534444 1 0.97509810624239812 1 0.98132238514225512 1 0.98754748968945316 1 1 4
-7 0 0 0 1 0 0
+7 0 0 0 1 0 0
8 0 0.0025200000000000001
-7 0 0 1 2 2 2 1.5430549669256652e-19 0.50126000000000004 2 -1.5430549669256652e-19 0.49874000000000002
+7 0 0 1 2 2 2 1.5430549669256652e-19 0.50126000000000004 2 -1.5430549669256652e-19 0.49874000000000002
0 2 0.0025200000000000001 2
-7 0 0 0 1 0 0
+7 0 0 0 1 0 0
8 0 0.14988159487986658
-7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
+7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
0 4 0.012452510310546802 1 0.018677614857744929 1 0.024901893757601767 1 0.031125378346555464 1 0.037348095999604043 1 0.043570075113847251 1 0.049791339500195375 1 0.056011913149960867 1 0.062231815808235363 1 0.068451066858882623 1 0.074669685244485792 1 0.080887686651431687 1 0.08710508649930275 1 0.093321898035032894 1 0.099538132537115312 1 0.10575380112979828 1 0.11196891216324817 1 0.11818347489459106 1 0.12439749509416391 1 0.13061097866015836 1 0.13682393075929764 1 0.14303635595040423 1 0.14924825660435104 1 0.15545963458681322 1 0.16167049062719938 1 0.16788082595175882 1 0.17409064000736726 1 0.18029993217511717 1 0.18650870178585921 1 0.19271694686181326 1 0.19892466633475747 1 0.20513185742090337 1 0.21133851853776786 1 0.21754464800816323 1 0.22375024377726974 1 0.22995530530011266 1 0.23615983082387357 1 0.24236382118925689 1 0.24856727602526885 1 0.2547701979496666 1 0.26097259061276307 1 0.26717445922337818 1 0.27337581033797814 1 0.27957665321447084 1 0.28577700036420717 1 0.29197686716772447 1 0.29817627241382305 1 0.30437523915085124 1 0.31057379564635523 1 0.31677197539920571 1 0.32296981870659941 1 0.3291673733598543 1 0.33536469590555762 1 0.34156185297660818 1 0.3477589228080224 1 0.3539559974761281 1 0.36015318523435597 1 0.36635061342357461 1 0.37254843209516636 1 0.37874681826072149 1 0.38494598201014885 1 0.3911461739362776 1 0.39734769357731764 1 0.40355090353038436 1 0.40975624333451433 1 0.41596425703125028 1 0.42217562044401452 1 0.42839119284530658 1 0.43461208040781973 1 0.44083974809739779 1 0.44707618887467077 1 0.45332422063984379 1 0.45958800969994934 1 0.4658741366469345 1 0.47219396540652614 1 0.47857010393931909 1 0.48506103813163204 1 0.49221420845893199 1 0.5 1 0.50778579154106807 1 0.51493896186836796 1 0.52142989606068102 1 0.52780603459347386 1 0.53412586335306556 1 0.54041199030005072 1 0.54667577936015621 1 0.55292381112532929 1 0.55916025190260221 1 0.56538791959218027 1 0.57160880715469353 1 0.57782437955598553 1 0.58403574296874983 1 0.59024375666548579 1 0.59644909646961564 1 0.60265230642268242 1 0.60885382606372229 1 0.61505401798985115 1 0.62125318173927857 1 0.62745156790483381 1 0.63364938657642544 1 0.63984681476564409 1 0.64604400252387206 1 0.65224107719197777 1 0.65843814702339198 1 0.66463530409444249 1 0.67083262664014576 1 0.67703018129340053 1 0.68322802460079435 1 0.68942620435364477 1 0.69562476084914859 1 0.70182372758617684 1 0.70802313283227536 1 0.71422299963579272 1 0.72042334678552888 1 0.72662418966202169 1 0.73282554077662143 1 0.73902740938723666 1 0.74522980205033329 1 0.75143272397473082 1 0.75763617881074286 1 0.76384016917612618 1 0.77004469469988723 1 0.77624975622273007 1 0.78245535199183658 1 0.78866148146223181 1 0.79486814257909633 1 0.80107533366524242 1 0.8072830531381866 1 0.81349129821414046 1 0.81970006782488258 1 0.82590935999263237 1 0.83211917404824076 1 0.83832950937280026 1 0.84454036541318656 1 0.85075174339564885 1 0.85696364404959546 1 0.86317606924070223 1 0.86938902133984142 1 0.87560250490583569 1 0.88181652510540864 1 0.88803108783675144 1 0.8942461988702014 1 0.90046186746288448 1 0.90667810196496701 1 0.91289491350069707 1 0.91911231334856824 1 0.92533031475551419 1 0.9315489331411172 1 0.93776818419176455 1 0.9439880868500391 1 0.95020866049980468 1 0.95642992488615264 1 0.96265190400039602 1 0.9688746216534444 1 0.97509810624239812 1 0.98132238514225512 1 0.98754748968945316 1 1 4
10 0 6.2831853071795862 -0.1988143157790461 0
3 1.3974516939166512 0 0 1 0 0 0 -9.7189165565310039e-18 1 0 -1 -9.7189165565310039e-18 0.46323705506589213
@@ -87,9 +87,9 @@ Surfaces 7
10 0 6.2831853071795862 -0.20813508570766034 0
3 0.39188463239115445 0 0 -1 -0 -0 0 -1.5799471481977533e-17 1 0 -1 -1.5799471481977533e-17 0.44286490271565371
0.01376506599129091
-7 0 0 0 1 0 0
+7 0 0 0 1 0 0
8 0.40000000000000002 0.69999999999999996
-7 0 0 1 2 2 2 -7.3478807948841184e-18 0.44 0 -7.3478807948841184e-18 0.44
+7 0 0 1 2 2 2 -7.3478807948841184e-18 0.44 0 -7.3478807948841184e-18 0.44
0 2 1 2
Triangulations 0
@@ -411,4 +411,4 @@ So
1100000
+2 0 *
-+1 0
\ No newline at end of file
++1 0
\ No newline at end of file
diff --git a/ceasiompy/CPACS2GMSH/tests/ToolInput/test_close_engine/SimpleEngine.brep b/ceasiompy/CPACS2GMSH/tests/ToolInput/test_close_engine/SimpleEngine.brep
new file mode 100644
index 000000000..cd838b290
--- /dev/null
+++ b/ceasiompy/CPACS2GMSH/tests/ToolInput/test_close_engine/SimpleEngine.brep
@@ -0,0 +1,325 @@
+DBRep_DrawableShape
+
+CASCADE Topology V1, (c) Matra-Datavision
+Locations 3
+1
+1.11022302462516e-16 0 1 0.0039998008
+ 0 1 0 0
+ -1 0 1.11022302462516e-16 0.0039998008
+1
+ 1 0 0 1.9920003984
+ 0 1 0 0
+ 0 0 1 0
+2 1 1 2 1 0
+Curve2ds 23
+1 0 0.5 1 0
+1 0 0.5 1 0
+1 6.2831853071795862 0 0 1
+1 0 0 0 1
+1 0 1 1 0
+1 0 0 1 0
+7 0 0 5 118 30 6.2831853071795862 0.49722879746868048 6.2467428323965901 0.49722879746868048 6.2103003576156155 0.49722879746868048 6.1738578828353443 0.49722879746868048 6.1374154080544105 0.49722879746868048 6.0645304584883677 0.49722879746868048 6.0280879837074322 0.49722879746868048 5.9916455089271636 0.49722879746868048 5.9552030341461926 0.49722879746868048 5.8848313587036101 0.49722879746868048 5.8509021580452272 0.49722879746868048 5.8169729573872901 0.49722879746868048 5.7830437567289685 0.49722879746868048 5.7126720812863248 0.49722879746868048 5.6762296065054114 0.49722879746868048 5.6397871317251385 0.49722879746868048 5.6033446569441399 0.49722879746868048 5.5304597073781183 0.49722879746868048 5.4940172325972245 0.49722879746868048 5.4575747578169542 0.49722879746868048 5.4211322830359361 0.49722879746868048 5.3495039705317007 0.49722879746868048 5.3143181328120122 0.49722879746868048 5.2791322950928752 0.49722879746868048 5.243946457373224 0.49722879746868048 5.1723181448689308 0.49722879746868048 5.13587567008799 0.49722879746868048 5.0994331953077161 0.49722879746868048 5.0629907205267433 0.49722879746868048 4.9901057709607759 0.49722879746868048 4.9536632961797524 0.49722879746868048 4.9172208213994812 0.49722879746868048 4.8807783466185946 0.49722879746868048 4.8078933970525579 0.49722879746868048 4.7714509222715744 0.49722879746868048 4.7350084474913032 0.49722879746868048 4.6985659727103783 0.49722879746868048 4.6256810231443666 0.49722879746868048 4.5892385483633742 0.49722879746868048 4.5527960735831039 0.49722879746868048 4.516353598802187 0.49722879746868048 4.4434686492361273 0.49722879746868048 4.4070261744552166 0.49722879746868048 4.3705836996749454 0.49722879746868048 4.3341412248939433 0.49722879746868048 4.2612562753279573 0.49722879746868048 4.2248138005469924 0.49722879746868048 4.1883713257667221 0.49722879746868048 4.1519288509857777 0.49722879746868048 4.0790439014197739 0.49722879746868048 4.0426014266387789 0.49722879746868048 4.0061589518585095 0.49722879746868048 3.9697164770775948 0.49722879746868048 3.8968315275115608 0.49722879746868048 3.8603890527305835 0.49722879746868048 3.8239465779503128 0.49722879746868048 3.7875041031693768 0.49722879746868048 3.7146191536033641 0.49722879746868048 3.6781766788223664 0.49722879746868048 3.6417342040420935 0.49722879746868048 3.6052917292611761 0.49722879746868048 3.5324067796951191 0.49722879746868048 3.4959643049141897 0.49722879746868048 3.4595218301339146 0.49722879746868048 3.4230793553529275 0.49722879746868048 3.3501944057869304 0.49722879746868048 3.313751931005958 0.49722879746868048 3.2773094562256859 0.49722879746868048 3.2408669814447442 0.49722879746868048 3.1679820318787177 0.49722879746868048 3.1315395570977542 0.49722879746868048 3.0950970823174808 0.49722879746868048 3.0586546075365275 0.49722879746868048 2.9895395691555402 0.49722879746868048 2.95686700555853 0.49722879746868048 2.9241944419618333 0.49722879746868048 2.8915218783648138 0.49722879746868048 2.8224068399838345 0.49722879746868048 2.7859643652028745 0.49722879746868048 2.749521890422602 0.49722879746868048 2.7130794156416478 0.49722879746868048 2.640194466075624 0.49722879746868048 2.6037519912946672 0.49722879746868048 2.5673095165143929 0.49722879746868048 2.5308670417334338 0.49722879746868048 2.4579820921674176 0.49722879746868048 2.4215396173864496 0.49722879746868048 2.3850971426061776 0.49722879746868048 2.3486546678252336 0.49722879746868048 2.2757697182592032 0.49722879746868048 2.239327243478249 0.49722879746868048 2.2028847686979751 0.49722879746868048 2.1664422939170143 0.49722879746868048 2.1036104408437106 0.49722879746868048 2.0772210625536225 0.49722879746868048 2.0508316842636125 0.49722879746868048 2.024442305973535 0.49722879746868048 1.961610452900213 0.49722879746868048 1.9251679781192741 0.49722879746868048 1.8887255033389998 0.49722879746868048 1.8522830285580227 0.49722879746868048 1.7831679901770534 0.49722879746868048 1.7504954265800365 0.49722879746868048 1.7178228629833392 0.49722879746868048 1.6851502993863257 0.49722879746868048 1.6185485351287514 0.49722879746868048 1.5846193344703974 0.49722879746868048 1.5506901338124588 0.49722879746868048 1.5167609331541041 0.49722879746868048 1.3697359342160698 0.49722879746868048 1.2566332069944544 0.49722879746868048 1.1435435803483773 0.49722879746868048 1.0304408531267781 0.49722879746868048 0.73389273899499829 0.49722879746868048 0.55036496471515794 0.49722879746868048 0.36698009013317567 0.49722879746868048 0.18345231585333915 0.49722879746868048 0 0.49722879746868048
+ 0 6 0.029000420763338419 4 0.058000841526677539 4 0.085001233271496168 4 0.11400165403483276 4 0.14300207479821528 4 0.17100248105233079 4 0.20000290181572761 4 0.22900332257910461 4 0.25800374334244064 4 0.28700416410574697 4 0.31600458486905331 4 0.34500500563237591 4 0.37400542639567941 4 0.40300584715895815 4 0.43200626792220864 4 0.46100668868545203 4 0.49000710944870285 4 0.51900753021194346 4 0.54500790744748351 4 0.57400832821072523 4 0.60300874897396439 4 0.63200916973720223 4 0.66100959050043506 4 0.68200989519037636 4 0.7110103159536113 4 0.73701069318915302 4 0.76401108493388792 4 0.85401116742439875 4 1 6
+7 0 0 5 118 30 -0.49283762810360682 5.5511151231257827e-17 -0.49283762913646589 0.017960209128777705 -0.49201948200409812 0.035920479942518888 -0.49038319833905381 0.05383094219538008 -0.4879332966256088 0.071642137383439247 -0.48142430980543927 0.10696790048027367 -0.47736520234338303 0.12448258971656827 -0.47251052802953791 0.14180004390736131 -0.46687369271157042 0.15887244209951631 -0.45451269175820785 0.19127663869964517 -0.44789029105706507 0.20664736888522994 -0.44061960271525569 0.22172779165774581 -0.43271803490035926 0.23648180004706135 -0.4150635696046569 0.26633387128159541 -0.40521683430128086 0.2813765604127868 -0.3946912491207365 0.29595991184347048 -0.38351587973919687 0.31004365465635941 -0.35993104477059201 0.33713667972374534 -0.34752149818061057 0.35014605503016366 -0.33452757457279575 0.36258068536716698 -0.32098515576932779 0.37440623336229645 -0.29336589750151804 0.39639132844111086 -0.27932350393004113 0.40659433110434745 -0.26484118847378019 0.41617269831374459 -0.24995623742106932 0.4251017695264066 -0.21891540237858279 0.44191350330830687 -0.2027330103959594 0.44974746030041945 -0.18620478802063373 0.45683829575581436 -0.16937637679661796 0.46316642882234821 -0.13521402894472634 0.47426644850899369 -0.11787997498507026 0.4790383732525022 -0.10034047368951193 0.48301681222360121 -0.082643959178922505 0.48619077923797638 -0.047035774448977295 0.49091662604437197 -0.029123981932133468 0.49246852206744912 -0.011153927413206432 0.49320284007104132 0.0068247660439580415 0.49311755228672272 0.042699815254525986 0.49131275536264363 0.060596294222031846 0.48959324002424404 0.078401920826762686 0.48705912409229624 0.096067526061194869 0.4837174053528418 0.1310216315814911 0.47544172078450242 0.14831025191840874 0.47050772653249562 0.16536191361220146 0.46478908020454862 0.18212952967734802 0.45830157344734568 0.21500537540443093 0.44382900575785039 0.23111371797959496 0.43584389511903254 0.24684684123478892 0.42713006043041357 0.26216129923728049 0.41771156434437257 0.29187037939793326 0.39752129431630973 0.30626510359294123 0.38674945103009911 0.32015877114898911 0.37532893947475832 0.33351301566315572 0.36329129660380521 0.35907167479369317 0.33805181579996957 0.37127617719216321 0.32484989118122554 0.38287037541256147 0.31110083121445553 0.3938222528776455 0.29684260297871629 0.41438425425307746 0.26738958026057691 0.42399444878431325 0.25219468462076111 0.4329052985194955 0.23657230198107584 0.44109219676280792 0.22056557247169536 0.45597674094780893 0.18787418534598088 0.46267443801098418 0.17118941544997232 0.46860690467958338 0.15421096082154104 0.47375775885682658 0.13698570628995577 0.48247202469597089 0.10213835318124879 0.4860354662873026 0.084516134919688429 0.48879312835017741 0.066743757974251791 0.49073739579135961 0.048870299535826146 0.49299285751545857 0.013020762018454757 0.49330405954486051 -0.0049554401869570056 0.49279561194172505 -0.022933303335245736 0.49146891874897536 -0.040863182799573149 0.48741217482147486 -0.074683216604735286 0.48484148786412834 -0.090592584910555274 0.48162213020155675 -0.10638800917017469 0.47776125039840911 -0.12203441573744353 0.46825812471422895 -0.15474439067028098 0.46246170736997672 -0.17176326955239513 0.45589353482485973 -0.1885060647718706 0.44857174463350968 -0.2049265422566135 0.43246563197189458 -0.2370337000680621 0.42368125418461633 -0.25272049066791202 0.41418792539654065 -0.26799592162305214 0.40401186077914064 -0.28281781088641078 0.38235437946183126 -0.3114749208381985 0.37087288837856558 -0.32531023995045272 0.35876872357152967 -0.33861254338511493 0.34607530985545643 -0.35134509775042166 0.31958353020950864 -0.37560333468118934 0.30578507329272442 -0.38712910056239125 0.29147083640089366 -0.3980178426636754 0.27668034731009872 -0.40823949247612629 0.2504312493562853 -0.42466724005081524 0.2391790170834088 -0.43120323635765878 0.22771520283520838 -0.43736554466032718 0.21605642180480866 -0.44314523355603158 0.18787418534597616 -0.45597674094781188 0.17118941544996602 -0.46267443801098584 0.15421096082153443 -0.46860690467958499 0.13698570628995035 -0.47375775885682908 0.10394079814626411 -0.48202128737987004 0.088159991513890298 -0.48528970800064297 0.072254452156979426 -0.4879114457958259 0.056259498231603623 -0.48988067921215056 0.023545097293108684 -0.49255879636342137 0.0068213647929623034 -0.49321629826348445 -0.0099201791530915233 -0.4931637029789841 -0.026639450361980212 -0.4924011364385098 -0.098818732105655449 -0.48603550858492989 -0.15365230498503707 -0.4732892645229273 -0.20630717762490969 -0.45282348349031049 -0.25535561369046478 -0.42519309493117552 -0.37139739225798463 -0.33634440168453522 -0.43060943183772055 -0.26489311940889637 -0.47210311356572016 -0.18086320150689755 -0.49283795281486092 -0.090412011291749084 -0.49283762810360682 -1.1102230246251565e-16
+ 0 6 0.029000420763338419 4 0.058000841526677539 4 0.085001233271496168 4 0.11400165403483276 4 0.14300207479821528 4 0.17100248105233079 4 0.20000290181572761 4 0.22900332257910461 4 0.25800374334244064 4 0.28700416410574697 4 0.31600458486905331 4 0.34500500563237591 4 0.37400542639567941 4 0.40300584715895815 4 0.43200626792220864 4 0.46100668868545203 4 0.49000710944870285 4 0.51900753021194346 4 0.54500790744748351 4 0.57400832821072523 4 0.60300874897396439 4 0.63200916973720223 4 0.66100959050043506 4 0.68200989519037636 4 0.7110103159536113 4 0.73701069318915302 4 0.76401108493388792 4 0.85401116742439875 4 1 6
+1 6.2831853071795862 0 0 1
+1 0 0 0 1
+1 6.2831853071795862 0 0 1
+1 0 0 0 1
+1 0 0.0025200000000000001 1 0
+1 0 0 1 0
+7 0 0 8 16 3 8.5001450322863548e-17 -0.0089406275329954443 -0.0035109565524648577 -0.0089407236897860332 -0.0070220371243660384 -0.0073643130840477071 -0.0097075444029976633 -0.0042154440011484013 -0.010744945738707009 3.5737038328598203e-14 -0.009707544402986653 0.0042154440010922448 -0.0070220371243809067 0.0073643130840675367 -0.0035109565524601514 0.0089407236897817797 0.0035109565524750544 0.0089407236897817485 0.0070220371243121735 0.0073643130840724911 0.0097075444031177495 0.0042154440010908571 0.010744945738554636 3.812401783154229e-14 0.0097075444031073481 -0.004215444001147004 0.0070220371243232375 -0.0073643130840465604 0.0035109565524730265 -0.0089407236897866421 8.6736173798840355e-17 -0.0089406275329954443
+ 0 9 0.50000000000011025 7 1 9
+7 0 0 8 16 3 6.2831853071795862 0.99380409907542866 5.8904873605703116 0.99380409907542866 5.4977863627325059 0.99380409907542866 5.1050865508633763 0.99380409907542866 4.7123889803837749 0.99380409907542866 4.3196914099069481 0.99380409907542866 3.9269915980377892 0.99380409907542866 3.5342906001986343 0.99380409907542866 2.7488947069813943 0.99380409907542866 2.3561937091399705 0.99380409907542866 1.9634938972790028 0.99380409907542866 1.5707963267868816 0.99380409907542866 1.1780987563235561 0.99380409907542866 0.78539894444426595 0.99380409907542866 0.39269794660974883 0.99380409907542866 0 0.99380409907542866
+ 0 9 0.50000000000011025 7 1 9
+1 6.2831853071795862 0 0 1
+1 0 0 0 1
+7 0 0 7 176 30 6.2831853071795862 0.00099630702602213268 6.2652333491589971 0.00099630702602213268 6.2472813911384888 0.00099630702602213268 6.2293294331180258 0.00099630702602213268 6.2113774750975717 0.00099630702602213268 6.1934255170770971 0.00099630702602213268 6.1754735590565817 0.00099630702602213268 6.1314912619052482 0.00099630702602213268 6.1054609227755163 0.00099630702602213268 6.079430583646384 0.00099630702602213268 6.0534002445174417 0.00099630702602213268 6.0273699053882899 0.00099630702602213268 6.0013395662585385 0.00099630702602213268 5.9492788879970533 0.00099630702602213268 5.9232485488673223 0.00099630702602213268 5.8972182097381909 0.00099630702602213268 5.871187870609246 0.00099630702602213268 5.8451575314800897 0.00099630702602213268 5.8191271923503356 0.00099630702602213268 5.7670665140888735 0.00099630702602213268 5.7410361749591301 0.00099630702602213268 5.7150058358299836 0.00099630702602213268 5.6889754967010351 0.00099630702602213268 5.6629451575718903 0.00099630702602213268 5.636914818442146 0.00099630702602213268 5.5848541401806937 0.00099630702602213268 5.5588238010509325 0.00099630702602213268 5.5327934619217691 0.00099630702602213268 5.5067631227928233 0.00099630702602213268 5.4807327836636963 0.00099630702602213268 5.4547024445339725 0.00099630702602213268 5.403539364173727 0.00099630702602213268 5.3784066229450023 0.00099630702602213268 5.3532738817167465 0.00099630702602213268 5.3281411404886541 0.00099630702602213268 5.3030083992604133 0.00099630702602213268 5.2778756580317037 0.00099630702602213268 5.2267125776714494 0.00099630702602213268 5.2006822385417157 0.00099630702602213268 5.174651899412579 0.00099630702602213268 5.148621560283634 0.00099630702602213268 5.1225912211544804 0.00099630702602213268 5.0965608820247308 0.00099630702602213268 5.0445002037632625 0.00099630702602213268 5.0184698646335217 0.00099630702602213268 4.9924395255043779 0.00099630702602213268 4.966409186375432 0.00099630702602213268 4.9403788472462855 0.00099630702602213268 4.9143485081165412 0.00099630702602213268 4.8622878298550498 0.00099630702602213268 4.8362574907253224 0.00099630702602213268 4.8102271515961927 0.00099630702602213268 4.7841968124672478 0.00099630702602213268 4.7581664733380915 0.00099630702602213268 4.7321361342083348 0.00099630702602213268 4.6800754559468629 0.00099630702602213268 4.6540451168171293 0.00099630702602213268 4.6280147776879916 0.00099630702602213268 4.601984438559044 0.00099630702602213268 4.5759540994298904 0.00099630702602213268 4.5499237603001399 0.00099630702602213268 4.4978630820386689 0.00099630702602213268 4.4718327429089291 0.00099630702602213268 4.445802403779787 0.00099630702602213268 4.4197720646508412 0.00099630702602213268 4.3937417255216955 0.00099630702602213268 4.3677113863919521 0.00099630702602213268 4.3156507081304873 0.00099630702602213268 4.289620369000736 0.00099630702602213268 4.2635900298715814 0.00099630702602213268 4.2375596907426365 0.00099630702602213268 4.2115293516135006 0.00099630702602213268 4.1854990124837661 0.00099630702602213268 4.1334383342222907 0.00099630702602213268 4.1074079950925393 0.00099630702602213268 4.0813776559633848 0.00099630702602213268 4.0553473168344381 0.00099630702602213268 4.0293169777053013 0.00099630702602213268 4.0032866385755668 0.00099630702602213268 3.9512259603140794 0.00099630702602213268 3.9251956211843386 0.00099630702602213268 3.8991652820551912 0.00099630702602213268 3.8731349429262418 0.00099630702602213268 3.8471046037970913 0.00099630702602213268 3.8210742646673452 0.00099630702602213268 3.7690135864058747 0.00099630702602213268 3.7429832472761277 0.00099630702602213268 3.7169529081469768 0.00099630702602213268 3.6909225690180292 0.00099630702602213268 3.6648922298888871 0.00099630702602213268 3.638861890759145 0.00099630702602213268 3.586801212497666 0.00099630702602213268 3.5607708733679155 0.00099630702602213268 3.5347405342387637 0.00099630702602213268 3.5087101951098165 0.00099630702602213268 3.4826798559806744 0.00099630702602213268 3.4566495168509364 0.00099630702602213268 3.4045888385894547 0.00099630702602213268 3.3785584994597064 0.00099630702602213268 3.3525281603305563 0.00099630702602213268 3.3264978212016088 0.00099630702602213268 3.3004674820724658 0.00099630702602213268 3.2744371429427255 0.00099630702602213268 3.2223764646812478 0.00099630702602213268 3.1963461255514978 0.00099630702602213268 3.1703157864223459 0.00099630702602213268 3.1442854472933979 0.00099630702602213268 3.1182551081642562 0.00099630702602213268 3.0922247690345168 0.00099630702602213268 3.040164090773037 0.00099630702602213268 3.0141337516432882 0.00099630702602213268 2.9881034125141368 0.00099630702602213268 2.9620730733851892 0.00099630702602213268 2.9360427342560476 0.00099630702602213268 2.9100123951263082 0.00099630702602213268 2.8606445105684259 0.00099630702602213268 2.8373069651417637 0.00099630702602213268 2.8139694197153799 0.00099630702602213268 2.7906318742890837 0.00099630702602213268 2.7672943288626892 0.00099630702602213268 2.7439567834360168 0.00099630702602213268 2.6945888988781399 0.00099630702602213268 2.6685585597484063 0.00099630702602213268 2.6425282206192713 0.00099630702602213268 2.6164978814903241 0.00099630702602213268 2.5904675423611665 0.00099630702602213268 2.5644372032314111 0.00099630702602213268 2.5123765249699406 0.00099630702602213268 2.4863461858401985 0.00099630702602213268 2.4603158467110537 0.00099630702602213268 2.4342855075821062 0.00099630702602213268 2.4082551684529574 0.00099630702602213268 2.3822248293232113 0.00099630702602213268 2.3301641510617417 0.00099630702602213268 2.3041338119319907 0.00099630702602213268 2.2781034728028362 0.00099630702602213268 2.2520731336738877 0.00099630702602213268 2.226042794544747 0.00099630702602213268 2.2000124554150098 0.00099630702602213268 2.1479517771535264 0.00099630702602213268 2.121921438023779 0.00099630702602213268 2.0958910988946289 0.00099630702602213268 2.0698607597656817 0.00099630702602213268 2.0438304206365383 0.00099630702602213268 2.0178000815067967 0.00099630702602213268 1.9657394032453048 0.00099630702602213268 1.939709064115569 0.00099630702602213268 1.9136787249864309 0.00099630702602213268 1.8876483858574828 0.00099630702602213268 1.8616180467283268 0.00099630702602213268 1.835587707598574 0.00099630702602213268 1.7844246272383479 0.00099630702602213268 1.759291886009628 0.00099630702602213268 1.7341591447813756 0.00099630702602213268 1.7090264035532801 0.00099630702602213268 1.6838936623250311 0.00099630702602213268 1.6587609210963141 0.00099630702602213268 1.6120858302419401 0.00099630702602213268 1.5905434806173193 0.00099630702602213268 1.5690011309928564 0.00099630702602213268 1.5474587813684519 0.00099630702602213268 1.5259164317439995 0.00099630702602213268 1.5043740821193885 0.00099630702602213268 1.4657773723749621 0.00099630702602213268 1.4487230122554766 0.00099630702602213268 1.4316686521360216 0.00099630702602213268 1.4146142920165747 0.00099630702602213268 1.3975599318971155 0.00099630702602213268 1.3805055717776258 0.00099630702602213268 1.1687562013613073 0.00099630702602212292 0.97380360399379895 0.00099630702602217952 0.77905696014949655 0.00099630702602202773 0.58439425150857249 0.00099630702602227297 0.38964760766433404 0.00099630702602201602 0.19469501029675967 0.00099630702602219296 0 0.00099630702602211533
+ 0 8 0.020001865913818001 6 0.049004571489872549 6 0.078007277065946887 6 0.10700998264203111 6 0.1360126882181005 6 0.16401530049821203 6 0.19301800607425304 6 0.22202071165030296 6 0.25102341722637367 6 0.28002612280246714 6 0.30902882837857643 6 0.33803153395467483 6 0.36703423953075048 6 0.3960369451068167 6 0.42503965068288341 6 0.45404235625893824 6 0.48304506183498419 6 0.51204776741101876 6 0.54105047298704145 6 0.56705289867541275 6 0.59605560425145676 6 0.62505830982751509 6 0.65406101540356398 6 0.683063720979602 6 0.71206642655564645 6 0.74006903883577169 6 0.7640712779325638 6 0.78307305055067933 6 1 8
+7 0 0 7 176 30 -0.49845974193988946 5.5511151231257827e-17 -0.4984597420201462 0.0089483268146138273 -0.49827232851857134 0.017896658271801511 -0.49789750179687742 0.026840276112039131 -0.49733540987531505 0.035774471365686775 -0.49658634843267219 0.044694544352988551 -0.49565076080627468 0.053595804684072615 -0.49290303172579447 0.075346325304044642 -0.49088589013437051 0.088169722677614359 -0.48847962275151374 0.10092946625209846 -0.48568667770145163 0.11361139953684614 -0.48251014169883893 0.12620150667811758 -0.4789537400487589 0.13868591245908415 -0.47108993466517823 0.163415852592278 -0.4667825129259226 0.17566144356864929 -0.46210394172821362 0.18777392268534573 -0.45705919390578387 0.19973981142373537 -0.45165384482683957 0.21154588529015061 -0.44589407239406126 0.22317917381588828 -0.43367924301027388 0.24607474800015411 -0.42722415809097064 0.25733708608267519 -0.42042818865259424 0.26840126152403326 -0.41329866758989703 0.27925492004584201 -0.40584347432648177 0.28988606637206799 -0.39807103481480161 0.30028306422903117 -0.38190960942373836 0.32058620939009602 -0.37352058653954195 0.33049240318230833 -0.36483223062232017 0.34014194463567232 -0.35585399183870486 0.34952401275714007 -0.34659579278320984 0.3586282386469849 -0.33706802847823081 0.36744470549880159 -0.31783256840648261 0.38418942651567767 -0.30814236673645462 0.39213786020594521 -0.29822074432399337 0.3998007370839044 -0.2880778181520447 0.40717000430624434 -0.27772403922646771 0.4142380687727481 -0.2671701925760353 0.42099779712629237 -0.24530093248260482 0.43411740341118304 -0.23397173314384981 0.44045440765160276 -0.22245215894003881 0.4464461176718979 -0.21075485355309911 0.45208571167999295 -0.1988927446430156 0.4573669568939458 -0.18687904384783074 0.46228420954194777 -0.16257545024195588 0.4713806215787068 -0.15028550178332487 0.47555980179552071 -0.13787089937933658 0.47936490704444296 -0.12534531354640108 0.48279151957480465 -0.11272258734575356 0.48583585235359056 -0.10001673638345429 0.48849474906543833 -0.074467161498213391 0.49303662062741516 -0.061623379074405668 0.49491960587706879 -0.048724790865963395 0.4964121208798829 -0.035785641598587326 0.49751229819650961 -0.022820231396680952 0.49821892192997103 -0.0098429157833505119 0.49853142772565862 0.016106704431904043 0.49836837930756089 0.029079068450656366 0.49789282472044372 0.042034575496261703 0.49702333291732309 0.054958878270766807 0.49576064860032432 0.067837693057855503 0.49410616726635687 0.080656799722848671 0.49206193520711461 0.10614728398186972 0.48719936527521757 0.11881871994146449 0.48438101626873514 0.13140219429759961 0.48117830617679613 0.14388373221321213 0.47759456718621857 0.15624953930806487 0.47363375998346896 0.1684860016587463 0.46930047375466344 0.19267337047859451 0.45989938000580205 0.20462433232957514 0.454831550960063 0.21641914031941159 0.44940166299736972 0.22804465477306093 0.44361552546189131 0.23948802737263827 0.43747953309294718 0.25073670115741697 0.43100066602500614 0.27282012067305633 0.41737231481884779 0.28365491696832135 0.41022279947579188 0.29427053733507741 0.40274551500658395 0.30465511230955428 0.39494855556774294 0.3147971650387576 0.38684053822424286 0.32468561128046936 0.37843060294951247 0.3439339085257383 0.36102622340698892 0.35329380360211438 0.35203173928839354 0.3623787575077505 0.34275462813583213 0.37117856398323062 0.33320500092396649 0.37968349763434756 0.32339341173593755 0.38788431393210299 0.31333085776336517 0.40366018567682355 0.29272670175549986 0.41123527724580428 0.28218505254287873 0.41848876559125336 0.27141527605369847 0.4254124455831701 0.26042916531053645 0.43199866528994996 0.24923886197333414 0.4382403259783843 0.23785685633939771 0.45002143957705693 0.21473511902410697 0.45556091946250782 0.20299533440090509 0.46074278222251241 0.19108947545914737 0.46556109552497815 0.17903062680719156 0.47001053425322065 0.16683211567639783 0.47408638050596458 0.15450751192112824 0.48148266811747487 0.12963374454121504 0.48480312641152884 0.11708452396310917 0.48774179423462566 0.10443678067704894 0.49029520846965885 0.091704457651150123 0.49246054712729503 0.078901626429025012 0.49423562934597415 0.066042487129782237 0.49700220291992914 0.040240249925186281 0.49799370060981829 0.027297092940454161 0.49859187540920225 0.014326226904836934 0.49879584807835708 0.0013419915002499429 0.49860539319021008 -0.011641263320482589 0.49802093913033968 -0.024609177333627601 0.49606619854996026 -0.050485582867736759 0.49469590755368126 -0.063394133637904596 0.49293378400699489 -0.076258662366574664 0.49078156165882281 -0.089064907439020041 0.48824161910815056 -0.10179871561425878 0.48531697980402821 -0.1144460420250539 0.47904761013846098 -0.13824190561788788 0.47577762577541255 -0.14941018425965283 0.47220400470345664 -0.16048775082137348 0.46832979109421613 -0.17146471064138297 0.46415842754390546 -0.18233130967818242 0.45969375507332966 -0.19307793451044097 0.44963776500089636 -0.21553734643050787 0.44397586891335977 -0.22721858408301207 0.43796021704927651 -0.23872567370005107 0.431597271609376 -0.25004578408910383 0.42489406481111619 -0.261166404459586 0.41785819888868281 -0.27207534442284936 0.40313749452484132 -0.29344612440708334 0.39545262237743872 -0.30390801336073398 0.38745140743560269 -0.31413453467832014 0.3791425300056942 -0.32411424057309685 0.37053517291561378 -0.33383610164638744 0.36163902151480171 -0.34328950688758397 0.34328950688767834 -0.36163902151471167 0.33383610164648309 -0.37053517291552807 0.32411424057319344 -0.37914253000561288 0.31413453467841829 -0.38745140743552409 0.30390801336083451 -0.39545262237736106 0.29344612440718665 -0.40313749452476466 0.27207534442295295 -0.4178581988886122 0.2611664044596948 -0.42489406481104941 0.25004578408921774 -0.4315972716093131 0.23872567370016615 -0.43796021704921617 0.22721858408312473 -0.44397586891330126 0.21553734643061845 -0.44963776500083991 0.19185287885273167 -0.46024226261526885 0.17984959469703193 -0.4651848884232746 0.16769841421048473 -0.46976200450087868 0.15541270354099895 -0.47396834343600663 0.14300604064392003 -0.47779925645548105 0.13049221528203017 -0.48125071342502085 0.1057129655315362 -0.48728208042655857 0.093453804184493103 -0.48988795256495898 0.081120345801083196 -0.49213385497758938 0.068725276567223673 -0.49401728520239951 0.056281368038084652 -0.49553630260214315 0.043801477138089309 -0.4966895283643778 0.020581744959880736 -0.49815038972307757 0.009847986588270425 -0.49855529129348725 -0.00089458572573335551 -0.49869033896615311 -0.011637832608796162 -0.49855532823746557 -0.022373618542432864 -0.49815036134674673 -0.033093811863007666 -0.49747584727625055 -0.052258327669681956 -0.49578568662910022 -0.060711514942100808 -0.49487038443655956 -0.069145817594788814 -0.49378695076538676 -0.077557224372637124 -0.49253586040474523 -0.085941741749629805 -0.4911177073411151 -0.094295393928843854 -0.48953320475829265 -0.19758290235664983 -0.4678044696224789 -0.2881413176812947 -0.42622953281465648 -0.36815177824908774 -0.36423086794942872 -0.43221805763491938 -0.28528478341752833 -0.47641760502413488 -0.19422486767018315 -0.49845945265814579 -0.097047406756765875 -0.49845974193988946 -1.1102230246251565e-16
+ 0 8 0.020001865913818001 6 0.049004571489872549 6 0.078007277065946887 6 0.10700998264203111 6 0.1360126882181005 6 0.16401530049821203 6 0.19301800607425304 6 0.22202071165030296 6 0.25102341722637367 6 0.28002612280246714 6 0.30902882837857643 6 0.33803153395467483 6 0.36703423953075048 6 0.3960369451068167 6 0.42503965068288341 6 0.45404235625893824 6 0.48304506183498419 6 0.51204776741101876 6 0.54105047298704145 6 0.56705289867541275 6 0.59605560425145676 6 0.62505830982751509 6 0.65406101540356398 6 0.683063720979602 6 0.71206642655564645 6 0.74006903883577169 6 0.7640712779325638 6 0.78307305055067933 6 1 8
+1 6.2831853071795862 0 0 1
+1 0 0 0 1
+1 0 1 1 0
+Curves 11
+2 5.1507962050250302e-18 0 0 1 0 0 0 -4.2488471045215345e-34 1 0 -1 -4.2488471045215345e-34 0.5
+7 0 0 3 82 80 1.7347234759767455e-18 4.8148248609680896e-35 0.5 1.9081958235744875e-17 8.3390424239522198e-19 0.50680934489010054 0.023319592761920728 2.4339552601114771e-18 0.51987475296392538 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563684e-19 0.50415566868708572 1.9833361382086636 2.9747251477338528e-19 0.50242904741988115 2 1.5430549669256652e-19 0.50126000000000004
+ 0.5 4 0.50778579154106807 1 0.51493896186836807 1 0.52142989606068113 1 0.52780603459347397 1 0.53412586335306578 1 0.54041199030005094 1 0.54667577936015643 1 0.55292381112532962 1 0.55916025190260255 1 0.5653879195921806 1 0.57160880715469398 1 0.57782437955598598 1 0.58403574296875027 1 0.59024375666548634 1 0.59644909646961619 1 0.60265230642268297 1 0.60885382606372285 1 0.61505401798985182 1 0.62125318173927924 1 0.62745156790483447 1 0.63364938657642622 1 0.63984681476564487 1 0.64604400252387284 1 0.65224107719197866 1 0.65843814702339287 1 0.66463530409444338 1 0.67083262664014676 1 0.67703018129340153 1 0.68322802460079535 1 0.68942620435364577 1 0.6956247608491497 1 0.70182372758617795 1 0.70802313283227647 1 0.71422299963579394 1 0.72042334678553011 1 0.72662418966202291 1 0.73282554077662276 1 0.73902740938723799 1 0.74522980205033462 1 0.75143272397473226 1 0.7576361788107443 1 0.76384016917612763 1 0.77004469469988879 1 0.77624975622273162 1 0.78245535199183813 1 0.78866148146223336 1 0.79486814257909799 1 0.80107533366524408 1 0.80728305313818827 1 0.81349129821414223 1 0.81970006782488436 1 0.82590935999263415 1 0.83211917404824265 1 0.83832950937280215 1 0.84454036541318844 1 0.85075174339565085 1 0.85696364404959746 1 0.86317606924070422 1 0.86938902133984342 1 0.8756025049058378 1 0.88181652510541075 1 0.88803108783675355 1 0.89424619887020362 1 0.9004618674628867 1 0.90667810196496923 1 0.9128949135006994 1 0.91911231334857058 1 0.92533031475551653 1 0.93154893314111964 1 0.937768184191767 1 0.94398808685004154 1 0.95020866049980723 1 0.95642992488615519 1 0.96265190400039857 1 0.96887462165344695 1 0.97509810624240079 1 0.98132238514225778 1 0.98754748968945583 1 1.0000000000000027 4
+2 2 0 0 1 0 0 -0 3.0783524855876494e-19 1 0 -1 3.0783524855876494e-19 0.50126000000000004
+7 0 0 5 118 30 0.0039998007999999478 1.4464221294116791e-16 0.49283762810360693 0.0039998007999999478 0.017960209128777795 0.492837629136466 0.0039998007999999478 0.035920479942518978 0.49201948200409823 0.0039998007999999478 0.05383094219538017 0.49038319833905392 0.0039998007999999486 0.07164213738343933 0.48793329662560891 0.0039998007999999486 0.10696790048027376 0.48142430980543938 0.0039998007999999495 0.12448258971656835 0.47736520234338314 0.0039998007999999504 0.14180004390736139 0.47251052802953802 0.0039998007999999504 0.1588724420995164 0.46687369271157053 0.0039998007999999521 0.19127663869964526 0.45451269175820797 0.003999800799999953 0.20664736888523003 0.44789029105706518 0.0039998007999999538 0.22172779165774589 0.4406196027152558 0.0039998007999999547 0.23648180004706143 0.43271803490035937 0.0039998007999999564 0.26633387128159552 0.41506356960465701 0.0039998007999999573 0.28137656041278691 0.40521683430128097 0.0039998007999999582 0.29595991184347059 0.39469124912073661 0.0039998007999999599 0.31004365465635952 0.38351587973919699 0.0039998007999999625 0.33713667972374545 0.35993104477059212 0.0039998007999999642 0.35014605503016377 0.34752149818061068 0.0039998007999999651 0.36258068536716709 0.33452757457279586 0.0039998007999999668 0.37440623336229656 0.3209851557693279 0.0039998007999999695 0.39639132844111097 0.29336589750151815 0.0039998007999999712 0.40659433110434756 0.27932350393004124 0.0039998007999999729 0.4161726983137447 0.2648411884737803 0.0039998007999999747 0.42510176952640671 0.2499562374210694 0.0039998007999999781 0.44191350330830698 0.21891540237858287 0.0039998007999999799 0.44974746030041957 0.20273301039595948 0.0039998007999999816 0.45683829575581447 0.18620478802063381 0.0039998007999999833 0.46316642882234832 0.16937637679661804 0.0039998007999999877 0.4742664485089938 0.13521402894472642 0.0039998007999999894 0.47903837325250231 0.11787997498507034 0.0039998007999999911 0.48301681222360132 0.10034047368951202 0.0039998007999999929 0.48619077923797649 0.082643959178922588 0.0039998007999999972 0.49091662604437208 0.047035774448977385 0.0039998007999999989 0.49246852206744923 0.029123981932133558 0.0039998008000000015 0.49320284007104143 0.011153927413206522 0.0039998008000000033 0.49311755228672283 -0.0068247660439579522 0.0039998008000000067 0.49131275536264374 -0.042699815254525895 0.0039998008000000094 0.48959324002424415 -0.060596294222031756 0.0039998008000000111 0.48705912409229635 -0.078401920826762603 0.0039998008000000128 0.48371740535284191 -0.096067526061194786 0.0039998008000000172 0.47544172078450253 -0.13102163158149102 0.0039998008000000189 0.47050772653249573 -0.14831025191840866 0.0039998008000000206 0.46478908020454873 -0.16536191361220137 0.0039998008000000224 0.45830157344734579 -0.18212952967734794 0.0039998008000000267 0.4438290057578505 -0.21500537540443085 0.0039998008000000284 0.43584389511903265 -0.23111371797959487 0.0039998008000000302 0.42713006043041368 -0.24684684123478884 0.0039998008000000319 0.41771156434437268 -0.26216129923728038 0.0039998008000000345 0.39752129431630984 -0.29187037939793314 0.0039998008000000362 0.38674945103009922 -0.30626510359294112 0.003999800800000038 0.37532893947475843 -0.320158771148989 0.0039998008000000397 0.36329129660380532 -0.33351301566315561 0.0039998008000000423 0.33805181579996968 -0.35907167479369306 0.003999800800000044 0.32484989118122565 -0.3712761771921631 0.0039998008000000449 0.31110083121445564 -0.38287037541256136 0.0039998008000000458 0.2968426029787164 -0.39382225287764538 0.0039998008000000484 0.26738958026057702 -0.41438425425307734 0.0039998008000000492 0.25219468462076122 -0.42399444878431314 0.0039998008000000501 0.23657230198107593 -0.43290529851949539 0.003999800800000051 0.22056557247169545 -0.44109219676280781 0.0039998008000000527 0.18787418534598097 -0.45597674094780882 0.0039998008000000536 0.1711894154499724 -0.46267443801098407 0.0039998008000000545 0.15421096082154112 -0.46860690467958327 0.0039998008000000553 0.13698570628995585 -0.47375775885682647 0.0039998008000000562 0.10213835318124888 -0.48247202469597078 0.0039998008000000562 0.084516134919688513 -0.48603546628730249 0.0039998008000000571 0.066743757974251874 -0.4887931283501773 0.0039998008000000571 0.048870299535826237 -0.4907373957913595 0.0039998008000000571 0.013020762018454845 -0.49299285751545846 0.0039998008000000571 -0.0049554401869569163 -0.4933040595448604 0.0039998008000000571 -0.022933303335245646 -0.49279561194172494 0.0039998008000000571 -0.040863182799573058 -0.49146891874897525 0.0039998008000000562 -0.074683216604735203 -0.48741217482147475 0.0039998008000000562 -0.090592584910555191 -0.48484148786412823 0.0039998008000000562 -0.1063880091701746 -0.48162213020155664 0.0039998008000000553 -0.12203441573744345 -0.477761250398409 0.0039998008000000545 -0.1547443906702809 -0.46825812471422884 0.0039998008000000536 -0.17176326955239504 -0.4624617073699766 0.0039998008000000527 -0.18850606477187051 -0.45589353482485961 0.0039998008000000519 -0.20492654225661341 -0.44857174463350957 0.0039998008000000501 -0.23703370006806201 -0.43246563197189447 0.0039998008000000492 -0.25272049066791191 -0.42368125418461622 0.0039998008000000484 -0.26799592162305202 -0.41418792539654053 0.0039998008000000475 -0.28281781088641067 -0.40401186077914053 0.0039998008000000449 -0.31147492083819839 -0.38235437946183115 0.0039998008000000432 -0.32531023995045261 -0.37087288837856547 0.0039998008000000423 -0.33861254338511482 -0.35876872357152956 0.0039998008000000406 -0.35134509775042155 -0.34607530985545631 0.003999800800000038 -0.37560333468118923 -0.31958353020950853 0.0039998008000000362 -0.38712910056239114 -0.30578507329272431 0.0039998008000000345 -0.39801784266367529 -0.29147083640089355 0.0039998008000000328 -0.40823949247612618 -0.27668034731009861 0.0039998008000000302 -0.42466724005081513 -0.25043124935628519 0.0039998008000000293 -0.43120323635765867 -0.23917901708340872 0.0039998008000000276 -0.43736554466032707 -0.2277152028352083 0.0039998008000000267 -0.44314523355603147 -0.21605642180480858 0.0039998008000000232 -0.45597674094781177 -0.18787418534597608 0.0039998008000000215 -0.46267443801098573 -0.17118941544996594 0.0039998008000000198 -0.46860690467958488 -0.15421096082153435 0.003999800800000018 -0.47375775885682897 -0.13698570628995027 0.0039998008000000137 -0.48202128737986993 -0.10394079814626403 0.003999800800000012 -0.48528970800064286 -0.088159991513890215 0.0039998008000000102 -0.48791144579582579 -0.072254452156979343 0.0039998008000000085 -0.48988067921215045 -0.056259498231603533 0.003999800800000005 -0.49255879636342126 -0.023545097293108594 0.0039998008000000033 -0.49321629826348434 -0.006821364792962214 0.0039998008000000015 -0.49316370297898399 0.0099201791530916135 0.0039998007999999998 -0.49240113643850969 0.026639450361980302 0.0039998007999999911 -0.48603550858492978 0.098818732105655532 0.0039998007999999851 -0.47328926452292719 0.15365230498503715 0.0039998007999999799 -0.45282348349031037 0.20630717762490977 0.0039998007999999738 -0.42519309493117541 0.25535561369046489 0.0039998007999999608 -0.33634440168453511 0.37139739225798474 0.0039998007999999547 -0.26489311940889626 0.43060943183772066 0.0039998007999999504 -0.18086320150689747 0.47210311356572027 0.0039998007999999478 -0.090412011291749 0.49283795281486104 0.0039998007999999478 -2.1891240752605581e-17 0.49283762810360693
+ 0 6 0.029000420763338419 4 0.058000841526677539 4 0.085001233271496168 4 0.11400165403483276 4 0.14300207479821528 4 0.17100248105233079 4 0.20000290181572761 4 0.22900332257910461 4 0.25800374334244064 4 0.28700416410574697 4 0.31600458486905331 4 0.34500500563237591 4 0.37400542639567941 4 0.40300584715895815 4 0.43200626792220864 4 0.46100668868545203 4 0.49000710944870285 4 0.51900753021194346 4 0.54500790744748351 4 0.57400832821072523 4 0.60300874897396439 4 0.63200916973720223 4 0.66100959050043506 4 0.68200989519037636 4 0.7110103159536113 4 0.73701069318915302 4 0.76401108493388792 4 0.85401116742439875 4 1 6
+7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
+ 0 4 0.012452510310546802 1 0.018677614857744929 1 0.024901893757601767 1 0.031125378346555464 1 0.037348095999604043 1 0.043570075113847251 1 0.049791339500195375 1 0.056011913149960867 1 0.062231815808235363 1 0.068451066858882623 1 0.074669685244485792 1 0.080887686651431687 1 0.08710508649930275 1 0.093321898035032894 1 0.099538132537115312 1 0.10575380112979828 1 0.11196891216324817 1 0.11818347489459106 1 0.12439749509416391 1 0.13061097866015836 1 0.13682393075929764 1 0.14303635595040423 1 0.14924825660435104 1 0.15545963458681322 1 0.16167049062719938 1 0.16788082595175882 1 0.17409064000736726 1 0.18029993217511717 1 0.18650870178585921 1 0.19271694686181326 1 0.19892466633475747 1 0.20513185742090337 1 0.21133851853776786 1 0.21754464800816323 1 0.22375024377726974 1 0.22995530530011266 1 0.23615983082387357 1 0.24236382118925689 1 0.24856727602526885 1 0.2547701979496666 1 0.26097259061276307 1 0.26717445922337818 1 0.27337581033797814 1 0.27957665321447084 1 0.28577700036420717 1 0.29197686716772447 1 0.29817627241382305 1 0.30437523915085124 1 0.31057379564635523 1 0.31677197539920571 1 0.32296981870659941 1 0.3291673733598543 1 0.33536469590555762 1 0.34156185297660818 1 0.3477589228080224 1 0.3539559974761281 1 0.36015318523435597 1 0.36635061342357461 1 0.37254843209516636 1 0.37874681826072149 1 0.38494598201014885 1 0.3911461739362776 1 0.39734769357731764 1 0.40355090353038436 1 0.40975624333451433 1 0.41596425703125028 1 0.42217562044401452 1 0.42839119284530658 1 0.43461208040781973 1 0.44083974809739779 1 0.44707618887467077 1 0.45332422063984379 1 0.45958800969994934 1 0.4658741366469345 1 0.47219396540652614 1 0.47857010393931909 1 0.48506103813163204 1 0.49221420845893199 1 0.5 1 0.50778579154106807 1 0.51493896186836796 1 0.52142989606068102 1 0.52780603459347386 1 0.53412586335306556 1 0.54041199030005072 1 0.54667577936015621 1 0.55292381112532929 1 0.55916025190260221 1 0.56538791959218027 1 0.57160880715469353 1 0.57782437955598553 1 0.58403574296874983 1 0.59024375666548579 1 0.59644909646961564 1 0.60265230642268242 1 0.60885382606372229 1 0.61505401798985115 1 0.62125318173927857 1 0.62745156790483381 1 0.63364938657642544 1 0.63984681476564409 1 0.64604400252387206 1 0.65224107719197777 1 0.65843814702339198 1 0.66463530409444249 1 0.67083262664014576 1 0.67703018129340053 1 0.68322802460079435 1 0.68942620435364477 1 0.69562476084914859 1 0.70182372758617684 1 0.70802313283227536 1 0.71422299963579272 1 0.72042334678552888 1 0.72662418966202169 1 0.73282554077662143 1 0.73902740938723666 1 0.74522980205033329 1 0.75143272397473082 1 0.75763617881074286 1 0.76384016917612618 1 0.77004469469988723 1 0.77624975622273007 1 0.78245535199183658 1 0.78866148146223181 1 0.79486814257909633 1 0.80107533366524242 1 0.8072830531381866 1 0.81349129821414046 1 0.81970006782488258 1 0.82590935999263237 1 0.83211917404824076 1 0.83832950937280026 1 0.84454036541318656 1 0.85075174339564885 1 0.85696364404959546 1 0.86317606924070223 1 0.86938902133984142 1 0.87560250490583569 1 0.88181652510540864 1 0.88803108783675144 1 0.8942461988702014 1 0.90046186746288448 1 0.90667810196496701 1 0.91289491350069707 1 0.91911231334856824 1 0.92533031475551419 1 0.9315489331411172 1 0.93776818419176455 1 0.9439880868500391 1 0.95020866049980468 1 0.95642992488615264 1 0.96265190400039602 1 0.9688746216534444 1 0.97509810624239812 1 0.98132238514225512 1 0.98754748968945316 1 1 4
+8 0 0.0025200000000000001
+7 0 0 1 2 2 2 1.5430549669256652e-19 0.50126000000000004 2 -1.5430549669256652e-19 0.49874000000000002
+ 0 2 0.0025200000000000001 2
+2 2 0 0 1 0 0 0 -3.0939065784289713e-19 1 0 -1 -3.0939065784289713e-19 0.49874000000000002
+7 0 0 8 16 3 0.0039998008000000007 -0.0089406275329953558 1.7347234759768071e-18 0.0039998008000000007 -0.0089407236897859448 0.0035109565524649444 0.0039998007999999998 -0.0073643130840476178 0.0070220371243661251 0.0039998007999999998 -0.004215444001148312 0.00970754440299775 0.0039998007999999998 3.5826169390308111e-14 0.010744945738707096 0.0039998007999999998 0.0042154440010923342 0.0097075444029867397 0.0039998007999999998 0.0073643130840676261 0.0070220371243809934 0.0039998008000000007 0.0089407236897818682 0.0035109565524602381 0.0039998008000000007 0.0089407236897818369 -0.0035109565524749677 0.0039998008000000015 0.0073643130840725804 -0.0070220371243120867 0.0039998008000000015 0.0042154440010909464 -0.0097075444031176628 0.0039998008000000015 3.8213148893252197e-14 -0.01074494573855455 0.0039998008000000015 -0.0042154440011469146 -0.0097075444031072614 0.0039998008000000015 -0.0073643130840464711 -0.0070220371243231508 0.0039998008000000007 -0.0089407236897865536 -0.0035109565524729398 0.0039998008000000007 -0.0089406275329953558 0
+ 0 9 0.50000000000011025 7 1 9
+7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
+ 0 4 0.012452510310546802 1 0.018677614857744929 1 0.024901893757601767 1 0.031125378346555464 1 0.037348095999604043 1 0.043570075113847251 1 0.049791339500195375 1 0.056011913149960867 1 0.062231815808235363 1 0.068451066858882623 1 0.074669685244485792 1 0.080887686651431687 1 0.08710508649930275 1 0.093321898035032894 1 0.099538132537115312 1 0.10575380112979828 1 0.11196891216324817 1 0.11818347489459106 1 0.12439749509416391 1 0.13061097866015836 1 0.13682393075929764 1 0.14303635595040423 1 0.14924825660435104 1 0.15545963458681322 1 0.16167049062719938 1 0.16788082595175882 1 0.17409064000736726 1 0.18029993217511717 1 0.18650870178585921 1 0.19271694686181326 1 0.19892466633475747 1 0.20513185742090337 1 0.21133851853776786 1 0.21754464800816323 1 0.22375024377726974 1 0.22995530530011266 1 0.23615983082387357 1 0.24236382118925689 1 0.24856727602526885 1 0.2547701979496666 1 0.26097259061276307 1 0.26717445922337818 1 0.27337581033797814 1 0.27957665321447084 1 0.28577700036420717 1 0.29197686716772447 1 0.29817627241382305 1 0.30437523915085124 1 0.31057379564635523 1 0.31677197539920571 1 0.32296981870659941 1 0.3291673733598543 1 0.33536469590555762 1 0.34156185297660818 1 0.3477589228080224 1 0.3539559974761281 1 0.36015318523435597 1 0.36635061342357461 1 0.37254843209516636 1 0.37874681826072149 1 0.38494598201014885 1 0.3911461739362776 1 0.39734769357731764 1 0.40355090353038436 1 0.40975624333451433 1 0.41596425703125028 1 0.42217562044401452 1 0.42839119284530658 1 0.43461208040781973 1 0.44083974809739779 1 0.44707618887467077 1 0.45332422063984379 1 0.45958800969994934 1 0.4658741366469345 1 0.47219396540652614 1 0.47857010393931909 1 0.48506103813163204 1 0.49221420845893199 1 0.5 1 0.50778579154106807 1 0.51493896186836796 1 0.52142989606068102 1 0.52780603459347386 1 0.53412586335306556 1 0.54041199030005072 1 0.54667577936015621 1 0.55292381112532929 1 0.55916025190260221 1 0.56538791959218027 1 0.57160880715469353 1 0.57782437955598553 1 0.58403574296874983 1 0.59024375666548579 1 0.59644909646961564 1 0.60265230642268242 1 0.60885382606372229 1 0.61505401798985115 1 0.62125318173927857 1 0.62745156790483381 1 0.63364938657642544 1 0.63984681476564409 1 0.64604400252387206 1 0.65224107719197777 1 0.65843814702339198 1 0.66463530409444249 1 0.67083262664014576 1 0.67703018129340053 1 0.68322802460079435 1 0.68942620435364477 1 0.69562476084914859 1 0.70182372758617684 1 0.70802313283227536 1 0.71422299963579272 1 0.72042334678552888 1 0.72662418966202169 1 0.73282554077662143 1 0.73902740938723666 1 0.74522980205033329 1 0.75143272397473082 1 0.75763617881074286 1 0.76384016917612618 1 0.77004469469988723 1 0.77624975622273007 1 0.78245535199183658 1 0.78866148146223181 1 0.79486814257909633 1 0.80107533366524242 1 0.8072830531381866 1 0.81349129821414046 1 0.81970006782488258 1 0.82590935999263237 1 0.83211917404824076 1 0.83832950937280026 1 0.84454036541318656 1 0.85075174339564885 1 0.85696364404959546 1 0.86317606924070223 1 0.86938902133984142 1 0.87560250490583569 1 0.88181652510540864 1 0.88803108783675144 1 0.8942461988702014 1 0.90046186746288448 1 0.90667810196496701 1 0.91289491350069707 1 0.91911231334856824 1 0.92533031475551419 1 0.9315489331411172 1 0.93776818419176455 1 0.9439880868500391 1 0.95020866049980468 1 0.95642992488615264 1 0.96265190400039602 1 0.9688746216534444 1 0.97509810624239812 1 0.98132238514225512 1 0.98754748968945316 1 1 4
+7 0 0 7 176 30 1.9960001992000003 1.4464221294116791e-16 0.49845974193988957 1.9960001992000003 0.0089483268146139158 0.49845974202014631 1.9960001992000003 0.017896658271801601 0.49827232851857145 1.9960001992000003 0.026840276112039221 0.49789750179687753 1.9960001992000003 0.035774471365686865 0.49733540987531516 1.9960001992000003 0.044694544352988641 0.4965863484326723 1.9960001992000003 0.053595804684072705 0.49565076080627479 1.9960001992000003 0.075346325304044726 0.49290303172579458 1.9960001992000003 0.088169722677614443 0.49088589013437062 1.9960001992000003 0.10092946625209855 0.48847962275151385 1.9960001992000003 0.11361139953684622 0.48568667770145174 1.9960001992000003 0.12620150667811766 0.48251014169883905 1.9960001992000003 0.13868591245908424 0.47895374004875901 1.9960001992000003 0.16341585259227809 0.47108993466517834 1.9960001992000003 0.17566144356864938 0.46678251292592271 1.9960001992000003 0.18777392268534582 0.46210394172821373 1.9960001992000003 0.19973981142373545 0.45705919390578398 1.9960001992000003 0.2115458852901507 0.45165384482683968 1.9960001992000003 0.22317917381588837 0.44589407239406137 1.9960001992000003 0.24607474800015419 0.433679243010274 1.9960001992000003 0.2573370860826753 0.42722415809097075 1.9960001992000003 0.26840126152403337 0.42042818865259435 1.9960001992000003 0.27925492004584213 0.41329866758989714 1.9960001992000003 0.2898860663720681 0.40584347432648188 1.9960001992000003 0.30028306422903128 0.39807103481480172 1.9960001992000003 0.32058620939009613 0.38190960942373847 1.9960001992000003 0.33049240318230844 0.37352058653954207 1.9960001992000003 0.34014194463567243 0.36483223062232029 1.9960001992000003 0.34952401275714018 0.35585399183870497 1.9960001992000003 0.35862823864698501 0.34659579278320995 1.9960001992000003 0.3674447054988017 0.33706802847823092 1.9960001992000003 0.38418942651567778 0.31783256840648272 1.9960001992000003 0.39213786020594532 0.30814236673645473 1.9960001992000003 0.39980073708390451 0.29822074432399348 1.9960001992000003 0.40717000430624445 0.28807781815204481 1.9960001992000003 0.41423806877274821 0.27772403922646782 1.9960001992000003 0.42099779712629248 0.26717019257603541 1.9960001992000003 0.43411740341118316 0.2453009324826049 1.9960001992000003 0.44045440765160287 0.23397173314384989 1.9960001992000003 0.44644611767189801 0.22245215894003889 1.9960001992000003 0.45208571167999306 0.2107548535530992 1.9960001992000003 0.45736695689394591 0.19889274464301568 1.9960001992000003 0.46228420954194788 0.18687904384783083 1.9960001992000003 0.47138062157870692 0.16257545024195597 1.9960001992000003 0.47555980179552082 0.15028550178332495 1.9960001992000003 0.47936490704444307 0.13787089937933666 1.9960001992000003 0.48279151957480476 0.12534531354640116 1.9960001992000003 0.48583585235359067 0.11272258734575365 1.9960001992000003 0.48849474906543844 0.10001673638345437 1.9960001992000003 0.49303662062741527 0.074467161498213474 1.9960001992000003 0.4949196058770689 0.061623379074405758 1.9960001992000003 0.49641212087988301 0.048724790865963485 1.9960001992000003 0.49751229819650972 0.035785641598587416 1.9960001992000003 0.49821892192997114 0.022820231396681042 1.9960001992000003 0.49853142772565873 0.0098429157833506004 1.9960001992000003 0.498368379307561 -0.016106704431903953 1.9960001992000003 0.49789282472044383 -0.029079068450656276 1.9960001992000003 0.4970233329173232 -0.042034575496261613 1.9960001992000003 0.49576064860032443 -0.054958878270766717 1.9960001992000003 0.49410616726635698 -0.067837693057855419 1.9960001992000003 0.49206193520711472 -0.080656799722848588 1.9960001992000003 0.48719936527521768 -0.10614728398186964 1.9960001992000003 0.48438101626873525 -0.11881871994146441 1.9960001992000003 0.48117830617679624 -0.13140219429759953 1.9960001992000003 0.47759456718621868 -0.14388373221321205 1.9960001992000003 0.47363375998346907 -0.15624953930806479 1.9960001992000003 0.46930047375466355 -0.16848600165874622 1.9960001992000003 0.45989938000580216 -0.19267337047859442 1.9960001992000003 0.45483155096006311 -0.20462433232957505 1.9960001992000003 0.44940166299736983 -0.21641914031941151 1.9960001992000003 0.44361552546189142 -0.22804465477306085 1.9960001992000003 0.43747953309294729 -0.23948802737263819 1.9960001992000003 0.43100066602500625 -0.25073670115741686 1.9960001992000003 0.4173723148188479 -0.27282012067305622 1.9960001992000003 0.41022279947579199 -0.28365491696832124 1.9960001992000003 0.40274551500658406 -0.2942705373350773 1.9960001992000003 0.39494855556774305 -0.30465511230955417 1.9960001992000003 0.38684053822424297 -0.31479716503875749 1.9960001992000003 0.37843060294951258 -0.32468561128046924 1.9960001992000003 0.36102622340698903 -0.34393390852573819 1.9960001992000003 0.35203173928839365 -0.35329380360211426 1.9960001992000003 0.34275462813583224 -0.36237875750775039 1.9960001992000003 0.3332050009239666 -0.37117856398323051 1.9960001992000003 0.32339341173593766 -0.37968349763434744 1.9960001992000003 0.31333085776336528 -0.38788431393210288 1.9960001992000003 0.29272670175549997 -0.40366018567682344 1.9960001992000003 0.28218505254287884 -0.41123527724580416 1.9960001992000003 0.27141527605369858 -0.41848876559125325 1.9960001992000003 0.26042916531053656 -0.42541244558316998 1.9960001992000003 0.24923886197333422 -0.43199866528994985 1.9960001992000003 0.23785685633939779 -0.43824032597838419 1.9960001992000003 0.21473511902410705 -0.45002143957705681 1.9960001992000003 0.20299533440090517 -0.45556091946250771 1.9960001992000003 0.19108947545914745 -0.4607427822225123 1.9960001992000003 0.17903062680719165 -0.46556109552497804 1.9960001992000003 0.16683211567639791 -0.47001053425322054 1.9960001992000003 0.15450751192112833 -0.47408638050596447 1.9960001992000003 0.12963374454121512 -0.48148266811747475 1.9960001992000003 0.11708452396310925 -0.48480312641152873 1.9960001992000003 0.10443678067704902 -0.48774179423462555 1.9960001992000003 0.091704457651150206 -0.49029520846965874 1.9960001992000003 0.078901626429025096 -0.49246054712729492 1.9960001992000003 0.066042487129782321 -0.49423562934597404 1.9960001992000003 0.040240249925186371 -0.49700220291992903 1.9960001992000003 0.027297092940454251 -0.49799370060981818 1.9960001992000003 0.014326226904837022 -0.49859187540920213 1.9960001992000003 0.001341991500250032 -0.49879584807835697 1.9960001992000003 -0.011641263320482501 -0.49860539319020997 1.9960001992000003 -0.02460917733362751 -0.49802093913033957 1.9960001992000003 -0.050485582867736668 -0.49606619854996015 1.9960001992000003 -0.063394133637904512 -0.49469590755368115 1.9960001992000003 -0.076258662366574581 -0.49293378400699478 1.9960001992000003 -0.089064907439019958 -0.4907815616588227 1.9960001992000003 -0.1017987156142587 -0.48824161910815045 1.9960001992000003 -0.11444604202505382 -0.4853169798040281 1.9960001992000003 -0.13824190561788779 -0.47904761013846087 1.9960001992000003 -0.14941018425965275 -0.47577762577541244 1.9960001992000003 -0.1604877508213734 -0.47220400470345653 1.9960001992000003 -0.17146471064138288 -0.46832979109421602 1.9960001992000003 -0.18233130967818234 -0.46415842754390535 1.9960001992000003 -0.19307793451044089 -0.45969375507332955 1.9960001992000003 -0.21553734643050779 -0.44963776500089625 1.9960001992000003 -0.22721858408301199 -0.44397586891335966 1.9960001992000003 -0.23872567370005099 -0.4379602170492764 1.9960001992000003 -0.25004578408910372 -0.43159727160937589 1.9960001992000003 -0.26116640445958589 -0.42489406481111608 1.9960001992000003 -0.27207534442284925 -0.4178581988886827 1.9960001992000003 -0.29344612440708323 -0.40313749452484121 1.9960001992000003 -0.30390801336073386 -0.39545262237743861 1.9960001992000003 -0.31413453467832003 -0.38745140743560258 1.9960001992000003 -0.32411424057309673 -0.37914253000569409 1.9960001992000003 -0.33383610164638733 -0.37053517291561366 1.9960001992000003 -0.34328950688758386 -0.3616390215148016 1.9960001992000003 -0.36163902151471156 -0.34328950688767823 1.9960001992000003 -0.37053517291552795 -0.33383610164648297 1.9960001992000003 -0.37914253000561277 -0.32411424057319332 1.9960001992000003 -0.38745140743552398 -0.31413453467841818 1.9960001992000003 -0.39545262237736095 -0.30390801336083439 1.9960001992000003 -0.40313749452476455 -0.29344612440718654 1.9960001992000003 -0.41785819888861209 -0.27207534442295284 1.9960001992000003 -0.4248940648110493 -0.26116640445969469 1.9960001992000003 -0.43159727160931299 -0.25004578408921763 1.9960001992000003 -0.43796021704921606 -0.23872567370016606 1.9960001992000003 -0.44397586891330115 -0.22721858408312465 1.9960001992000003 -0.4496377650008398 -0.21553734643061837 1.9960001992000003 -0.46024226261526874 -0.19185287885273158 1.9960001992000003 -0.46518488842327449 -0.17984959469703185 1.9960001992000003 -0.46976200450087857 -0.16769841421048465 1.9960001992000003 -0.47396834343600652 -0.15541270354099887 1.9960001992000003 -0.47779925645548094 -0.14300604064391995 1.9960001992000003 -0.48125071342502074 -0.13049221528203009 1.9960001992000003 -0.48728208042655846 -0.10571296553153611 1.9960001992000003 -0.48988795256495887 -0.09345380418449302 1.9960001992000003 -0.49213385497758927 -0.081120345801083113 1.9960001992000003 -0.4940172852023994 -0.068725276567223589 1.9960001992000003 -0.49553630260214304 -0.056281368038084562 1.9960001992000003 -0.49668952836437769 -0.043801477138089219 1.9960001992000003 -0.49815038972307746 -0.020581744959880646 1.9960001992000003 -0.49855529129348714 -0.0098479865882703348 1.9960001992000003 -0.498690338966153 0.00089458572573344484 1.9960001992000003 -0.49855532823746546 0.01163783260879625 1.9960001992000003 -0.49815036134674662 0.022373618542432954 1.9960001992000003 -0.49747584727625044 0.033093811863007756 1.9960001992000003 -0.49578568662910011 0.052258327669682046 1.9960001992000003 -0.49487038443655945 0.060711514942100898 1.9960001992000003 -0.49378695076538665 0.069145817594788897 1.9960001992000003 -0.49253586040474512 0.077557224372637207 1.9960001992000003 -0.49111770734111498 0.085941741749629888 1.9960001992000003 -0.48953320475829254 0.094295393928843937 1.9960001992000003 -0.46780446962247879 0.19758290235664991 1.9960001992000003 -0.42622953281465636 0.28814131768129481 1.9960001992000003 -0.36423086794942861 0.36815177824908785 1.9960001992000003 -0.28528478341752822 0.43221805763491949 1.9960001992000003 -0.19422486767018307 0.47641760502413499 1.9960001992000003 -0.097047406756765792 0.4984594526581459 1.9960001992000003 -2.1891240752605581e-17 0.49845974193988957
+ 0 8 0.020001865913818001 6 0.049004571489872549 6 0.078007277065946887 6 0.10700998264203111 6 0.1360126882181005 6 0.16401530049821203 6 0.19301800607425304 6 0.22202071165030296 6 0.25102341722637367 6 0.28002612280246714 6 0.30902882837857643 6 0.33803153395467483 6 0.36703423953075048 6 0.3960369451068167 6 0.42503965068288341 6 0.45404235625893824 6 0.48304506183498419 6 0.51204776741101876 6 0.54105047298704145 6 0.56705289867541275 6 0.59605560425145676 6 0.62505830982751509 6 0.65406101540356398 6 0.683063720979602 6 0.71206642655564645 6 0.74006903883577169 6 0.7640712779325638 6 0.78307305055067933 6 1 8
+7 0 0 3 81 79 1 -0.0012600000000000001 0 0.99166806910433225 -0.0024290474198804652 0 0.97916907934632713 -0.0041556686870856405 0 0.9625013807263838 -0.0064065754633828211 0 0.95000132931521664 -0.0080695147108952852 0 0.93750127960794405 -0.009707852209573575 0 0.92500124260478966 -0.011322315812076994 0 0.91250118837727034 -0.012912930886184051 0 0.90000115960536642 -0.014480856552602568 0 0.88750112337475495 -0.016025977431048548 0 0.87500108849312463 -0.01754880638747152 0 0.86250106246199998 -0.019049805988652704 0 0.85000102944828482 -0.020529010547511328 0 0.83750100916791592 -0.021987024618123435 0 0.82500098575434166 -0.023423790211651842 0 0.81250096307329922 -0.024839539588589164 0 0.80000095229575618 -0.02623460040061484 0 0.7875009305699 -0.027608627134194662 0 0.77500092060707626 -0.028962080624074963 0 0.76250090701572382 -0.030294657285077038 0 0.75000090093676053 -0.031606513785123712 0 0.73750089105646288 -0.032897325203243725 0 0.72500088155224662 -0.034167037781514191 0 0.71250087862114031 -0.035415590209551026 0 0.70000087895100138 -0.036642679019159449 0 0.68750087227697532 -0.037847781327729632 0 0.6750008748552182 -0.039030894820797343 0 0.66250087359381438 -0.04019134768145604 0 0.65000087480075985 -0.04132883243621395 0 0.63750087815684497 -0.042442849673491136 0 0.625000877465687 -0.043532704745740072 0 0.61250088448934137 -0.044598076807669175 0 0.60000088431277843 -0.04563794173852994 0 0.58750089092946689 -0.046651919785977214 0 0.57500089296569545 -0.047638951624771643 0 0.5625008930402241 -0.048598256747966771 0 0.5500009010820599 -0.049529215643651064 0 0.53750089643319277 -0.050430285415768737 0 0.52500090129826238 -0.051301060818450712 0 0.51250089779487273 -0.052139902072950384 0 0.50000089749479981 -0.052945976246431804 0 0.48750088860344998 -0.053717653632727083 0 0.47500087974225197 -0.054453887628772561 0 0.4625008713526475 -0.055153292696117018 0 0.45000085526456401 -0.055814057545055237 0 0.43750083301262427 -0.056434614408511809 0 0.42500080860956885 -0.057013445108689248 0 0.41250077772299104 -0.057548589221626589 0 0.40000073803096653 -0.058038007077278866 0 0.38750069220288164 -0.058479817687152012 0 0.37500063518954613 -0.058871583983993149 0 0.3625005686566567 -0.059211135066700046 0 0.3500004886680172 -0.059495790765773687 0 0.33750039371847834 -0.059722842002354941 0 0.32500028290526284 -0.059889604063107046 0 0.31250015190983488 -0.059992723391208563 0 0.29999999813430406 -0.060029097107844702 0 0.2874998173873341 -0.059994687799052197 0 0.27499960455498923 -0.059885944767455929 0 0.26249935422640547 -0.059698303969986058 0 0.24999905865034713 -0.059427166686205624 0 0.23749870917010102 -0.059067284443917699 0 0.22499829176985606 -0.058613039953011835 0 0.21249779146755365 -0.058057937119780946 0 0.19999719409576988 -0.057394559096993807 0 0.18749645845558685 -0.056615244552909234 0 0.17499556762814103 -0.055710017225365414 0 0.16249444360960999 -0.054668793588188679 0 0.14999303957538462 -0.053478201474552511 0 0.13749122200948111 -0.052123698918757067 0 0.12498884671342025 -0.050586635395975146 0 0.11248561848475973 -0.048844854472586741 0 0.099981138068765518 -0.046869505690216813 0 0.087474479216191003 -0.044623807360225462 0 0.074964621118000355 -0.04205569685918592 0 0.062446492760321902 -0.039098544280603768 0 0.049918541725142838 -0.035624947347748813 0 0.037328260760967027 -0.031515506079397577 0 0.019422298161866122 -0.023907069430545717 0 0.0056742009741973755 -0.01660351401419858 0 0 0 0
+ 0 4 0.024905020621093604 1 0.037355229715489859 1 0.049803787515203535 1 0.062250756693110927 1 0.074696191999208086 1 0.087140150227694502 1 0.09958267900039075 1 0.11202382629992173 1 0.12446363161647073 1 0.13690213371776525 1 0.14933937048897158 1 0.16177537330286337 1 0.1742101729986055 1 0.18664379607006579 1 0.19907626507423062 1 0.21150760225959656 1 0.22393782432649634 1 0.23636694978918213 1 0.24879499018832782 1 0.26122195732031672 1 0.27364786151859527 1 0.28607271190080846 1 0.29849651320870207 1 0.31091926917362644 1 0.32334098125439875 1 0.33576165190351764 1 0.34818128001473453 1 0.36059986435023433 1 0.37301740357171842 1 0.38543389372362652 1 0.39784933266951494 1 0.41026371484180674 1 0.42267703707553572 1 0.43508929601632645 1 0.44750048755453947 1 0.45991061060022531 1 0.47231966164774714 1 0.48472764237851379 1 0.49713455205053769 1 0.5095403958993332 1 0.52194518122552613 1 0.53434891844675636 1 0.54675162067595628 1 0.55915330642894168 1 0.57155400072841434 1 0.58395373433544895 1 0.5963525448276461 1 0.60875047830170248 1 0.62114759129271047 1 0.63354395079841141 1 0.64593963741319882 1 0.6583347467197086 1 0.67072939181111524 1 0.68312370595321636 1 0.69551784561604479 1 0.70791199495225621 1 0.72030637046871193 1 0.73270122684714922 1 0.74509686419033272 1 0.75749363652144297 1 0.76989196402029769 1 0.78229234787255519 1 0.79469538715463528 1 0.80710180706076873 1 0.81951248666902865 1 0.83192851406250057 1 0.84435124088802904 1 0.85678238569061316 1 0.86922416081563947 1 0.88167949619479558 1 0.89415237774934153 1 0.90664844127968758 1 0.91917601939989868 1 0.931748273293869 1 0.94438793081305228 1 0.95714020787863818 1 0.97012207626326408 1 1 4
+Polygon3D 0
+PolygonOnTriangulations 0
+Surfaces 6
+7 0 0 0 1 0 0
+8 0.5 1
+7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
+ 0 4 0.012452510310546802 1 0.018677614857744929 1 0.024901893757601767 1 0.031125378346555464 1 0.037348095999604043 1 0.043570075113847251 1 0.049791339500195375 1 0.056011913149960867 1 0.062231815808235363 1 0.068451066858882623 1 0.074669685244485792 1 0.080887686651431687 1 0.08710508649930275 1 0.093321898035032894 1 0.099538132537115312 1 0.10575380112979828 1 0.11196891216324817 1 0.11818347489459106 1 0.12439749509416391 1 0.13061097866015836 1 0.13682393075929764 1 0.14303635595040423 1 0.14924825660435104 1 0.15545963458681322 1 0.16167049062719938 1 0.16788082595175882 1 0.17409064000736726 1 0.18029993217511717 1 0.18650870178585921 1 0.19271694686181326 1 0.19892466633475747 1 0.20513185742090337 1 0.21133851853776786 1 0.21754464800816323 1 0.22375024377726974 1 0.22995530530011266 1 0.23615983082387357 1 0.24236382118925689 1 0.24856727602526885 1 0.2547701979496666 1 0.26097259061276307 1 0.26717445922337818 1 0.27337581033797814 1 0.27957665321447084 1 0.28577700036420717 1 0.29197686716772447 1 0.29817627241382305 1 0.30437523915085124 1 0.31057379564635523 1 0.31677197539920571 1 0.32296981870659941 1 0.3291673733598543 1 0.33536469590555762 1 0.34156185297660818 1 0.3477589228080224 1 0.3539559974761281 1 0.36015318523435597 1 0.36635061342357461 1 0.37254843209516636 1 0.37874681826072149 1 0.38494598201014885 1 0.3911461739362776 1 0.39734769357731764 1 0.40355090353038436 1 0.40975624333451433 1 0.41596425703125028 1 0.42217562044401452 1 0.42839119284530658 1 0.43461208040781973 1 0.44083974809739779 1 0.44707618887467077 1 0.45332422063984379 1 0.45958800969994934 1 0.4658741366469345 1 0.47219396540652614 1 0.47857010393931909 1 0.48506103813163204 1 0.49221420845893199 1 0.5 1 0.50778579154106807 1 0.51493896186836796 1 0.52142989606068102 1 0.52780603459347386 1 0.53412586335306556 1 0.54041199030005072 1 0.54667577936015621 1 0.55292381112532929 1 0.55916025190260221 1 0.56538791959218027 1 0.57160880715469353 1 0.57782437955598553 1 0.58403574296874983 1 0.59024375666548579 1 0.59644909646961564 1 0.60265230642268242 1 0.60885382606372229 1 0.61505401798985115 1 0.62125318173927857 1 0.62745156790483381 1 0.63364938657642544 1 0.63984681476564409 1 0.64604400252387206 1 0.65224107719197777 1 0.65843814702339198 1 0.66463530409444249 1 0.67083262664014576 1 0.67703018129340053 1 0.68322802460079435 1 0.68942620435364477 1 0.69562476084914859 1 0.70182372758617684 1 0.70802313283227536 1 0.71422299963579272 1 0.72042334678552888 1 0.72662418966202169 1 0.73282554077662143 1 0.73902740938723666 1 0.74522980205033329 1 0.75143272397473082 1 0.75763617881074286 1 0.76384016917612618 1 0.77004469469988723 1 0.77624975622273007 1 0.78245535199183658 1 0.78866148146223181 1 0.79486814257909633 1 0.80107533366524242 1 0.8072830531381866 1 0.81349129821414046 1 0.81970006782488258 1 0.82590935999263237 1 0.83211917404824076 1 0.83832950937280026 1 0.84454036541318656 1 0.85075174339564885 1 0.85696364404959546 1 0.86317606924070223 1 0.86938902133984142 1 0.87560250490583569 1 0.88181652510540864 1 0.88803108783675144 1 0.8942461988702014 1 0.90046186746288448 1 0.90667810196496701 1 0.91289491350069707 1 0.91911231334856824 1 0.92533031475551419 1 0.9315489331411172 1 0.93776818419176455 1 0.9439880868500391 1 0.95020866049980468 1 0.95642992488615264 1 0.96265190400039602 1 0.9688746216534444 1 0.97509810624239812 1 0.98132238514225512 1 0.98754748968945316 1 1 4
+7 0 0 0 1 0 0
+8 0.39936054338562016 0.5
+7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
+ 0 4 0.012452510310546802 1 0.018677614857744929 1 0.024901893757601767 1 0.031125378346555464 1 0.037348095999604043 1 0.043570075113847251 1 0.049791339500195375 1 0.056011913149960867 1 0.062231815808235363 1 0.068451066858882623 1 0.074669685244485792 1 0.080887686651431687 1 0.08710508649930275 1 0.093321898035032894 1 0.099538132537115312 1 0.10575380112979828 1 0.11196891216324817 1 0.11818347489459106 1 0.12439749509416391 1 0.13061097866015836 1 0.13682393075929764 1 0.14303635595040423 1 0.14924825660435104 1 0.15545963458681322 1 0.16167049062719938 1 0.16788082595175882 1 0.17409064000736726 1 0.18029993217511717 1 0.18650870178585921 1 0.19271694686181326 1 0.19892466633475747 1 0.20513185742090337 1 0.21133851853776786 1 0.21754464800816323 1 0.22375024377726974 1 0.22995530530011266 1 0.23615983082387357 1 0.24236382118925689 1 0.24856727602526885 1 0.2547701979496666 1 0.26097259061276307 1 0.26717445922337818 1 0.27337581033797814 1 0.27957665321447084 1 0.28577700036420717 1 0.29197686716772447 1 0.29817627241382305 1 0.30437523915085124 1 0.31057379564635523 1 0.31677197539920571 1 0.32296981870659941 1 0.3291673733598543 1 0.33536469590555762 1 0.34156185297660818 1 0.3477589228080224 1 0.3539559974761281 1 0.36015318523435597 1 0.36635061342357461 1 0.37254843209516636 1 0.37874681826072149 1 0.38494598201014885 1 0.3911461739362776 1 0.39734769357731764 1 0.40355090353038436 1 0.40975624333451433 1 0.41596425703125028 1 0.42217562044401452 1 0.42839119284530658 1 0.43461208040781973 1 0.44083974809739779 1 0.44707618887467077 1 0.45332422063984379 1 0.45958800969994934 1 0.4658741366469345 1 0.47219396540652614 1 0.47857010393931909 1 0.48506103813163204 1 0.49221420845893199 1 0.5 1 0.50778579154106807 1 0.51493896186836796 1 0.52142989606068102 1 0.52780603459347386 1 0.53412586335306556 1 0.54041199030005072 1 0.54667577936015621 1 0.55292381112532929 1 0.55916025190260221 1 0.56538791959218027 1 0.57160880715469353 1 0.57782437955598553 1 0.58403574296874983 1 0.59024375666548579 1 0.59644909646961564 1 0.60265230642268242 1 0.60885382606372229 1 0.61505401798985115 1 0.62125318173927857 1 0.62745156790483381 1 0.63364938657642544 1 0.63984681476564409 1 0.64604400252387206 1 0.65224107719197777 1 0.65843814702339198 1 0.66463530409444249 1 0.67083262664014576 1 0.67703018129340053 1 0.68322802460079435 1 0.68942620435364477 1 0.69562476084914859 1 0.70182372758617684 1 0.70802313283227536 1 0.71422299963579272 1 0.72042334678552888 1 0.72662418966202169 1 0.73282554077662143 1 0.73902740938723666 1 0.74522980205033329 1 0.75143272397473082 1 0.75763617881074286 1 0.76384016917612618 1 0.77004469469988723 1 0.77624975622273007 1 0.78245535199183658 1 0.78866148146223181 1 0.79486814257909633 1 0.80107533366524242 1 0.8072830531381866 1 0.81349129821414046 1 0.81970006782488258 1 0.82590935999263237 1 0.83211917404824076 1 0.83832950937280026 1 0.84454036541318656 1 0.85075174339564885 1 0.85696364404959546 1 0.86317606924070223 1 0.86938902133984142 1 0.87560250490583569 1 0.88181652510540864 1 0.88803108783675144 1 0.8942461988702014 1 0.90046186746288448 1 0.90667810196496701 1 0.91289491350069707 1 0.91911231334856824 1 0.92533031475551419 1 0.9315489331411172 1 0.93776818419176455 1 0.9439880868500391 1 0.95020866049980468 1 0.95642992488615264 1 0.96265190400039602 1 0.9688746216534444 1 0.97509810624239812 1 0.98132238514225512 1 0.98754748968945316 1 1 4
+7 0 0 0 1 0 0
+8 0 0.0025200000000000001
+7 0 0 1 2 2 2 1.5430549669256652e-19 0.50126000000000004 2 -1.5430549669256652e-19 0.49874000000000002
+ 0 2 0.0025200000000000001 2
+1 0.0039998007999999131 8.9131061709910073e-17 0 0 0 1 1 0 -0 -0 1 0
+7 0 0 0 1 0 0
+8 0 0.14988159487986658
+7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
+ 0 4 0.012452510310546802 1 0.018677614857744929 1 0.024901893757601767 1 0.031125378346555464 1 0.037348095999604043 1 0.043570075113847251 1 0.049791339500195375 1 0.056011913149960867 1 0.062231815808235363 1 0.068451066858882623 1 0.074669685244485792 1 0.080887686651431687 1 0.08710508649930275 1 0.093321898035032894 1 0.099538132537115312 1 0.10575380112979828 1 0.11196891216324817 1 0.11818347489459106 1 0.12439749509416391 1 0.13061097866015836 1 0.13682393075929764 1 0.14303635595040423 1 0.14924825660435104 1 0.15545963458681322 1 0.16167049062719938 1 0.16788082595175882 1 0.17409064000736726 1 0.18029993217511717 1 0.18650870178585921 1 0.19271694686181326 1 0.19892466633475747 1 0.20513185742090337 1 0.21133851853776786 1 0.21754464800816323 1 0.22375024377726974 1 0.22995530530011266 1 0.23615983082387357 1 0.24236382118925689 1 0.24856727602526885 1 0.2547701979496666 1 0.26097259061276307 1 0.26717445922337818 1 0.27337581033797814 1 0.27957665321447084 1 0.28577700036420717 1 0.29197686716772447 1 0.29817627241382305 1 0.30437523915085124 1 0.31057379564635523 1 0.31677197539920571 1 0.32296981870659941 1 0.3291673733598543 1 0.33536469590555762 1 0.34156185297660818 1 0.3477589228080224 1 0.3539559974761281 1 0.36015318523435597 1 0.36635061342357461 1 0.37254843209516636 1 0.37874681826072149 1 0.38494598201014885 1 0.3911461739362776 1 0.39734769357731764 1 0.40355090353038436 1 0.40975624333451433 1 0.41596425703125028 1 0.42217562044401452 1 0.42839119284530658 1 0.43461208040781973 1 0.44083974809739779 1 0.44707618887467077 1 0.45332422063984379 1 0.45958800969994934 1 0.4658741366469345 1 0.47219396540652614 1 0.47857010393931909 1 0.48506103813163204 1 0.49221420845893199 1 0.5 1 0.50778579154106807 1 0.51493896186836796 1 0.52142989606068102 1 0.52780603459347386 1 0.53412586335306556 1 0.54041199030005072 1 0.54667577936015621 1 0.55292381112532929 1 0.55916025190260221 1 0.56538791959218027 1 0.57160880715469353 1 0.57782437955598553 1 0.58403574296874983 1 0.59024375666548579 1 0.59644909646961564 1 0.60265230642268242 1 0.60885382606372229 1 0.61505401798985115 1 0.62125318173927857 1 0.62745156790483381 1 0.63364938657642544 1 0.63984681476564409 1 0.64604400252387206 1 0.65224107719197777 1 0.65843814702339198 1 0.66463530409444249 1 0.67083262664014576 1 0.67703018129340053 1 0.68322802460079435 1 0.68942620435364477 1 0.69562476084914859 1 0.70182372758617684 1 0.70802313283227536 1 0.71422299963579272 1 0.72042334678552888 1 0.72662418966202169 1 0.73282554077662143 1 0.73902740938723666 1 0.74522980205033329 1 0.75143272397473082 1 0.75763617881074286 1 0.76384016917612618 1 0.77004469469988723 1 0.77624975622273007 1 0.78245535199183658 1 0.78866148146223181 1 0.79486814257909633 1 0.80107533366524242 1 0.8072830531381866 1 0.81349129821414046 1 0.81970006782488258 1 0.82590935999263237 1 0.83211917404824076 1 0.83832950937280026 1 0.84454036541318656 1 0.85075174339564885 1 0.85696364404959546 1 0.86317606924070223 1 0.86938902133984142 1 0.87560250490583569 1 0.88181652510540864 1 0.88803108783675144 1 0.8942461988702014 1 0.90046186746288448 1 0.90667810196496701 1 0.91289491350069707 1 0.91911231334856824 1 0.92533031475551419 1 0.9315489331411172 1 0.93776818419176455 1 0.9439880868500391 1 0.95020866049980468 1 0.95642992488615264 1 0.96265190400039602 1 0.9688746216534444 1 0.97509810624239812 1 0.98132238514225512 1 0.98754748968945316 1 1 4
+7 0 0 0 1 0 0
+8 0 1
+7 0 0 3 81 79 1 -0.0012600000000000001 0 0.99166806910433225 -0.0024290474198804652 0 0.97916907934632713 -0.0041556686870856405 0 0.9625013807263838 -0.0064065754633828211 0 0.95000132931521664 -0.0080695147108952852 0 0.93750127960794405 -0.009707852209573575 0 0.92500124260478966 -0.011322315812076994 0 0.91250118837727034 -0.012912930886184051 0 0.90000115960536642 -0.014480856552602568 0 0.88750112337475495 -0.016025977431048548 0 0.87500108849312463 -0.01754880638747152 0 0.86250106246199998 -0.019049805988652704 0 0.85000102944828482 -0.020529010547511328 0 0.83750100916791592 -0.021987024618123435 0 0.82500098575434166 -0.023423790211651842 0 0.81250096307329922 -0.024839539588589164 0 0.80000095229575618 -0.02623460040061484 0 0.7875009305699 -0.027608627134194662 0 0.77500092060707626 -0.028962080624074963 0 0.76250090701572382 -0.030294657285077038 0 0.75000090093676053 -0.031606513785123712 0 0.73750089105646288 -0.032897325203243725 0 0.72500088155224662 -0.034167037781514191 0 0.71250087862114031 -0.035415590209551026 0 0.70000087895100138 -0.036642679019159449 0 0.68750087227697532 -0.037847781327729632 0 0.6750008748552182 -0.039030894820797343 0 0.66250087359381438 -0.04019134768145604 0 0.65000087480075985 -0.04132883243621395 0 0.63750087815684497 -0.042442849673491136 0 0.625000877465687 -0.043532704745740072 0 0.61250088448934137 -0.044598076807669175 0 0.60000088431277843 -0.04563794173852994 0 0.58750089092946689 -0.046651919785977214 0 0.57500089296569545 -0.047638951624771643 0 0.5625008930402241 -0.048598256747966771 0 0.5500009010820599 -0.049529215643651064 0 0.53750089643319277 -0.050430285415768737 0 0.52500090129826238 -0.051301060818450712 0 0.51250089779487273 -0.052139902072950384 0 0.50000089749479981 -0.052945976246431804 0 0.48750088860344998 -0.053717653632727083 0 0.47500087974225197 -0.054453887628772561 0 0.4625008713526475 -0.055153292696117018 0 0.45000085526456401 -0.055814057545055237 0 0.43750083301262427 -0.056434614408511809 0 0.42500080860956885 -0.057013445108689248 0 0.41250077772299104 -0.057548589221626589 0 0.40000073803096653 -0.058038007077278866 0 0.38750069220288164 -0.058479817687152012 0 0.37500063518954613 -0.058871583983993149 0 0.3625005686566567 -0.059211135066700046 0 0.3500004886680172 -0.059495790765773687 0 0.33750039371847834 -0.059722842002354941 0 0.32500028290526284 -0.059889604063107046 0 0.31250015190983488 -0.059992723391208563 0 0.29999999813430406 -0.060029097107844702 0 0.2874998173873341 -0.059994687799052197 0 0.27499960455498923 -0.059885944767455929 0 0.26249935422640547 -0.059698303969986058 0 0.24999905865034713 -0.059427166686205624 0 0.23749870917010102 -0.059067284443917699 0 0.22499829176985606 -0.058613039953011835 0 0.21249779146755365 -0.058057937119780946 0 0.19999719409576988 -0.057394559096993807 0 0.18749645845558685 -0.056615244552909234 0 0.17499556762814103 -0.055710017225365414 0 0.16249444360960999 -0.054668793588188679 0 0.14999303957538462 -0.053478201474552511 0 0.13749122200948111 -0.052123698918757067 0 0.12498884671342025 -0.050586635395975146 0 0.11248561848475973 -0.048844854472586741 0 0.099981138068765518 -0.046869505690216813 0 0.087474479216191003 -0.044623807360225462 0 0.074964621118000355 -0.04205569685918592 0 0.062446492760321902 -0.039098544280603768 0 0.049918541725142838 -0.035624947347748813 0 0.037328260760967027 -0.031515506079397577 0 0.019422298161866122 -0.023907069430545717 0 0.0056742009741973755 -0.01660351401419858 0 0 0 0
+ 0 4 0.024905020621093604 1 0.037355229715489859 1 0.049803787515203535 1 0.062250756693110927 1 0.074696191999208086 1 0.087140150227694502 1 0.09958267900039075 1 0.11202382629992173 1 0.12446363161647073 1 0.13690213371776525 1 0.14933937048897158 1 0.16177537330286337 1 0.1742101729986055 1 0.18664379607006579 1 0.19907626507423062 1 0.21150760225959656 1 0.22393782432649634 1 0.23636694978918213 1 0.24879499018832782 1 0.26122195732031672 1 0.27364786151859527 1 0.28607271190080846 1 0.29849651320870207 1 0.31091926917362644 1 0.32334098125439875 1 0.33576165190351764 1 0.34818128001473453 1 0.36059986435023433 1 0.37301740357171842 1 0.38543389372362652 1 0.39784933266951494 1 0.41026371484180674 1 0.42267703707553572 1 0.43508929601632645 1 0.44750048755453947 1 0.45991061060022531 1 0.47231966164774714 1 0.48472764237851379 1 0.49713455205053769 1 0.5095403958993332 1 0.52194518122552613 1 0.53434891844675636 1 0.54675162067595628 1 0.55915330642894168 1 0.57155400072841434 1 0.58395373433544895 1 0.5963525448276461 1 0.60875047830170248 1 0.62114759129271047 1 0.63354395079841141 1 0.64593963741319882 1 0.6583347467197086 1 0.67072939181111524 1 0.68312370595321636 1 0.69551784561604479 1 0.70791199495225621 1 0.72030637046871193 1 0.73270122684714922 1 0.74509686419033272 1 0.75749363652144297 1 0.76989196402029769 1 0.78229234787255519 1 0.79469538715463528 1 0.80710180706076873 1 0.81951248666902865 1 0.83192851406250057 1 0.84435124088802904 1 0.85678238569061316 1 0.86922416081563947 1 0.88167949619479558 1 0.89415237774934153 1 0.90664844127968758 1 0.91917601939989868 1 0.931748273293869 1 0.94438793081305228 1 0.95714020787863818 1 0.97012207626326408 1 1 4
+Triangulations 0
+
+TShapes 37
+Ve
+2e-07
+5.15079620502503e-18 -2.12442355226077e-34 0.5
+0 0
+
+0101101
+*
+Ed
+ 2e-07 1 1 0
+1 1 0 0 6.28318530717959
+2 1 2 0 0 6.28318530717959
+2 2 1 0 0 6.28318530717959
+4 C2 2 0 1 0
+0
+
+0101000
++37 0 -37 0 *
+Ve
+2e-07
+2 1.54305496692567e-19 0.50126
+0 0
+
+0101101
+*
+Ed
+ 1e-07 1 1 0
+1 2 0 0.5 1
+3 3 4C0 1 0 0.5 1
+0
+
+0101000
++37 0 -35 0 *
+Ed
+ 2e-07 1 1 0
+1 3 0 0 6.28318530717959
+2 5 1 0 0 6.28318530717959
+2 6 3 0 0 6.28318530717959
+4 C0 1 0 3 0
+0
+
+0101000
++35 0 -35 0 *
+Wi
+
+0101100
++36 0 +34 0 -33 0 -34 0 *
+Fa
+0 0 1 0
+
+0111000
++32 0 *
+Ve
+5.000001e-06
+0.00399980079999999 -8.77137581722074e-19 0.492837628103607
+0 0
+
+0101101
+*
+Ed
+ 5e-06 1 1 0
+1 4 0 0 1
+2 7 2 0 0 1
+2 8 4 1 0 1
+0
+
+0101000
++30 0 -30 0 *
+Ed
+ 1e-07 1 1 0
+1 5 0 0.49722879746868 0.5
+3 9 10C2 2 0 0.49722879746868 0.5
+0
+
+0101000
++30 0 -37 0 *
+Wi
+
+0101100
+-29 0 -28 0 -36 0 +28 0 *
+Fa
+0 1e-07 2 0
+
+0111000
++27 0 *
+Ve
+2e-07
+2 -1.54305496692567e-19 0.49874
+0 0
+
+0101101
+*
+Ed
+ 2e-07 1 1 0
+1 6 0 0 0.00252
+3 11 12CN 3 0 0 0.00252
+0
+
+0101000
++35 0 -25 0 *
+Ed
+ 2e-07 1 1 0
+1 7 0 0 6.28318530717959
+2 13 3 0 0 6.28318530717959
+2 14 5 0 0 6.28318530717959
+4 C0 3 0 5 0
+0
+
+0101000
++25 0 -25 0 *
+Wi
+
+0101100
++33 0 +24 0 -23 0 -24 0 *
+Fa
+0 2e-07 3 0
+
+0111000
++22 0 *
+Wi
+
+0101100
+-29 0 *
+Ve
+5.000001e-06
+0.00399980079999997 -0.00894062753299536 0
+0 0
+
+0101101
+*
+Ed
+ 5e-06 1 1 0
+1 8 0 0 1
+2 15 4 1 0 1
+2 16 6 0 0 1
+0
+
+0101000
++19 0 -19 0 *
+Wi
+
+0101100
++18 0 *
+Fa
+0 1e-07 4 1
+
+0101000
++20 0 +17 0 *
+Ve
+5.000001e-06
+1.9960001992 -1.88627210317514e-19 0.49845974193989
+0 0
+
+0101101
+*
+Ed
+ 1e-07 1 1 0
+1 9 0 0 0.000996307026022182
+3 17 18C2 5 0 0 0.000996307026022182
+0
+
+0101000
++25 0 -15 0 *
+Ed
+ 5e-06 1 1 0
+1 10 0 0 1
+2 19 5 0 0 1
+2 20 4 3 0 1
+0
+
+0101000
++15 0 -15 0 *
+Wi
+
+0101100
+-14 0 +13 0 +14 0 +23 0 *
+Fa
+0 1e-07 5 0
+
+0111000
++12 0 *
+Ve
+1e-07
+8.67361737988404e-19 3.46944695195361e-18 0
+0 0
+
+0101101
+*
+Ed
+ 1e-07 1 1 0
+1 11 0 0.993804099075429 1
+3 21 22C2 6 0 0.993804099075429 1
+0
+
+0101000
++19 0 -10 0 *
+Ed
+ 1e-07 1 1 1
+2 23 6 0 0 6.28318530717959
+0
+
+0101100
++10 0 -10 0 *
+Wi
+
+0101100
+-18 0 -9 0 -8 0 +9 0 *
+Fa
+0 1e-07 6 0
+
+0101000
++7 0 *
+Wi
+
+0101100
+-13 0 *
+Fa
+0 1e-07 4 3
+
+0111000
++5 0 *
+Sh
+
+0101100
++31 0 +26 0 +21 0 -16 0 +11 0 -6 0 +4 0 *
+So
+
+0100000
++3 0 *
+Co
+
+1100000
++2 0 *
+
++1 0
\ No newline at end of file
diff --git a/ceasiompy/CPACS2GMSH/tests/ToolInput/test_close_engine/SimpleNacelle_centerCowl.brep b/ceasiompy/CPACS2GMSH/tests/ToolInput/test_close_engine/SimpleNacelle_centerCowl.brep
new file mode 100644
index 000000000..1a8ff7eed
--- /dev/null
+++ b/ceasiompy/CPACS2GMSH/tests/ToolInput/test_close_engine/SimpleNacelle_centerCowl.brep
@@ -0,0 +1,89 @@
+DBRep_DrawableShape
+
+CASCADE Topology V1, (c) Matra-Datavision
+Locations 0
+Curve2ds 4
+1 0 0 1 0
+1 0 1 1 0
+1 6.2831853071795862 0 0 1
+1 0 0 0 1
+Curves 2
+2 1 0 0 1 0 0 0 -1 0 0 0 -1 0.0012600000000000001
+7 0 0 3 81 79 1 -0.0012600000000000001 0 0.99166806910433225 -0.0024290474198804652 0 0.97916907934632713 -0.0041556686870856405 0 0.9625013807263838 -0.0064065754633828211 0 0.95000132931521664 -0.0080695147108952852 0 0.93750127960794405 -0.009707852209573575 0 0.92500124260478966 -0.011322315812076994 0 0.91250118837727034 -0.012912930886184051 0 0.90000115960536642 -0.014480856552602568 0 0.88750112337475495 -0.016025977431048548 0 0.87500108849312463 -0.01754880638747152 0 0.86250106246199998 -0.019049805988652704 0 0.85000102944828482 -0.020529010547511328 0 0.83750100916791592 -0.021987024618123435 0 0.82500098575434166 -0.023423790211651842 0 0.81250096307329922 -0.024839539588589164 0 0.80000095229575618 -0.02623460040061484 0 0.7875009305699 -0.027608627134194662 0 0.77500092060707626 -0.028962080624074963 0 0.76250090701572382 -0.030294657285077038 0 0.75000090093676053 -0.031606513785123712 0 0.73750089105646288 -0.032897325203243725 0 0.72500088155224662 -0.034167037781514191 0 0.71250087862114031 -0.035415590209551026 0 0.70000087895100138 -0.036642679019159449 0 0.68750087227697532 -0.037847781327729632 0 0.6750008748552182 -0.039030894820797343 0 0.66250087359381438 -0.04019134768145604 0 0.65000087480075985 -0.04132883243621395 0 0.63750087815684497 -0.042442849673491136 0 0.625000877465687 -0.043532704745740072 0 0.61250088448934137 -0.044598076807669175 0 0.60000088431277843 -0.04563794173852994 0 0.58750089092946689 -0.046651919785977214 0 0.57500089296569545 -0.047638951624771643 0 0.5625008930402241 -0.048598256747966771 0 0.5500009010820599 -0.049529215643651064 0 0.53750089643319277 -0.050430285415768737 0 0.52500090129826238 -0.051301060818450712 0 0.51250089779487273 -0.052139902072950384 0 0.50000089749479981 -0.052945976246431804 0 0.48750088860344998 -0.053717653632727083 0 0.47500087974225197 -0.054453887628772561 0 0.4625008713526475 -0.055153292696117018 0 0.45000085526456401 -0.055814057545055237 0 0.43750083301262427 -0.056434614408511809 0 0.42500080860956885 -0.057013445108689248 0 0.41250077772299104 -0.057548589221626589 0 0.40000073803096653 -0.058038007077278866 0 0.38750069220288164 -0.058479817687152012 0 0.37500063518954613 -0.058871583983993149 0 0.3625005686566567 -0.059211135066700046 0 0.3500004886680172 -0.059495790765773687 0 0.33750039371847834 -0.059722842002354941 0 0.32500028290526284 -0.059889604063107046 0 0.31250015190983488 -0.059992723391208563 0 0.29999999813430406 -0.060029097107844702 0 0.2874998173873341 -0.059994687799052197 0 0.27499960455498923 -0.059885944767455929 0 0.26249935422640547 -0.059698303969986058 0 0.24999905865034713 -0.059427166686205624 0 0.23749870917010102 -0.059067284443917699 0 0.22499829176985606 -0.058613039953011835 0 0.21249779146755365 -0.058057937119780946 0 0.19999719409576988 -0.057394559096993807 0 0.18749645845558685 -0.056615244552909234 0 0.17499556762814103 -0.055710017225365414 0 0.16249444360960999 -0.054668793588188679 0 0.14999303957538462 -0.053478201474552511 0 0.13749122200948111 -0.052123698918757067 0 0.12498884671342025 -0.050586635395975146 0 0.11248561848475973 -0.048844854472586741 0 0.099981138068765518 -0.046869505690216813 0 0.087474479216191003 -0.044623807360225462 0 0.074964621118000355 -0.04205569685918592 0 0.062446492760321902 -0.039098544280603768 0 0.049918541725142838 -0.035624947347748813 0 0.037328260760967027 -0.031515506079397577 0 0.019422298161866122 -0.023907069430545717 0 0.0056742009741973755 -0.01660351401419858 0 0 0 0
+ 0 4 0.024905020621093604 1 0.037355229715489859 1 0.049803787515203535 1 0.062250756693110927 1 0.074696191999208086 1 0.087140150227694502 1 0.09958267900039075 1 0.11202382629992173 1 0.12446363161647073 1 0.13690213371776525 1 0.14933937048897158 1 0.16177537330286337 1 0.1742101729986055 1 0.18664379607006579 1 0.19907626507423062 1 0.21150760225959656 1 0.22393782432649634 1 0.23636694978918213 1 0.24879499018832782 1 0.26122195732031672 1 0.27364786151859527 1 0.28607271190080846 1 0.29849651320870207 1 0.31091926917362644 1 0.32334098125439875 1 0.33576165190351764 1 0.34818128001473453 1 0.36059986435023433 1 0.37301740357171842 1 0.38543389372362652 1 0.39784933266951494 1 0.41026371484180674 1 0.42267703707553572 1 0.43508929601632645 1 0.44750048755453947 1 0.45991061060022531 1 0.47231966164774714 1 0.48472764237851379 1 0.49713455205053769 1 0.5095403958993332 1 0.52194518122552613 1 0.53434891844675636 1 0.54675162067595628 1 0.55915330642894168 1 0.57155400072841434 1 0.58395373433544895 1 0.5963525448276461 1 0.60875047830170248 1 0.62114759129271047 1 0.63354395079841141 1 0.64593963741319882 1 0.6583347467197086 1 0.67072939181111524 1 0.68312370595321636 1 0.69551784561604479 1 0.70791199495225621 1 0.72030637046871193 1 0.73270122684714922 1 0.74509686419033272 1 0.75749363652144297 1 0.76989196402029769 1 0.78229234787255519 1 0.79469538715463528 1 0.80710180706076873 1 0.81951248666902865 1 0.83192851406250057 1 0.84435124088802904 1 0.85678238569061316 1 0.86922416081563947 1 0.88167949619479558 1 0.89415237774934153 1 0.90664844127968758 1 0.91917601939989868 1 0.931748273293869 1 0.94438793081305228 1 0.95714020787863818 1 0.97012207626326408 1 1 4
+Polygon3D 0
+PolygonOnTriangulations 0
+Surfaces 2
+7 0 0 0 1 0 0
+8 0 1
+7 0 0 3 81 79 1 -0.0012600000000000001 0 0.99166806910433225 -0.0024290474198804652 0 0.97916907934632713 -0.0041556686870856405 0 0.9625013807263838 -0.0064065754633828211 0 0.95000132931521664 -0.0080695147108952852 0 0.93750127960794405 -0.009707852209573575 0 0.92500124260478966 -0.011322315812076994 0 0.91250118837727034 -0.012912930886184051 0 0.90000115960536642 -0.014480856552602568 0 0.88750112337475495 -0.016025977431048548 0 0.87500108849312463 -0.01754880638747152 0 0.86250106246199998 -0.019049805988652704 0 0.85000102944828482 -0.020529010547511328 0 0.83750100916791592 -0.021987024618123435 0 0.82500098575434166 -0.023423790211651842 0 0.81250096307329922 -0.024839539588589164 0 0.80000095229575618 -0.02623460040061484 0 0.7875009305699 -0.027608627134194662 0 0.77500092060707626 -0.028962080624074963 0 0.76250090701572382 -0.030294657285077038 0 0.75000090093676053 -0.031606513785123712 0 0.73750089105646288 -0.032897325203243725 0 0.72500088155224662 -0.034167037781514191 0 0.71250087862114031 -0.035415590209551026 0 0.70000087895100138 -0.036642679019159449 0 0.68750087227697532 -0.037847781327729632 0 0.6750008748552182 -0.039030894820797343 0 0.66250087359381438 -0.04019134768145604 0 0.65000087480075985 -0.04132883243621395 0 0.63750087815684497 -0.042442849673491136 0 0.625000877465687 -0.043532704745740072 0 0.61250088448934137 -0.044598076807669175 0 0.60000088431277843 -0.04563794173852994 0 0.58750089092946689 -0.046651919785977214 0 0.57500089296569545 -0.047638951624771643 0 0.5625008930402241 -0.048598256747966771 0 0.5500009010820599 -0.049529215643651064 0 0.53750089643319277 -0.050430285415768737 0 0.52500090129826238 -0.051301060818450712 0 0.51250089779487273 -0.052139902072950384 0 0.50000089749479981 -0.052945976246431804 0 0.48750088860344998 -0.053717653632727083 0 0.47500087974225197 -0.054453887628772561 0 0.4625008713526475 -0.055153292696117018 0 0.45000085526456401 -0.055814057545055237 0 0.43750083301262427 -0.056434614408511809 0 0.42500080860956885 -0.057013445108689248 0 0.41250077772299104 -0.057548589221626589 0 0.40000073803096653 -0.058038007077278866 0 0.38750069220288164 -0.058479817687152012 0 0.37500063518954613 -0.058871583983993149 0 0.3625005686566567 -0.059211135066700046 0 0.3500004886680172 -0.059495790765773687 0 0.33750039371847834 -0.059722842002354941 0 0.32500028290526284 -0.059889604063107046 0 0.31250015190983488 -0.059992723391208563 0 0.29999999813430406 -0.060029097107844702 0 0.2874998173873341 -0.059994687799052197 0 0.27499960455498923 -0.059885944767455929 0 0.26249935422640547 -0.059698303969986058 0 0.24999905865034713 -0.059427166686205624 0 0.23749870917010102 -0.059067284443917699 0 0.22499829176985606 -0.058613039953011835 0 0.21249779146755365 -0.058057937119780946 0 0.19999719409576988 -0.057394559096993807 0 0.18749645845558685 -0.056615244552909234 0 0.17499556762814103 -0.055710017225365414 0 0.16249444360960999 -0.054668793588188679 0 0.14999303957538462 -0.053478201474552511 0 0.13749122200948111 -0.052123698918757067 0 0.12498884671342025 -0.050586635395975146 0 0.11248561848475973 -0.048844854472586741 0 0.099981138068765518 -0.046869505690216813 0 0.087474479216191003 -0.044623807360225462 0 0.074964621118000355 -0.04205569685918592 0 0.062446492760321902 -0.039098544280603768 0 0.049918541725142838 -0.035624947347748813 0 0.037328260760967027 -0.031515506079397577 0 0.019422298161866122 -0.023907069430545717 0 0.0056742009741973755 -0.01660351401419858 0 0 0 0
+ 0 4 0.024905020621093604 1 0.037355229715489859 1 0.049803787515203535 1 0.062250756693110927 1 0.074696191999208086 1 0.087140150227694502 1 0.09958267900039075 1 0.11202382629992173 1 0.12446363161647073 1 0.13690213371776525 1 0.14933937048897158 1 0.16177537330286337 1 0.1742101729986055 1 0.18664379607006579 1 0.19907626507423062 1 0.21150760225959656 1 0.22393782432649634 1 0.23636694978918213 1 0.24879499018832782 1 0.26122195732031672 1 0.27364786151859527 1 0.28607271190080846 1 0.29849651320870207 1 0.31091926917362644 1 0.32334098125439875 1 0.33576165190351764 1 0.34818128001473453 1 0.36059986435023433 1 0.37301740357171842 1 0.38543389372362652 1 0.39784933266951494 1 0.41026371484180674 1 0.42267703707553572 1 0.43508929601632645 1 0.44750048755453947 1 0.45991061060022531 1 0.47231966164774714 1 0.48472764237851379 1 0.49713455205053769 1 0.5095403958993332 1 0.52194518122552613 1 0.53434891844675636 1 0.54675162067595628 1 0.55915330642894168 1 0.57155400072841434 1 0.58395373433544895 1 0.5963525448276461 1 0.60875047830170248 1 0.62114759129271047 1 0.63354395079841141 1 0.64593963741319882 1 0.6583347467197086 1 0.67072939181111524 1 0.68312370595321636 1 0.69551784561604479 1 0.70791199495225621 1 0.72030637046871193 1 0.73270122684714922 1 0.74509686419033272 1 0.75749363652144297 1 0.76989196402029769 1 0.78229234787255519 1 0.79469538715463528 1 0.80710180706076873 1 0.81951248666902865 1 0.83192851406250057 1 0.84435124088802904 1 0.85678238569061316 1 0.86922416081563947 1 0.88167949619479558 1 0.89415237774934153 1 0.90664844127968758 1 0.91917601939989868 1 0.931748273293869 1 0.94438793081305228 1 0.95714020787863818 1 0.97012207626326408 1 1 4
+1 1 0 0 1 0 0 0 -1 0 0 0 -1
+Triangulations 0
+
+TShapes 11
+Ve
+1e-07
+1 -0.00126 0
+0 0
+
+0101101
+*
+Ed
+ 1e-07 1 1 0
+1 1 0 0 6.28318530717959
+2 1 1 0 0 6.28318530717959
+0
+
+0101100
++11 0 -11 0 *
+Ve
+1e-07
+8.67361737988404e-19 3.46944695195361e-18 0
+0 0
+
+0101101
+*
+Ed
+ 1e-07 1 1 1
+2 2 1 0 0 6.28318530717959
+0
+
+0101100
++9 0 -9 0 *
+Ed
+ 1e-07 1 1 0
+1 2 0 0 1
+3 3 4C2 1 0 0 1
+0
+
+0101000
++11 0 -9 0 *
+Wi
+
+0101000
++10 0 -8 0 -7 0 +7 0 *
+Fa
+0 1e-07 1 0
+
+0111000
++6 0 *
+Wi
+
+0101100
++10 0 *
+Fa
+0 1e-07 2 0
+
+0111000
++4 0 *
+Sh
+
+0101100
++5 0 -3 0 *
+So
+
+1100000
+-2 0 *
+
++1 0
\ No newline at end of file
diff --git a/ceasiompy/CPACS2GMSH/tests/ToolInput/test_close_engine/SimpleNacelle_fanCowl.brep b/ceasiompy/CPACS2GMSH/tests/ToolInput/test_close_engine/SimpleNacelle_fanCowl.brep
new file mode 100644
index 000000000..b318eef23
--- /dev/null
+++ b/ceasiompy/CPACS2GMSH/tests/ToolInput/test_close_engine/SimpleNacelle_fanCowl.brep
@@ -0,0 +1,259 @@
+DBRep_DrawableShape
+
+CASCADE Topology V1, (c) Matra-Datavision
+Locations 3
+1
+1.11022302462516e-16 0 1 0.0039998008
+ 0 1 0 0
+ -1 0 1.11022302462516e-16 0.0039998008
+1
+ 1 0 0 1.9920003984
+ 0 1 0 0
+ 0 0 1 0
+2 1 1 2 1 0
+Curve2ds 18
+1 0 0.5 1 0
+1 0 0.5 1 0
+1 6.2831853071795862 0 0 1
+1 0 0 0 1
+1 0 1 1 0
+1 0 0 1 0
+7 0 0 5 118 30 6.2831853071795862 0.49722879746868048 6.2467428323965901 0.49722879746868048 6.2103003576156155 0.49722879746868048 6.1738578828353443 0.49722879746868048 6.1374154080544105 0.49722879746868048 6.0645304584883677 0.49722879746868048 6.0280879837074322 0.49722879746868048 5.9916455089271636 0.49722879746868048 5.9552030341461926 0.49722879746868048 5.8848313587036101 0.49722879746868048 5.8509021580452272 0.49722879746868048 5.8169729573872901 0.49722879746868048 5.7830437567289685 0.49722879746868048 5.7126720812863248 0.49722879746868048 5.6762296065054114 0.49722879746868048 5.6397871317251385 0.49722879746868048 5.6033446569441399 0.49722879746868048 5.5304597073781183 0.49722879746868048 5.4940172325972245 0.49722879746868048 5.4575747578169542 0.49722879746868048 5.4211322830359361 0.49722879746868048 5.3495039705317007 0.49722879746868048 5.3143181328120122 0.49722879746868048 5.2791322950928752 0.49722879746868048 5.243946457373224 0.49722879746868048 5.1723181448689308 0.49722879746868048 5.13587567008799 0.49722879746868048 5.0994331953077161 0.49722879746868048 5.0629907205267433 0.49722879746868048 4.9901057709607759 0.49722879746868048 4.9536632961797524 0.49722879746868048 4.9172208213994812 0.49722879746868048 4.8807783466185946 0.49722879746868048 4.8078933970525579 0.49722879746868048 4.7714509222715744 0.49722879746868048 4.7350084474913032 0.49722879746868048 4.6985659727103783 0.49722879746868048 4.6256810231443666 0.49722879746868048 4.5892385483633742 0.49722879746868048 4.5527960735831039 0.49722879746868048 4.516353598802187 0.49722879746868048 4.4434686492361273 0.49722879746868048 4.4070261744552166 0.49722879746868048 4.3705836996749454 0.49722879746868048 4.3341412248939433 0.49722879746868048 4.2612562753279573 0.49722879746868048 4.2248138005469924 0.49722879746868048 4.1883713257667221 0.49722879746868048 4.1519288509857777 0.49722879746868048 4.0790439014197739 0.49722879746868048 4.0426014266387789 0.49722879746868048 4.0061589518585095 0.49722879746868048 3.9697164770775948 0.49722879746868048 3.8968315275115608 0.49722879746868048 3.8603890527305835 0.49722879746868048 3.8239465779503128 0.49722879746868048 3.7875041031693768 0.49722879746868048 3.7146191536033641 0.49722879746868048 3.6781766788223664 0.49722879746868048 3.6417342040420935 0.49722879746868048 3.6052917292611761 0.49722879746868048 3.5324067796951191 0.49722879746868048 3.4959643049141897 0.49722879746868048 3.4595218301339146 0.49722879746868048 3.4230793553529275 0.49722879746868048 3.3501944057869304 0.49722879746868048 3.313751931005958 0.49722879746868048 3.2773094562256859 0.49722879746868048 3.2408669814447442 0.49722879746868048 3.1679820318787177 0.49722879746868048 3.1315395570977542 0.49722879746868048 3.0950970823174808 0.49722879746868048 3.0586546075365275 0.49722879746868048 2.9895395691555402 0.49722879746868048 2.95686700555853 0.49722879746868048 2.9241944419618333 0.49722879746868048 2.8915218783648138 0.49722879746868048 2.8224068399838345 0.49722879746868048 2.7859643652028745 0.49722879746868048 2.749521890422602 0.49722879746868048 2.7130794156416478 0.49722879746868048 2.640194466075624 0.49722879746868048 2.6037519912946672 0.49722879746868048 2.5673095165143929 0.49722879746868048 2.5308670417334338 0.49722879746868048 2.4579820921674176 0.49722879746868048 2.4215396173864496 0.49722879746868048 2.3850971426061776 0.49722879746868048 2.3486546678252336 0.49722879746868048 2.2757697182592032 0.49722879746868048 2.239327243478249 0.49722879746868048 2.2028847686979751 0.49722879746868048 2.1664422939170143 0.49722879746868048 2.1036104408437106 0.49722879746868048 2.0772210625536225 0.49722879746868048 2.0508316842636125 0.49722879746868048 2.024442305973535 0.49722879746868048 1.961610452900213 0.49722879746868048 1.9251679781192741 0.49722879746868048 1.8887255033389998 0.49722879746868048 1.8522830285580227 0.49722879746868048 1.7831679901770534 0.49722879746868048 1.7504954265800365 0.49722879746868048 1.7178228629833392 0.49722879746868048 1.6851502993863257 0.49722879746868048 1.6185485351287514 0.49722879746868048 1.5846193344703974 0.49722879746868048 1.5506901338124588 0.49722879746868048 1.5167609331541041 0.49722879746868048 1.3697359342160698 0.49722879746868048 1.2566332069944544 0.49722879746868048 1.1435435803483773 0.49722879746868048 1.0304408531267781 0.49722879746868048 0.73389273899499829 0.49722879746868048 0.55036496471515794 0.49722879746868048 0.36698009013317567 0.49722879746868048 0.18345231585333915 0.49722879746868048 0 0.49722879746868048
+ 0 6 0.029000420763338419 4 0.058000841526677539 4 0.085001233271496168 4 0.11400165403483276 4 0.14300207479821528 4 0.17100248105233079 4 0.20000290181572761 4 0.22900332257910461 4 0.25800374334244064 4 0.28700416410574697 4 0.31600458486905331 4 0.34500500563237591 4 0.37400542639567941 4 0.40300584715895815 4 0.43200626792220864 4 0.46100668868545203 4 0.49000710944870285 4 0.51900753021194346 4 0.54500790744748351 4 0.57400832821072523 4 0.60300874897396439 4 0.63200916973720223 4 0.66100959050043506 4 0.68200989519037636 4 0.7110103159536113 4 0.73701069318915302 4 0.76401108493388792 4 0.85401116742439875 4 1 6
+7 0 0 5 118 30 -0.49283762810360682 5.5511151231257827e-17 -0.49283762913646589 0.017960209128777705 -0.49201948200409812 0.035920479942518888 -0.49038319833905381 0.05383094219538008 -0.4879332966256088 0.071642137383439247 -0.48142430980543927 0.10696790048027367 -0.47736520234338303 0.12448258971656827 -0.47251052802953791 0.14180004390736131 -0.46687369271157042 0.15887244209951631 -0.45451269175820785 0.19127663869964517 -0.44789029105706507 0.20664736888522994 -0.44061960271525569 0.22172779165774581 -0.43271803490035926 0.23648180004706135 -0.4150635696046569 0.26633387128159541 -0.40521683430128086 0.2813765604127868 -0.3946912491207365 0.29595991184347048 -0.38351587973919687 0.31004365465635941 -0.35993104477059201 0.33713667972374534 -0.34752149818061057 0.35014605503016366 -0.33452757457279575 0.36258068536716698 -0.32098515576932779 0.37440623336229645 -0.29336589750151804 0.39639132844111086 -0.27932350393004113 0.40659433110434745 -0.26484118847378019 0.41617269831374459 -0.24995623742106932 0.4251017695264066 -0.21891540237858279 0.44191350330830687 -0.2027330103959594 0.44974746030041945 -0.18620478802063373 0.45683829575581436 -0.16937637679661796 0.46316642882234821 -0.13521402894472634 0.47426644850899369 -0.11787997498507026 0.4790383732525022 -0.10034047368951193 0.48301681222360121 -0.082643959178922505 0.48619077923797638 -0.047035774448977295 0.49091662604437197 -0.029123981932133468 0.49246852206744912 -0.011153927413206432 0.49320284007104132 0.0068247660439580415 0.49311755228672272 0.042699815254525986 0.49131275536264363 0.060596294222031846 0.48959324002424404 0.078401920826762686 0.48705912409229624 0.096067526061194869 0.4837174053528418 0.1310216315814911 0.47544172078450242 0.14831025191840874 0.47050772653249562 0.16536191361220146 0.46478908020454862 0.18212952967734802 0.45830157344734568 0.21500537540443093 0.44382900575785039 0.23111371797959496 0.43584389511903254 0.24684684123478892 0.42713006043041357 0.26216129923728049 0.41771156434437257 0.29187037939793326 0.39752129431630973 0.30626510359294123 0.38674945103009911 0.32015877114898911 0.37532893947475832 0.33351301566315572 0.36329129660380521 0.35907167479369317 0.33805181579996957 0.37127617719216321 0.32484989118122554 0.38287037541256147 0.31110083121445553 0.3938222528776455 0.29684260297871629 0.41438425425307746 0.26738958026057691 0.42399444878431325 0.25219468462076111 0.4329052985194955 0.23657230198107584 0.44109219676280792 0.22056557247169536 0.45597674094780893 0.18787418534598088 0.46267443801098418 0.17118941544997232 0.46860690467958338 0.15421096082154104 0.47375775885682658 0.13698570628995577 0.48247202469597089 0.10213835318124879 0.4860354662873026 0.084516134919688429 0.48879312835017741 0.066743757974251791 0.49073739579135961 0.048870299535826146 0.49299285751545857 0.013020762018454757 0.49330405954486051 -0.0049554401869570056 0.49279561194172505 -0.022933303335245736 0.49146891874897536 -0.040863182799573149 0.48741217482147486 -0.074683216604735286 0.48484148786412834 -0.090592584910555274 0.48162213020155675 -0.10638800917017469 0.47776125039840911 -0.12203441573744353 0.46825812471422895 -0.15474439067028098 0.46246170736997672 -0.17176326955239513 0.45589353482485973 -0.1885060647718706 0.44857174463350968 -0.2049265422566135 0.43246563197189458 -0.2370337000680621 0.42368125418461633 -0.25272049066791202 0.41418792539654065 -0.26799592162305214 0.40401186077914064 -0.28281781088641078 0.38235437946183126 -0.3114749208381985 0.37087288837856558 -0.32531023995045272 0.35876872357152967 -0.33861254338511493 0.34607530985545643 -0.35134509775042166 0.31958353020950864 -0.37560333468118934 0.30578507329272442 -0.38712910056239125 0.29147083640089366 -0.3980178426636754 0.27668034731009872 -0.40823949247612629 0.2504312493562853 -0.42466724005081524 0.2391790170834088 -0.43120323635765878 0.22771520283520838 -0.43736554466032718 0.21605642180480866 -0.44314523355603158 0.18787418534597616 -0.45597674094781188 0.17118941544996602 -0.46267443801098584 0.15421096082153443 -0.46860690467958499 0.13698570628995035 -0.47375775885682908 0.10394079814626411 -0.48202128737987004 0.088159991513890298 -0.48528970800064297 0.072254452156979426 -0.4879114457958259 0.056259498231603623 -0.48988067921215056 0.023545097293108684 -0.49255879636342137 0.0068213647929623034 -0.49321629826348445 -0.0099201791530915233 -0.4931637029789841 -0.026639450361980212 -0.4924011364385098 -0.098818732105655449 -0.48603550858492989 -0.15365230498503707 -0.4732892645229273 -0.20630717762490969 -0.45282348349031049 -0.25535561369046478 -0.42519309493117552 -0.37139739225798463 -0.33634440168453522 -0.43060943183772055 -0.26489311940889637 -0.47210311356572016 -0.18086320150689755 -0.49283795281486092 -0.090412011291749084 -0.49283762810360682 -1.1102230246251565e-16
+ 0 6 0.029000420763338419 4 0.058000841526677539 4 0.085001233271496168 4 0.11400165403483276 4 0.14300207479821528 4 0.17100248105233079 4 0.20000290181572761 4 0.22900332257910461 4 0.25800374334244064 4 0.28700416410574697 4 0.31600458486905331 4 0.34500500563237591 4 0.37400542639567941 4 0.40300584715895815 4 0.43200626792220864 4 0.46100668868545203 4 0.49000710944870285 4 0.51900753021194346 4 0.54500790744748351 4 0.57400832821072523 4 0.60300874897396439 4 0.63200916973720223 4 0.66100959050043506 4 0.68200989519037636 4 0.7110103159536113 4 0.73701069318915302 4 0.76401108493388792 4 0.85401116742439875 4 1 6
+1 6.2831853071795862 0 0 1
+1 0 0 0 1
+1 6.2831853071795862 0 0 1
+1 0 0 0 1
+1 0 0.0025200000000000001 1 0
+1 0 0 1 0
+1 6.2831853071795862 0 0 1
+1 0 0 0 1
+7 0 0 7 176 30 6.2831853071795862 0.00099630702602213268 6.2652333491589971 0.00099630702602213268 6.2472813911384888 0.00099630702602213268 6.2293294331180258 0.00099630702602213268 6.2113774750975717 0.00099630702602213268 6.1934255170770971 0.00099630702602213268 6.1754735590565817 0.00099630702602213268 6.1314912619052482 0.00099630702602213268 6.1054609227755163 0.00099630702602213268 6.079430583646384 0.00099630702602213268 6.0534002445174417 0.00099630702602213268 6.0273699053882899 0.00099630702602213268 6.0013395662585385 0.00099630702602213268 5.9492788879970533 0.00099630702602213268 5.9232485488673223 0.00099630702602213268 5.8972182097381909 0.00099630702602213268 5.871187870609246 0.00099630702602213268 5.8451575314800897 0.00099630702602213268 5.8191271923503356 0.00099630702602213268 5.7670665140888735 0.00099630702602213268 5.7410361749591301 0.00099630702602213268 5.7150058358299836 0.00099630702602213268 5.6889754967010351 0.00099630702602213268 5.6629451575718903 0.00099630702602213268 5.636914818442146 0.00099630702602213268 5.5848541401806937 0.00099630702602213268 5.5588238010509325 0.00099630702602213268 5.5327934619217691 0.00099630702602213268 5.5067631227928233 0.00099630702602213268 5.4807327836636963 0.00099630702602213268 5.4547024445339725 0.00099630702602213268 5.403539364173727 0.00099630702602213268 5.3784066229450023 0.00099630702602213268 5.3532738817167465 0.00099630702602213268 5.3281411404886541 0.00099630702602213268 5.3030083992604133 0.00099630702602213268 5.2778756580317037 0.00099630702602213268 5.2267125776714494 0.00099630702602213268 5.2006822385417157 0.00099630702602213268 5.174651899412579 0.00099630702602213268 5.148621560283634 0.00099630702602213268 5.1225912211544804 0.00099630702602213268 5.0965608820247308 0.00099630702602213268 5.0445002037632625 0.00099630702602213268 5.0184698646335217 0.00099630702602213268 4.9924395255043779 0.00099630702602213268 4.966409186375432 0.00099630702602213268 4.9403788472462855 0.00099630702602213268 4.9143485081165412 0.00099630702602213268 4.8622878298550498 0.00099630702602213268 4.8362574907253224 0.00099630702602213268 4.8102271515961927 0.00099630702602213268 4.7841968124672478 0.00099630702602213268 4.7581664733380915 0.00099630702602213268 4.7321361342083348 0.00099630702602213268 4.6800754559468629 0.00099630702602213268 4.6540451168171293 0.00099630702602213268 4.6280147776879916 0.00099630702602213268 4.601984438559044 0.00099630702602213268 4.5759540994298904 0.00099630702602213268 4.5499237603001399 0.00099630702602213268 4.4978630820386689 0.00099630702602213268 4.4718327429089291 0.00099630702602213268 4.445802403779787 0.00099630702602213268 4.4197720646508412 0.00099630702602213268 4.3937417255216955 0.00099630702602213268 4.3677113863919521 0.00099630702602213268 4.3156507081304873 0.00099630702602213268 4.289620369000736 0.00099630702602213268 4.2635900298715814 0.00099630702602213268 4.2375596907426365 0.00099630702602213268 4.2115293516135006 0.00099630702602213268 4.1854990124837661 0.00099630702602213268 4.1334383342222907 0.00099630702602213268 4.1074079950925393 0.00099630702602213268 4.0813776559633848 0.00099630702602213268 4.0553473168344381 0.00099630702602213268 4.0293169777053013 0.00099630702602213268 4.0032866385755668 0.00099630702602213268 3.9512259603140794 0.00099630702602213268 3.9251956211843386 0.00099630702602213268 3.8991652820551912 0.00099630702602213268 3.8731349429262418 0.00099630702602213268 3.8471046037970913 0.00099630702602213268 3.8210742646673452 0.00099630702602213268 3.7690135864058747 0.00099630702602213268 3.7429832472761277 0.00099630702602213268 3.7169529081469768 0.00099630702602213268 3.6909225690180292 0.00099630702602213268 3.6648922298888871 0.00099630702602213268 3.638861890759145 0.00099630702602213268 3.586801212497666 0.00099630702602213268 3.5607708733679155 0.00099630702602213268 3.5347405342387637 0.00099630702602213268 3.5087101951098165 0.00099630702602213268 3.4826798559806744 0.00099630702602213268 3.4566495168509364 0.00099630702602213268 3.4045888385894547 0.00099630702602213268 3.3785584994597064 0.00099630702602213268 3.3525281603305563 0.00099630702602213268 3.3264978212016088 0.00099630702602213268 3.3004674820724658 0.00099630702602213268 3.2744371429427255 0.00099630702602213268 3.2223764646812478 0.00099630702602213268 3.1963461255514978 0.00099630702602213268 3.1703157864223459 0.00099630702602213268 3.1442854472933979 0.00099630702602213268 3.1182551081642562 0.00099630702602213268 3.0922247690345168 0.00099630702602213268 3.040164090773037 0.00099630702602213268 3.0141337516432882 0.00099630702602213268 2.9881034125141368 0.00099630702602213268 2.9620730733851892 0.00099630702602213268 2.9360427342560476 0.00099630702602213268 2.9100123951263082 0.00099630702602213268 2.8606445105684259 0.00099630702602213268 2.8373069651417637 0.00099630702602213268 2.8139694197153799 0.00099630702602213268 2.7906318742890837 0.00099630702602213268 2.7672943288626892 0.00099630702602213268 2.7439567834360168 0.00099630702602213268 2.6945888988781399 0.00099630702602213268 2.6685585597484063 0.00099630702602213268 2.6425282206192713 0.00099630702602213268 2.6164978814903241 0.00099630702602213268 2.5904675423611665 0.00099630702602213268 2.5644372032314111 0.00099630702602213268 2.5123765249699406 0.00099630702602213268 2.4863461858401985 0.00099630702602213268 2.4603158467110537 0.00099630702602213268 2.4342855075821062 0.00099630702602213268 2.4082551684529574 0.00099630702602213268 2.3822248293232113 0.00099630702602213268 2.3301641510617417 0.00099630702602213268 2.3041338119319907 0.00099630702602213268 2.2781034728028362 0.00099630702602213268 2.2520731336738877 0.00099630702602213268 2.226042794544747 0.00099630702602213268 2.2000124554150098 0.00099630702602213268 2.1479517771535264 0.00099630702602213268 2.121921438023779 0.00099630702602213268 2.0958910988946289 0.00099630702602213268 2.0698607597656817 0.00099630702602213268 2.0438304206365383 0.00099630702602213268 2.0178000815067967 0.00099630702602213268 1.9657394032453048 0.00099630702602213268 1.939709064115569 0.00099630702602213268 1.9136787249864309 0.00099630702602213268 1.8876483858574828 0.00099630702602213268 1.8616180467283268 0.00099630702602213268 1.835587707598574 0.00099630702602213268 1.7844246272383479 0.00099630702602213268 1.759291886009628 0.00099630702602213268 1.7341591447813756 0.00099630702602213268 1.7090264035532801 0.00099630702602213268 1.6838936623250311 0.00099630702602213268 1.6587609210963141 0.00099630702602213268 1.6120858302419401 0.00099630702602213268 1.5905434806173193 0.00099630702602213268 1.5690011309928564 0.00099630702602213268 1.5474587813684519 0.00099630702602213268 1.5259164317439995 0.00099630702602213268 1.5043740821193885 0.00099630702602213268 1.4657773723749621 0.00099630702602213268 1.4487230122554766 0.00099630702602213268 1.4316686521360216 0.00099630702602213268 1.4146142920165747 0.00099630702602213268 1.3975599318971155 0.00099630702602213268 1.3805055717776258 0.00099630702602213268 1.1687562013613073 0.00099630702602212292 0.97380360399379895 0.00099630702602217952 0.77905696014949655 0.00099630702602202773 0.58439425150857249 0.00099630702602227297 0.38964760766433404 0.00099630702602201602 0.19469501029675967 0.00099630702602219296 0 0.00099630702602211533
+ 0 8 0.020001865913818001 6 0.049004571489872549 6 0.078007277065946887 6 0.10700998264203111 6 0.1360126882181005 6 0.16401530049821203 6 0.19301800607425304 6 0.22202071165030296 6 0.25102341722637367 6 0.28002612280246714 6 0.30902882837857643 6 0.33803153395467483 6 0.36703423953075048 6 0.3960369451068167 6 0.42503965068288341 6 0.45404235625893824 6 0.48304506183498419 6 0.51204776741101876 6 0.54105047298704145 6 0.56705289867541275 6 0.59605560425145676 6 0.62505830982751509 6 0.65406101540356398 6 0.683063720979602 6 0.71206642655564645 6 0.74006903883577169 6 0.7640712779325638 6 0.78307305055067933 6 1 8
+7 0 0 7 176 30 -0.49845974193988946 5.5511151231257827e-17 -0.4984597420201462 0.0089483268146138273 -0.49827232851857134 0.017896658271801511 -0.49789750179687742 0.026840276112039131 -0.49733540987531505 0.035774471365686775 -0.49658634843267219 0.044694544352988551 -0.49565076080627468 0.053595804684072615 -0.49290303172579447 0.075346325304044642 -0.49088589013437051 0.088169722677614359 -0.48847962275151374 0.10092946625209846 -0.48568667770145163 0.11361139953684614 -0.48251014169883893 0.12620150667811758 -0.4789537400487589 0.13868591245908415 -0.47108993466517823 0.163415852592278 -0.4667825129259226 0.17566144356864929 -0.46210394172821362 0.18777392268534573 -0.45705919390578387 0.19973981142373537 -0.45165384482683957 0.21154588529015061 -0.44589407239406126 0.22317917381588828 -0.43367924301027388 0.24607474800015411 -0.42722415809097064 0.25733708608267519 -0.42042818865259424 0.26840126152403326 -0.41329866758989703 0.27925492004584201 -0.40584347432648177 0.28988606637206799 -0.39807103481480161 0.30028306422903117 -0.38190960942373836 0.32058620939009602 -0.37352058653954195 0.33049240318230833 -0.36483223062232017 0.34014194463567232 -0.35585399183870486 0.34952401275714007 -0.34659579278320984 0.3586282386469849 -0.33706802847823081 0.36744470549880159 -0.31783256840648261 0.38418942651567767 -0.30814236673645462 0.39213786020594521 -0.29822074432399337 0.3998007370839044 -0.2880778181520447 0.40717000430624434 -0.27772403922646771 0.4142380687727481 -0.2671701925760353 0.42099779712629237 -0.24530093248260482 0.43411740341118304 -0.23397173314384981 0.44045440765160276 -0.22245215894003881 0.4464461176718979 -0.21075485355309911 0.45208571167999295 -0.1988927446430156 0.4573669568939458 -0.18687904384783074 0.46228420954194777 -0.16257545024195588 0.4713806215787068 -0.15028550178332487 0.47555980179552071 -0.13787089937933658 0.47936490704444296 -0.12534531354640108 0.48279151957480465 -0.11272258734575356 0.48583585235359056 -0.10001673638345429 0.48849474906543833 -0.074467161498213391 0.49303662062741516 -0.061623379074405668 0.49491960587706879 -0.048724790865963395 0.4964121208798829 -0.035785641598587326 0.49751229819650961 -0.022820231396680952 0.49821892192997103 -0.0098429157833505119 0.49853142772565862 0.016106704431904043 0.49836837930756089 0.029079068450656366 0.49789282472044372 0.042034575496261703 0.49702333291732309 0.054958878270766807 0.49576064860032432 0.067837693057855503 0.49410616726635687 0.080656799722848671 0.49206193520711461 0.10614728398186972 0.48719936527521757 0.11881871994146449 0.48438101626873514 0.13140219429759961 0.48117830617679613 0.14388373221321213 0.47759456718621857 0.15624953930806487 0.47363375998346896 0.1684860016587463 0.46930047375466344 0.19267337047859451 0.45989938000580205 0.20462433232957514 0.454831550960063 0.21641914031941159 0.44940166299736972 0.22804465477306093 0.44361552546189131 0.23948802737263827 0.43747953309294718 0.25073670115741697 0.43100066602500614 0.27282012067305633 0.41737231481884779 0.28365491696832135 0.41022279947579188 0.29427053733507741 0.40274551500658395 0.30465511230955428 0.39494855556774294 0.3147971650387576 0.38684053822424286 0.32468561128046936 0.37843060294951247 0.3439339085257383 0.36102622340698892 0.35329380360211438 0.35203173928839354 0.3623787575077505 0.34275462813583213 0.37117856398323062 0.33320500092396649 0.37968349763434756 0.32339341173593755 0.38788431393210299 0.31333085776336517 0.40366018567682355 0.29272670175549986 0.41123527724580428 0.28218505254287873 0.41848876559125336 0.27141527605369847 0.4254124455831701 0.26042916531053645 0.43199866528994996 0.24923886197333414 0.4382403259783843 0.23785685633939771 0.45002143957705693 0.21473511902410697 0.45556091946250782 0.20299533440090509 0.46074278222251241 0.19108947545914737 0.46556109552497815 0.17903062680719156 0.47001053425322065 0.16683211567639783 0.47408638050596458 0.15450751192112824 0.48148266811747487 0.12963374454121504 0.48480312641152884 0.11708452396310917 0.48774179423462566 0.10443678067704894 0.49029520846965885 0.091704457651150123 0.49246054712729503 0.078901626429025012 0.49423562934597415 0.066042487129782237 0.49700220291992914 0.040240249925186281 0.49799370060981829 0.027297092940454161 0.49859187540920225 0.014326226904836934 0.49879584807835708 0.0013419915002499429 0.49860539319021008 -0.011641263320482589 0.49802093913033968 -0.024609177333627601 0.49606619854996026 -0.050485582867736759 0.49469590755368126 -0.063394133637904596 0.49293378400699489 -0.076258662366574664 0.49078156165882281 -0.089064907439020041 0.48824161910815056 -0.10179871561425878 0.48531697980402821 -0.1144460420250539 0.47904761013846098 -0.13824190561788788 0.47577762577541255 -0.14941018425965283 0.47220400470345664 -0.16048775082137348 0.46832979109421613 -0.17146471064138297 0.46415842754390546 -0.18233130967818242 0.45969375507332966 -0.19307793451044097 0.44963776500089636 -0.21553734643050787 0.44397586891335977 -0.22721858408301207 0.43796021704927651 -0.23872567370005107 0.431597271609376 -0.25004578408910383 0.42489406481111619 -0.261166404459586 0.41785819888868281 -0.27207534442284936 0.40313749452484132 -0.29344612440708334 0.39545262237743872 -0.30390801336073398 0.38745140743560269 -0.31413453467832014 0.3791425300056942 -0.32411424057309685 0.37053517291561378 -0.33383610164638744 0.36163902151480171 -0.34328950688758397 0.34328950688767834 -0.36163902151471167 0.33383610164648309 -0.37053517291552807 0.32411424057319344 -0.37914253000561288 0.31413453467841829 -0.38745140743552409 0.30390801336083451 -0.39545262237736106 0.29344612440718665 -0.40313749452476466 0.27207534442295295 -0.4178581988886122 0.2611664044596948 -0.42489406481104941 0.25004578408921774 -0.4315972716093131 0.23872567370016615 -0.43796021704921617 0.22721858408312473 -0.44397586891330126 0.21553734643061845 -0.44963776500083991 0.19185287885273167 -0.46024226261526885 0.17984959469703193 -0.4651848884232746 0.16769841421048473 -0.46976200450087868 0.15541270354099895 -0.47396834343600663 0.14300604064392003 -0.47779925645548105 0.13049221528203017 -0.48125071342502085 0.1057129655315362 -0.48728208042655857 0.093453804184493103 -0.48988795256495898 0.081120345801083196 -0.49213385497758938 0.068725276567223673 -0.49401728520239951 0.056281368038084652 -0.49553630260214315 0.043801477138089309 -0.4966895283643778 0.020581744959880736 -0.49815038972307757 0.009847986588270425 -0.49855529129348725 -0.00089458572573335551 -0.49869033896615311 -0.011637832608796162 -0.49855532823746557 -0.022373618542432864 -0.49815036134674673 -0.033093811863007666 -0.49747584727625055 -0.052258327669681956 -0.49578568662910022 -0.060711514942100808 -0.49487038443655956 -0.069145817594788814 -0.49378695076538676 -0.077557224372637124 -0.49253586040474523 -0.085941741749629805 -0.4911177073411151 -0.094295393928843854 -0.48953320475829265 -0.19758290235664983 -0.4678044696224789 -0.2881413176812947 -0.42622953281465648 -0.36815177824908774 -0.36423086794942872 -0.43221805763491938 -0.28528478341752833 -0.47641760502413488 -0.19422486767018315 -0.49845945265814579 -0.097047406756765875 -0.49845974193988946 -1.1102230246251565e-16
+ 0 8 0.020001865913818001 6 0.049004571489872549 6 0.078007277065946887 6 0.10700998264203111 6 0.1360126882181005 6 0.16401530049821203 6 0.19301800607425304 6 0.22202071165030296 6 0.25102341722637367 6 0.28002612280246714 6 0.30902882837857643 6 0.33803153395467483 6 0.36703423953075048 6 0.3960369451068167 6 0.42503965068288341 6 0.45404235625893824 6 0.48304506183498419 6 0.51204776741101876 6 0.54105047298704145 6 0.56705289867541275 6 0.59605560425145676 6 0.62505830982751509 6 0.65406101540356398 6 0.683063720979602 6 0.71206642655564645 6 0.74006903883577169 6 0.7640712779325638 6 0.78307305055067933 6 1 8
+Curves 9
+2 5.1507962050250302e-18 0 0 1 0 0 0 -4.2488471045215345e-34 1 0 -1 -4.2488471045215345e-34 0.5
+7 0 0 3 82 80 1.7347234759767455e-18 4.8148248609680896e-35 0.5 1.9081958235744875e-17 8.3390424239522198e-19 0.50680934489010054 0.023319592761920728 2.4339552601114771e-18 0.51987475296392538 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563684e-19 0.50415566868708572 1.9833361382086636 2.9747251477338528e-19 0.50242904741988115 2 1.5430549669256652e-19 0.50126000000000004
+ 0.5 4 0.50778579154106807 1 0.51493896186836807 1 0.52142989606068113 1 0.52780603459347397 1 0.53412586335306578 1 0.54041199030005094 1 0.54667577936015643 1 0.55292381112532962 1 0.55916025190260255 1 0.5653879195921806 1 0.57160880715469398 1 0.57782437955598598 1 0.58403574296875027 1 0.59024375666548634 1 0.59644909646961619 1 0.60265230642268297 1 0.60885382606372285 1 0.61505401798985182 1 0.62125318173927924 1 0.62745156790483447 1 0.63364938657642622 1 0.63984681476564487 1 0.64604400252387284 1 0.65224107719197866 1 0.65843814702339287 1 0.66463530409444338 1 0.67083262664014676 1 0.67703018129340153 1 0.68322802460079535 1 0.68942620435364577 1 0.6956247608491497 1 0.70182372758617795 1 0.70802313283227647 1 0.71422299963579394 1 0.72042334678553011 1 0.72662418966202291 1 0.73282554077662276 1 0.73902740938723799 1 0.74522980205033462 1 0.75143272397473226 1 0.7576361788107443 1 0.76384016917612763 1 0.77004469469988879 1 0.77624975622273162 1 0.78245535199183813 1 0.78866148146223336 1 0.79486814257909799 1 0.80107533366524408 1 0.80728305313818827 1 0.81349129821414223 1 0.81970006782488436 1 0.82590935999263415 1 0.83211917404824265 1 0.83832950937280215 1 0.84454036541318844 1 0.85075174339565085 1 0.85696364404959746 1 0.86317606924070422 1 0.86938902133984342 1 0.8756025049058378 1 0.88181652510541075 1 0.88803108783675355 1 0.89424619887020362 1 0.9004618674628867 1 0.90667810196496923 1 0.9128949135006994 1 0.91911231334857058 1 0.92533031475551653 1 0.93154893314111964 1 0.937768184191767 1 0.94398808685004154 1 0.95020866049980723 1 0.95642992488615519 1 0.96265190400039857 1 0.96887462165344695 1 0.97509810624240079 1 0.98132238514225778 1 0.98754748968945583 1 1.0000000000000027 4
+2 2 0 0 1 0 0 -0 3.0783524855876494e-19 1 0 -1 3.0783524855876494e-19 0.50126000000000004
+7 0 0 5 118 30 0.0039998007999999478 1.4464221294116791e-16 0.49283762810360693 0.0039998007999999478 0.017960209128777795 0.492837629136466 0.0039998007999999478 0.035920479942518978 0.49201948200409823 0.0039998007999999478 0.05383094219538017 0.49038319833905392 0.0039998007999999486 0.07164213738343933 0.48793329662560891 0.0039998007999999486 0.10696790048027376 0.48142430980543938 0.0039998007999999495 0.12448258971656835 0.47736520234338314 0.0039998007999999504 0.14180004390736139 0.47251052802953802 0.0039998007999999504 0.1588724420995164 0.46687369271157053 0.0039998007999999521 0.19127663869964526 0.45451269175820797 0.003999800799999953 0.20664736888523003 0.44789029105706518 0.0039998007999999538 0.22172779165774589 0.4406196027152558 0.0039998007999999547 0.23648180004706143 0.43271803490035937 0.0039998007999999564 0.26633387128159552 0.41506356960465701 0.0039998007999999573 0.28137656041278691 0.40521683430128097 0.0039998007999999582 0.29595991184347059 0.39469124912073661 0.0039998007999999599 0.31004365465635952 0.38351587973919699 0.0039998007999999625 0.33713667972374545 0.35993104477059212 0.0039998007999999642 0.35014605503016377 0.34752149818061068 0.0039998007999999651 0.36258068536716709 0.33452757457279586 0.0039998007999999668 0.37440623336229656 0.3209851557693279 0.0039998007999999695 0.39639132844111097 0.29336589750151815 0.0039998007999999712 0.40659433110434756 0.27932350393004124 0.0039998007999999729 0.4161726983137447 0.2648411884737803 0.0039998007999999747 0.42510176952640671 0.2499562374210694 0.0039998007999999781 0.44191350330830698 0.21891540237858287 0.0039998007999999799 0.44974746030041957 0.20273301039595948 0.0039998007999999816 0.45683829575581447 0.18620478802063381 0.0039998007999999833 0.46316642882234832 0.16937637679661804 0.0039998007999999877 0.4742664485089938 0.13521402894472642 0.0039998007999999894 0.47903837325250231 0.11787997498507034 0.0039998007999999911 0.48301681222360132 0.10034047368951202 0.0039998007999999929 0.48619077923797649 0.082643959178922588 0.0039998007999999972 0.49091662604437208 0.047035774448977385 0.0039998007999999989 0.49246852206744923 0.029123981932133558 0.0039998008000000015 0.49320284007104143 0.011153927413206522 0.0039998008000000033 0.49311755228672283 -0.0068247660439579522 0.0039998008000000067 0.49131275536264374 -0.042699815254525895 0.0039998008000000094 0.48959324002424415 -0.060596294222031756 0.0039998008000000111 0.48705912409229635 -0.078401920826762603 0.0039998008000000128 0.48371740535284191 -0.096067526061194786 0.0039998008000000172 0.47544172078450253 -0.13102163158149102 0.0039998008000000189 0.47050772653249573 -0.14831025191840866 0.0039998008000000206 0.46478908020454873 -0.16536191361220137 0.0039998008000000224 0.45830157344734579 -0.18212952967734794 0.0039998008000000267 0.4438290057578505 -0.21500537540443085 0.0039998008000000284 0.43584389511903265 -0.23111371797959487 0.0039998008000000302 0.42713006043041368 -0.24684684123478884 0.0039998008000000319 0.41771156434437268 -0.26216129923728038 0.0039998008000000345 0.39752129431630984 -0.29187037939793314 0.0039998008000000362 0.38674945103009922 -0.30626510359294112 0.003999800800000038 0.37532893947475843 -0.320158771148989 0.0039998008000000397 0.36329129660380532 -0.33351301566315561 0.0039998008000000423 0.33805181579996968 -0.35907167479369306 0.003999800800000044 0.32484989118122565 -0.3712761771921631 0.0039998008000000449 0.31110083121445564 -0.38287037541256136 0.0039998008000000458 0.2968426029787164 -0.39382225287764538 0.0039998008000000484 0.26738958026057702 -0.41438425425307734 0.0039998008000000492 0.25219468462076122 -0.42399444878431314 0.0039998008000000501 0.23657230198107593 -0.43290529851949539 0.003999800800000051 0.22056557247169545 -0.44109219676280781 0.0039998008000000527 0.18787418534598097 -0.45597674094780882 0.0039998008000000536 0.1711894154499724 -0.46267443801098407 0.0039998008000000545 0.15421096082154112 -0.46860690467958327 0.0039998008000000553 0.13698570628995585 -0.47375775885682647 0.0039998008000000562 0.10213835318124888 -0.48247202469597078 0.0039998008000000562 0.084516134919688513 -0.48603546628730249 0.0039998008000000571 0.066743757974251874 -0.4887931283501773 0.0039998008000000571 0.048870299535826237 -0.4907373957913595 0.0039998008000000571 0.013020762018454845 -0.49299285751545846 0.0039998008000000571 -0.0049554401869569163 -0.4933040595448604 0.0039998008000000571 -0.022933303335245646 -0.49279561194172494 0.0039998008000000571 -0.040863182799573058 -0.49146891874897525 0.0039998008000000562 -0.074683216604735203 -0.48741217482147475 0.0039998008000000562 -0.090592584910555191 -0.48484148786412823 0.0039998008000000562 -0.1063880091701746 -0.48162213020155664 0.0039998008000000553 -0.12203441573744345 -0.477761250398409 0.0039998008000000545 -0.1547443906702809 -0.46825812471422884 0.0039998008000000536 -0.17176326955239504 -0.4624617073699766 0.0039998008000000527 -0.18850606477187051 -0.45589353482485961 0.0039998008000000519 -0.20492654225661341 -0.44857174463350957 0.0039998008000000501 -0.23703370006806201 -0.43246563197189447 0.0039998008000000492 -0.25272049066791191 -0.42368125418461622 0.0039998008000000484 -0.26799592162305202 -0.41418792539654053 0.0039998008000000475 -0.28281781088641067 -0.40401186077914053 0.0039998008000000449 -0.31147492083819839 -0.38235437946183115 0.0039998008000000432 -0.32531023995045261 -0.37087288837856547 0.0039998008000000423 -0.33861254338511482 -0.35876872357152956 0.0039998008000000406 -0.35134509775042155 -0.34607530985545631 0.003999800800000038 -0.37560333468118923 -0.31958353020950853 0.0039998008000000362 -0.38712910056239114 -0.30578507329272431 0.0039998008000000345 -0.39801784266367529 -0.29147083640089355 0.0039998008000000328 -0.40823949247612618 -0.27668034731009861 0.0039998008000000302 -0.42466724005081513 -0.25043124935628519 0.0039998008000000293 -0.43120323635765867 -0.23917901708340872 0.0039998008000000276 -0.43736554466032707 -0.2277152028352083 0.0039998008000000267 -0.44314523355603147 -0.21605642180480858 0.0039998008000000232 -0.45597674094781177 -0.18787418534597608 0.0039998008000000215 -0.46267443801098573 -0.17118941544996594 0.0039998008000000198 -0.46860690467958488 -0.15421096082153435 0.003999800800000018 -0.47375775885682897 -0.13698570628995027 0.0039998008000000137 -0.48202128737986993 -0.10394079814626403 0.003999800800000012 -0.48528970800064286 -0.088159991513890215 0.0039998008000000102 -0.48791144579582579 -0.072254452156979343 0.0039998008000000085 -0.48988067921215045 -0.056259498231603533 0.003999800800000005 -0.49255879636342126 -0.023545097293108594 0.0039998008000000033 -0.49321629826348434 -0.006821364792962214 0.0039998008000000015 -0.49316370297898399 0.0099201791530916135 0.0039998007999999998 -0.49240113643850969 0.026639450361980302 0.0039998007999999911 -0.48603550858492978 0.098818732105655532 0.0039998007999999851 -0.47328926452292719 0.15365230498503715 0.0039998007999999799 -0.45282348349031037 0.20630717762490977 0.0039998007999999738 -0.42519309493117541 0.25535561369046489 0.0039998007999999608 -0.33634440168453511 0.37139739225798474 0.0039998007999999547 -0.26489311940889626 0.43060943183772066 0.0039998007999999504 -0.18086320150689747 0.47210311356572027 0.0039998007999999478 -0.090412011291749 0.49283795281486104 0.0039998007999999478 -2.1891240752605581e-17 0.49283762810360693
+ 0 6 0.029000420763338419 4 0.058000841526677539 4 0.085001233271496168 4 0.11400165403483276 4 0.14300207479821528 4 0.17100248105233079 4 0.20000290181572761 4 0.22900332257910461 4 0.25800374334244064 4 0.28700416410574697 4 0.31600458486905331 4 0.34500500563237591 4 0.37400542639567941 4 0.40300584715895815 4 0.43200626792220864 4 0.46100668868545203 4 0.49000710944870285 4 0.51900753021194346 4 0.54500790744748351 4 0.57400832821072523 4 0.60300874897396439 4 0.63200916973720223 4 0.66100959050043506 4 0.68200989519037636 4 0.7110103159536113 4 0.73701069318915302 4 0.76401108493388792 4 0.85401116742439875 4 1 6
+7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
+ 0 4 0.012452510310546802 1 0.018677614857744929 1 0.024901893757601767 1 0.031125378346555464 1 0.037348095999604043 1 0.043570075113847251 1 0.049791339500195375 1 0.056011913149960867 1 0.062231815808235363 1 0.068451066858882623 1 0.074669685244485792 1 0.080887686651431687 1 0.08710508649930275 1 0.093321898035032894 1 0.099538132537115312 1 0.10575380112979828 1 0.11196891216324817 1 0.11818347489459106 1 0.12439749509416391 1 0.13061097866015836 1 0.13682393075929764 1 0.14303635595040423 1 0.14924825660435104 1 0.15545963458681322 1 0.16167049062719938 1 0.16788082595175882 1 0.17409064000736726 1 0.18029993217511717 1 0.18650870178585921 1 0.19271694686181326 1 0.19892466633475747 1 0.20513185742090337 1 0.21133851853776786 1 0.21754464800816323 1 0.22375024377726974 1 0.22995530530011266 1 0.23615983082387357 1 0.24236382118925689 1 0.24856727602526885 1 0.2547701979496666 1 0.26097259061276307 1 0.26717445922337818 1 0.27337581033797814 1 0.27957665321447084 1 0.28577700036420717 1 0.29197686716772447 1 0.29817627241382305 1 0.30437523915085124 1 0.31057379564635523 1 0.31677197539920571 1 0.32296981870659941 1 0.3291673733598543 1 0.33536469590555762 1 0.34156185297660818 1 0.3477589228080224 1 0.3539559974761281 1 0.36015318523435597 1 0.36635061342357461 1 0.37254843209516636 1 0.37874681826072149 1 0.38494598201014885 1 0.3911461739362776 1 0.39734769357731764 1 0.40355090353038436 1 0.40975624333451433 1 0.41596425703125028 1 0.42217562044401452 1 0.42839119284530658 1 0.43461208040781973 1 0.44083974809739779 1 0.44707618887467077 1 0.45332422063984379 1 0.45958800969994934 1 0.4658741366469345 1 0.47219396540652614 1 0.47857010393931909 1 0.48506103813163204 1 0.49221420845893199 1 0.5 1 0.50778579154106807 1 0.51493896186836796 1 0.52142989606068102 1 0.52780603459347386 1 0.53412586335306556 1 0.54041199030005072 1 0.54667577936015621 1 0.55292381112532929 1 0.55916025190260221 1 0.56538791959218027 1 0.57160880715469353 1 0.57782437955598553 1 0.58403574296874983 1 0.59024375666548579 1 0.59644909646961564 1 0.60265230642268242 1 0.60885382606372229 1 0.61505401798985115 1 0.62125318173927857 1 0.62745156790483381 1 0.63364938657642544 1 0.63984681476564409 1 0.64604400252387206 1 0.65224107719197777 1 0.65843814702339198 1 0.66463530409444249 1 0.67083262664014576 1 0.67703018129340053 1 0.68322802460079435 1 0.68942620435364477 1 0.69562476084914859 1 0.70182372758617684 1 0.70802313283227536 1 0.71422299963579272 1 0.72042334678552888 1 0.72662418966202169 1 0.73282554077662143 1 0.73902740938723666 1 0.74522980205033329 1 0.75143272397473082 1 0.75763617881074286 1 0.76384016917612618 1 0.77004469469988723 1 0.77624975622273007 1 0.78245535199183658 1 0.78866148146223181 1 0.79486814257909633 1 0.80107533366524242 1 0.8072830531381866 1 0.81349129821414046 1 0.81970006782488258 1 0.82590935999263237 1 0.83211917404824076 1 0.83832950937280026 1 0.84454036541318656 1 0.85075174339564885 1 0.85696364404959546 1 0.86317606924070223 1 0.86938902133984142 1 0.87560250490583569 1 0.88181652510540864 1 0.88803108783675144 1 0.8942461988702014 1 0.90046186746288448 1 0.90667810196496701 1 0.91289491350069707 1 0.91911231334856824 1 0.92533031475551419 1 0.9315489331411172 1 0.93776818419176455 1 0.9439880868500391 1 0.95020866049980468 1 0.95642992488615264 1 0.96265190400039602 1 0.9688746216534444 1 0.97509810624239812 1 0.98132238514225512 1 0.98754748968945316 1 1 4
+8 0 0.0025200000000000001
+7 0 0 1 2 2 2 1.5430549669256652e-19 0.50126000000000004 2 -1.5430549669256652e-19 0.49874000000000002
+ 0 2 0.0025200000000000001 2
+2 2 0 0 1 0 0 0 -3.0939065784289713e-19 1 0 -1 -3.0939065784289713e-19 0.49874000000000002
+7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
+ 0 4 0.012452510310546802 1 0.018677614857744929 1 0.024901893757601767 1 0.031125378346555464 1 0.037348095999604043 1 0.043570075113847251 1 0.049791339500195375 1 0.056011913149960867 1 0.062231815808235363 1 0.068451066858882623 1 0.074669685244485792 1 0.080887686651431687 1 0.08710508649930275 1 0.093321898035032894 1 0.099538132537115312 1 0.10575380112979828 1 0.11196891216324817 1 0.11818347489459106 1 0.12439749509416391 1 0.13061097866015836 1 0.13682393075929764 1 0.14303635595040423 1 0.14924825660435104 1 0.15545963458681322 1 0.16167049062719938 1 0.16788082595175882 1 0.17409064000736726 1 0.18029993217511717 1 0.18650870178585921 1 0.19271694686181326 1 0.19892466633475747 1 0.20513185742090337 1 0.21133851853776786 1 0.21754464800816323 1 0.22375024377726974 1 0.22995530530011266 1 0.23615983082387357 1 0.24236382118925689 1 0.24856727602526885 1 0.2547701979496666 1 0.26097259061276307 1 0.26717445922337818 1 0.27337581033797814 1 0.27957665321447084 1 0.28577700036420717 1 0.29197686716772447 1 0.29817627241382305 1 0.30437523915085124 1 0.31057379564635523 1 0.31677197539920571 1 0.32296981870659941 1 0.3291673733598543 1 0.33536469590555762 1 0.34156185297660818 1 0.3477589228080224 1 0.3539559974761281 1 0.36015318523435597 1 0.36635061342357461 1 0.37254843209516636 1 0.37874681826072149 1 0.38494598201014885 1 0.3911461739362776 1 0.39734769357731764 1 0.40355090353038436 1 0.40975624333451433 1 0.41596425703125028 1 0.42217562044401452 1 0.42839119284530658 1 0.43461208040781973 1 0.44083974809739779 1 0.44707618887467077 1 0.45332422063984379 1 0.45958800969994934 1 0.4658741366469345 1 0.47219396540652614 1 0.47857010393931909 1 0.48506103813163204 1 0.49221420845893199 1 0.5 1 0.50778579154106807 1 0.51493896186836796 1 0.52142989606068102 1 0.52780603459347386 1 0.53412586335306556 1 0.54041199030005072 1 0.54667577936015621 1 0.55292381112532929 1 0.55916025190260221 1 0.56538791959218027 1 0.57160880715469353 1 0.57782437955598553 1 0.58403574296874983 1 0.59024375666548579 1 0.59644909646961564 1 0.60265230642268242 1 0.60885382606372229 1 0.61505401798985115 1 0.62125318173927857 1 0.62745156790483381 1 0.63364938657642544 1 0.63984681476564409 1 0.64604400252387206 1 0.65224107719197777 1 0.65843814702339198 1 0.66463530409444249 1 0.67083262664014576 1 0.67703018129340053 1 0.68322802460079435 1 0.68942620435364477 1 0.69562476084914859 1 0.70182372758617684 1 0.70802313283227536 1 0.71422299963579272 1 0.72042334678552888 1 0.72662418966202169 1 0.73282554077662143 1 0.73902740938723666 1 0.74522980205033329 1 0.75143272397473082 1 0.75763617881074286 1 0.76384016917612618 1 0.77004469469988723 1 0.77624975622273007 1 0.78245535199183658 1 0.78866148146223181 1 0.79486814257909633 1 0.80107533366524242 1 0.8072830531381866 1 0.81349129821414046 1 0.81970006782488258 1 0.82590935999263237 1 0.83211917404824076 1 0.83832950937280026 1 0.84454036541318656 1 0.85075174339564885 1 0.85696364404959546 1 0.86317606924070223 1 0.86938902133984142 1 0.87560250490583569 1 0.88181652510540864 1 0.88803108783675144 1 0.8942461988702014 1 0.90046186746288448 1 0.90667810196496701 1 0.91289491350069707 1 0.91911231334856824 1 0.92533031475551419 1 0.9315489331411172 1 0.93776818419176455 1 0.9439880868500391 1 0.95020866049980468 1 0.95642992488615264 1 0.96265190400039602 1 0.9688746216534444 1 0.97509810624239812 1 0.98132238514225512 1 0.98754748968945316 1 1 4
+7 0 0 7 176 30 1.9960001992000003 1.4464221294116791e-16 0.49845974193988957 1.9960001992000003 0.0089483268146139158 0.49845974202014631 1.9960001992000003 0.017896658271801601 0.49827232851857145 1.9960001992000003 0.026840276112039221 0.49789750179687753 1.9960001992000003 0.035774471365686865 0.49733540987531516 1.9960001992000003 0.044694544352988641 0.4965863484326723 1.9960001992000003 0.053595804684072705 0.49565076080627479 1.9960001992000003 0.075346325304044726 0.49290303172579458 1.9960001992000003 0.088169722677614443 0.49088589013437062 1.9960001992000003 0.10092946625209855 0.48847962275151385 1.9960001992000003 0.11361139953684622 0.48568667770145174 1.9960001992000003 0.12620150667811766 0.48251014169883905 1.9960001992000003 0.13868591245908424 0.47895374004875901 1.9960001992000003 0.16341585259227809 0.47108993466517834 1.9960001992000003 0.17566144356864938 0.46678251292592271 1.9960001992000003 0.18777392268534582 0.46210394172821373 1.9960001992000003 0.19973981142373545 0.45705919390578398 1.9960001992000003 0.2115458852901507 0.45165384482683968 1.9960001992000003 0.22317917381588837 0.44589407239406137 1.9960001992000003 0.24607474800015419 0.433679243010274 1.9960001992000003 0.2573370860826753 0.42722415809097075 1.9960001992000003 0.26840126152403337 0.42042818865259435 1.9960001992000003 0.27925492004584213 0.41329866758989714 1.9960001992000003 0.2898860663720681 0.40584347432648188 1.9960001992000003 0.30028306422903128 0.39807103481480172 1.9960001992000003 0.32058620939009613 0.38190960942373847 1.9960001992000003 0.33049240318230844 0.37352058653954207 1.9960001992000003 0.34014194463567243 0.36483223062232029 1.9960001992000003 0.34952401275714018 0.35585399183870497 1.9960001992000003 0.35862823864698501 0.34659579278320995 1.9960001992000003 0.3674447054988017 0.33706802847823092 1.9960001992000003 0.38418942651567778 0.31783256840648272 1.9960001992000003 0.39213786020594532 0.30814236673645473 1.9960001992000003 0.39980073708390451 0.29822074432399348 1.9960001992000003 0.40717000430624445 0.28807781815204481 1.9960001992000003 0.41423806877274821 0.27772403922646782 1.9960001992000003 0.42099779712629248 0.26717019257603541 1.9960001992000003 0.43411740341118316 0.2453009324826049 1.9960001992000003 0.44045440765160287 0.23397173314384989 1.9960001992000003 0.44644611767189801 0.22245215894003889 1.9960001992000003 0.45208571167999306 0.2107548535530992 1.9960001992000003 0.45736695689394591 0.19889274464301568 1.9960001992000003 0.46228420954194788 0.18687904384783083 1.9960001992000003 0.47138062157870692 0.16257545024195597 1.9960001992000003 0.47555980179552082 0.15028550178332495 1.9960001992000003 0.47936490704444307 0.13787089937933666 1.9960001992000003 0.48279151957480476 0.12534531354640116 1.9960001992000003 0.48583585235359067 0.11272258734575365 1.9960001992000003 0.48849474906543844 0.10001673638345437 1.9960001992000003 0.49303662062741527 0.074467161498213474 1.9960001992000003 0.4949196058770689 0.061623379074405758 1.9960001992000003 0.49641212087988301 0.048724790865963485 1.9960001992000003 0.49751229819650972 0.035785641598587416 1.9960001992000003 0.49821892192997114 0.022820231396681042 1.9960001992000003 0.49853142772565873 0.0098429157833506004 1.9960001992000003 0.498368379307561 -0.016106704431903953 1.9960001992000003 0.49789282472044383 -0.029079068450656276 1.9960001992000003 0.4970233329173232 -0.042034575496261613 1.9960001992000003 0.49576064860032443 -0.054958878270766717 1.9960001992000003 0.49410616726635698 -0.067837693057855419 1.9960001992000003 0.49206193520711472 -0.080656799722848588 1.9960001992000003 0.48719936527521768 -0.10614728398186964 1.9960001992000003 0.48438101626873525 -0.11881871994146441 1.9960001992000003 0.48117830617679624 -0.13140219429759953 1.9960001992000003 0.47759456718621868 -0.14388373221321205 1.9960001992000003 0.47363375998346907 -0.15624953930806479 1.9960001992000003 0.46930047375466355 -0.16848600165874622 1.9960001992000003 0.45989938000580216 -0.19267337047859442 1.9960001992000003 0.45483155096006311 -0.20462433232957505 1.9960001992000003 0.44940166299736983 -0.21641914031941151 1.9960001992000003 0.44361552546189142 -0.22804465477306085 1.9960001992000003 0.43747953309294729 -0.23948802737263819 1.9960001992000003 0.43100066602500625 -0.25073670115741686 1.9960001992000003 0.4173723148188479 -0.27282012067305622 1.9960001992000003 0.41022279947579199 -0.28365491696832124 1.9960001992000003 0.40274551500658406 -0.2942705373350773 1.9960001992000003 0.39494855556774305 -0.30465511230955417 1.9960001992000003 0.38684053822424297 -0.31479716503875749 1.9960001992000003 0.37843060294951258 -0.32468561128046924 1.9960001992000003 0.36102622340698903 -0.34393390852573819 1.9960001992000003 0.35203173928839365 -0.35329380360211426 1.9960001992000003 0.34275462813583224 -0.36237875750775039 1.9960001992000003 0.3332050009239666 -0.37117856398323051 1.9960001992000003 0.32339341173593766 -0.37968349763434744 1.9960001992000003 0.31333085776336528 -0.38788431393210288 1.9960001992000003 0.29272670175549997 -0.40366018567682344 1.9960001992000003 0.28218505254287884 -0.41123527724580416 1.9960001992000003 0.27141527605369858 -0.41848876559125325 1.9960001992000003 0.26042916531053656 -0.42541244558316998 1.9960001992000003 0.24923886197333422 -0.43199866528994985 1.9960001992000003 0.23785685633939779 -0.43824032597838419 1.9960001992000003 0.21473511902410705 -0.45002143957705681 1.9960001992000003 0.20299533440090517 -0.45556091946250771 1.9960001992000003 0.19108947545914745 -0.4607427822225123 1.9960001992000003 0.17903062680719165 -0.46556109552497804 1.9960001992000003 0.16683211567639791 -0.47001053425322054 1.9960001992000003 0.15450751192112833 -0.47408638050596447 1.9960001992000003 0.12963374454121512 -0.48148266811747475 1.9960001992000003 0.11708452396310925 -0.48480312641152873 1.9960001992000003 0.10443678067704902 -0.48774179423462555 1.9960001992000003 0.091704457651150206 -0.49029520846965874 1.9960001992000003 0.078901626429025096 -0.49246054712729492 1.9960001992000003 0.066042487129782321 -0.49423562934597404 1.9960001992000003 0.040240249925186371 -0.49700220291992903 1.9960001992000003 0.027297092940454251 -0.49799370060981818 1.9960001992000003 0.014326226904837022 -0.49859187540920213 1.9960001992000003 0.001341991500250032 -0.49879584807835697 1.9960001992000003 -0.011641263320482501 -0.49860539319020997 1.9960001992000003 -0.02460917733362751 -0.49802093913033957 1.9960001992000003 -0.050485582867736668 -0.49606619854996015 1.9960001992000003 -0.063394133637904512 -0.49469590755368115 1.9960001992000003 -0.076258662366574581 -0.49293378400699478 1.9960001992000003 -0.089064907439019958 -0.4907815616588227 1.9960001992000003 -0.1017987156142587 -0.48824161910815045 1.9960001992000003 -0.11444604202505382 -0.4853169798040281 1.9960001992000003 -0.13824190561788779 -0.47904761013846087 1.9960001992000003 -0.14941018425965275 -0.47577762577541244 1.9960001992000003 -0.1604877508213734 -0.47220400470345653 1.9960001992000003 -0.17146471064138288 -0.46832979109421602 1.9960001992000003 -0.18233130967818234 -0.46415842754390535 1.9960001992000003 -0.19307793451044089 -0.45969375507332955 1.9960001992000003 -0.21553734643050779 -0.44963776500089625 1.9960001992000003 -0.22721858408301199 -0.44397586891335966 1.9960001992000003 -0.23872567370005099 -0.4379602170492764 1.9960001992000003 -0.25004578408910372 -0.43159727160937589 1.9960001992000003 -0.26116640445958589 -0.42489406481111608 1.9960001992000003 -0.27207534442284925 -0.4178581988886827 1.9960001992000003 -0.29344612440708323 -0.40313749452484121 1.9960001992000003 -0.30390801336073386 -0.39545262237743861 1.9960001992000003 -0.31413453467832003 -0.38745140743560258 1.9960001992000003 -0.32411424057309673 -0.37914253000569409 1.9960001992000003 -0.33383610164638733 -0.37053517291561366 1.9960001992000003 -0.34328950688758386 -0.3616390215148016 1.9960001992000003 -0.36163902151471156 -0.34328950688767823 1.9960001992000003 -0.37053517291552795 -0.33383610164648297 1.9960001992000003 -0.37914253000561277 -0.32411424057319332 1.9960001992000003 -0.38745140743552398 -0.31413453467841818 1.9960001992000003 -0.39545262237736095 -0.30390801336083439 1.9960001992000003 -0.40313749452476455 -0.29344612440718654 1.9960001992000003 -0.41785819888861209 -0.27207534442295284 1.9960001992000003 -0.4248940648110493 -0.26116640445969469 1.9960001992000003 -0.43159727160931299 -0.25004578408921763 1.9960001992000003 -0.43796021704921606 -0.23872567370016606 1.9960001992000003 -0.44397586891330115 -0.22721858408312465 1.9960001992000003 -0.4496377650008398 -0.21553734643061837 1.9960001992000003 -0.46024226261526874 -0.19185287885273158 1.9960001992000003 -0.46518488842327449 -0.17984959469703185 1.9960001992000003 -0.46976200450087857 -0.16769841421048465 1.9960001992000003 -0.47396834343600652 -0.15541270354099887 1.9960001992000003 -0.47779925645548094 -0.14300604064391995 1.9960001992000003 -0.48125071342502074 -0.13049221528203009 1.9960001992000003 -0.48728208042655846 -0.10571296553153611 1.9960001992000003 -0.48988795256495887 -0.09345380418449302 1.9960001992000003 -0.49213385497758927 -0.081120345801083113 1.9960001992000003 -0.4940172852023994 -0.068725276567223589 1.9960001992000003 -0.49553630260214304 -0.056281368038084562 1.9960001992000003 -0.49668952836437769 -0.043801477138089219 1.9960001992000003 -0.49815038972307746 -0.020581744959880646 1.9960001992000003 -0.49855529129348714 -0.0098479865882703348 1.9960001992000003 -0.498690338966153 0.00089458572573344484 1.9960001992000003 -0.49855532823746546 0.01163783260879625 1.9960001992000003 -0.49815036134674662 0.022373618542432954 1.9960001992000003 -0.49747584727625044 0.033093811863007756 1.9960001992000003 -0.49578568662910011 0.052258327669682046 1.9960001992000003 -0.49487038443655945 0.060711514942100898 1.9960001992000003 -0.49378695076538665 0.069145817594788897 1.9960001992000003 -0.49253586040474512 0.077557224372637207 1.9960001992000003 -0.49111770734111498 0.085941741749629888 1.9960001992000003 -0.48953320475829254 0.094295393928843937 1.9960001992000003 -0.46780446962247879 0.19758290235664991 1.9960001992000003 -0.42622953281465636 0.28814131768129481 1.9960001992000003 -0.36423086794942861 0.36815177824908785 1.9960001992000003 -0.28528478341752822 0.43221805763491949 1.9960001992000003 -0.19422486767018307 0.47641760502413499 1.9960001992000003 -0.097047406756765792 0.4984594526581459 1.9960001992000003 -2.1891240752605581e-17 0.49845974193988957
+ 0 8 0.020001865913818001 6 0.049004571489872549 6 0.078007277065946887 6 0.10700998264203111 6 0.1360126882181005 6 0.16401530049821203 6 0.19301800607425304 6 0.22202071165030296 6 0.25102341722637367 6 0.28002612280246714 6 0.30902882837857643 6 0.33803153395467483 6 0.36703423953075048 6 0.3960369451068167 6 0.42503965068288341 6 0.45404235625893824 6 0.48304506183498419 6 0.51204776741101876 6 0.54105047298704145 6 0.56705289867541275 6 0.59605560425145676 6 0.62505830982751509 6 0.65406101540356398 6 0.683063720979602 6 0.71206642655564645 6 0.74006903883577169 6 0.7640712779325638 6 0.78307305055067933 6 1 8
+Polygon3D 0
+PolygonOnTriangulations 0
+Surfaces 5
+7 0 0 0 1 0 0
+8 0.5 1
+7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
+ 0 4 0.012452510310546802 1 0.018677614857744929 1 0.024901893757601767 1 0.031125378346555464 1 0.037348095999604043 1 0.043570075113847251 1 0.049791339500195375 1 0.056011913149960867 1 0.062231815808235363 1 0.068451066858882623 1 0.074669685244485792 1 0.080887686651431687 1 0.08710508649930275 1 0.093321898035032894 1 0.099538132537115312 1 0.10575380112979828 1 0.11196891216324817 1 0.11818347489459106 1 0.12439749509416391 1 0.13061097866015836 1 0.13682393075929764 1 0.14303635595040423 1 0.14924825660435104 1 0.15545963458681322 1 0.16167049062719938 1 0.16788082595175882 1 0.17409064000736726 1 0.18029993217511717 1 0.18650870178585921 1 0.19271694686181326 1 0.19892466633475747 1 0.20513185742090337 1 0.21133851853776786 1 0.21754464800816323 1 0.22375024377726974 1 0.22995530530011266 1 0.23615983082387357 1 0.24236382118925689 1 0.24856727602526885 1 0.2547701979496666 1 0.26097259061276307 1 0.26717445922337818 1 0.27337581033797814 1 0.27957665321447084 1 0.28577700036420717 1 0.29197686716772447 1 0.29817627241382305 1 0.30437523915085124 1 0.31057379564635523 1 0.31677197539920571 1 0.32296981870659941 1 0.3291673733598543 1 0.33536469590555762 1 0.34156185297660818 1 0.3477589228080224 1 0.3539559974761281 1 0.36015318523435597 1 0.36635061342357461 1 0.37254843209516636 1 0.37874681826072149 1 0.38494598201014885 1 0.3911461739362776 1 0.39734769357731764 1 0.40355090353038436 1 0.40975624333451433 1 0.41596425703125028 1 0.42217562044401452 1 0.42839119284530658 1 0.43461208040781973 1 0.44083974809739779 1 0.44707618887467077 1 0.45332422063984379 1 0.45958800969994934 1 0.4658741366469345 1 0.47219396540652614 1 0.47857010393931909 1 0.48506103813163204 1 0.49221420845893199 1 0.5 1 0.50778579154106807 1 0.51493896186836796 1 0.52142989606068102 1 0.52780603459347386 1 0.53412586335306556 1 0.54041199030005072 1 0.54667577936015621 1 0.55292381112532929 1 0.55916025190260221 1 0.56538791959218027 1 0.57160880715469353 1 0.57782437955598553 1 0.58403574296874983 1 0.59024375666548579 1 0.59644909646961564 1 0.60265230642268242 1 0.60885382606372229 1 0.61505401798985115 1 0.62125318173927857 1 0.62745156790483381 1 0.63364938657642544 1 0.63984681476564409 1 0.64604400252387206 1 0.65224107719197777 1 0.65843814702339198 1 0.66463530409444249 1 0.67083262664014576 1 0.67703018129340053 1 0.68322802460079435 1 0.68942620435364477 1 0.69562476084914859 1 0.70182372758617684 1 0.70802313283227536 1 0.71422299963579272 1 0.72042334678552888 1 0.72662418966202169 1 0.73282554077662143 1 0.73902740938723666 1 0.74522980205033329 1 0.75143272397473082 1 0.75763617881074286 1 0.76384016917612618 1 0.77004469469988723 1 0.77624975622273007 1 0.78245535199183658 1 0.78866148146223181 1 0.79486814257909633 1 0.80107533366524242 1 0.8072830531381866 1 0.81349129821414046 1 0.81970006782488258 1 0.82590935999263237 1 0.83211917404824076 1 0.83832950937280026 1 0.84454036541318656 1 0.85075174339564885 1 0.85696364404959546 1 0.86317606924070223 1 0.86938902133984142 1 0.87560250490583569 1 0.88181652510540864 1 0.88803108783675144 1 0.8942461988702014 1 0.90046186746288448 1 0.90667810196496701 1 0.91289491350069707 1 0.91911231334856824 1 0.92533031475551419 1 0.9315489331411172 1 0.93776818419176455 1 0.9439880868500391 1 0.95020866049980468 1 0.95642992488615264 1 0.96265190400039602 1 0.9688746216534444 1 0.97509810624239812 1 0.98132238514225512 1 0.98754748968945316 1 1 4
+7 0 0 0 1 0 0
+8 0.39936054338562016 0.5
+7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
+ 0 4 0.012452510310546802 1 0.018677614857744929 1 0.024901893757601767 1 0.031125378346555464 1 0.037348095999604043 1 0.043570075113847251 1 0.049791339500195375 1 0.056011913149960867 1 0.062231815808235363 1 0.068451066858882623 1 0.074669685244485792 1 0.080887686651431687 1 0.08710508649930275 1 0.093321898035032894 1 0.099538132537115312 1 0.10575380112979828 1 0.11196891216324817 1 0.11818347489459106 1 0.12439749509416391 1 0.13061097866015836 1 0.13682393075929764 1 0.14303635595040423 1 0.14924825660435104 1 0.15545963458681322 1 0.16167049062719938 1 0.16788082595175882 1 0.17409064000736726 1 0.18029993217511717 1 0.18650870178585921 1 0.19271694686181326 1 0.19892466633475747 1 0.20513185742090337 1 0.21133851853776786 1 0.21754464800816323 1 0.22375024377726974 1 0.22995530530011266 1 0.23615983082387357 1 0.24236382118925689 1 0.24856727602526885 1 0.2547701979496666 1 0.26097259061276307 1 0.26717445922337818 1 0.27337581033797814 1 0.27957665321447084 1 0.28577700036420717 1 0.29197686716772447 1 0.29817627241382305 1 0.30437523915085124 1 0.31057379564635523 1 0.31677197539920571 1 0.32296981870659941 1 0.3291673733598543 1 0.33536469590555762 1 0.34156185297660818 1 0.3477589228080224 1 0.3539559974761281 1 0.36015318523435597 1 0.36635061342357461 1 0.37254843209516636 1 0.37874681826072149 1 0.38494598201014885 1 0.3911461739362776 1 0.39734769357731764 1 0.40355090353038436 1 0.40975624333451433 1 0.41596425703125028 1 0.42217562044401452 1 0.42839119284530658 1 0.43461208040781973 1 0.44083974809739779 1 0.44707618887467077 1 0.45332422063984379 1 0.45958800969994934 1 0.4658741366469345 1 0.47219396540652614 1 0.47857010393931909 1 0.48506103813163204 1 0.49221420845893199 1 0.5 1 0.50778579154106807 1 0.51493896186836796 1 0.52142989606068102 1 0.52780603459347386 1 0.53412586335306556 1 0.54041199030005072 1 0.54667577936015621 1 0.55292381112532929 1 0.55916025190260221 1 0.56538791959218027 1 0.57160880715469353 1 0.57782437955598553 1 0.58403574296874983 1 0.59024375666548579 1 0.59644909646961564 1 0.60265230642268242 1 0.60885382606372229 1 0.61505401798985115 1 0.62125318173927857 1 0.62745156790483381 1 0.63364938657642544 1 0.63984681476564409 1 0.64604400252387206 1 0.65224107719197777 1 0.65843814702339198 1 0.66463530409444249 1 0.67083262664014576 1 0.67703018129340053 1 0.68322802460079435 1 0.68942620435364477 1 0.69562476084914859 1 0.70182372758617684 1 0.70802313283227536 1 0.71422299963579272 1 0.72042334678552888 1 0.72662418966202169 1 0.73282554077662143 1 0.73902740938723666 1 0.74522980205033329 1 0.75143272397473082 1 0.75763617881074286 1 0.76384016917612618 1 0.77004469469988723 1 0.77624975622273007 1 0.78245535199183658 1 0.78866148146223181 1 0.79486814257909633 1 0.80107533366524242 1 0.8072830531381866 1 0.81349129821414046 1 0.81970006782488258 1 0.82590935999263237 1 0.83211917404824076 1 0.83832950937280026 1 0.84454036541318656 1 0.85075174339564885 1 0.85696364404959546 1 0.86317606924070223 1 0.86938902133984142 1 0.87560250490583569 1 0.88181652510540864 1 0.88803108783675144 1 0.8942461988702014 1 0.90046186746288448 1 0.90667810196496701 1 0.91289491350069707 1 0.91911231334856824 1 0.92533031475551419 1 0.9315489331411172 1 0.93776818419176455 1 0.9439880868500391 1 0.95020866049980468 1 0.95642992488615264 1 0.96265190400039602 1 0.9688746216534444 1 0.97509810624239812 1 0.98132238514225512 1 0.98754748968945316 1 1 4
+7 0 0 0 1 0 0
+8 0 0.0025200000000000001
+7 0 0 1 2 2 2 1.5430549669256652e-19 0.50126000000000004 2 -1.5430549669256652e-19 0.49874000000000002
+ 0 2 0.0025200000000000001 2
+1 0.0039998007999999131 8.9131061709910073e-17 0 0 0 1 1 0 -0 -0 1 0
+7 0 0 0 1 0 0
+8 0 0.14988159487986658
+7 0 0 3 161 159 2 -1.5430549669256652e-19 0.49874000000000002 1.9833361382086645 -2.9747251477337483e-19 0.49757095258011952 1.9583381586926543 -5.0892263559563135e-19 0.49584433131291433 1.9250027614527676 -7.8457921347277432e-19 0.4935934245366172 1.9000026586304333 -9.8823053613703899e-19 0.49193048528910471 1.8750025592158881 -1.1888690135049838e-18 0.4902921477904264 1.8500024852095793 -1.3865837818195555e-18 0.48867768418792301 1.8250023767545407 -1.5813779477376294e-18 0.48708706911381594 1.8000023192107328 -1.773393462605671e-18 0.48551914344739744 1.7750022467495099 -1.9626161964141328e-18 0.48397402256895145 1.7500021769862493 -2.1491089571273625e-18 0.48245119361252847 1.725002124924 -2.3329283928381615e-18 0.48095019401134731 1.7000020588965696 -2.5140787056672e-18 0.47947098945248867 1.6750020183358318 -2.6926339321358919e-18 0.47801297538187659 1.6500019715086833 -2.8685869706598533e-18 0.47657620978834814 1.6250019261465984 -3.0419662649459682e-18 0.47516046041141086 1.6000019045915124 -3.2128119407522834e-18 0.47376539959938518 1.5750018611398 -3.3810816848744256e-18 0.47239137286580535 1.5500018412141525 -3.5468319332920984e-18 0.47103791937592504 1.5250018140314476 -3.7100255075435642e-18 0.46970534271492298 1.5000018018735211 -3.8706815939158447e-18 0.46839348621487631 1.4750017821129258 -4.028760401066198e-18 0.46710267479675627 1.4500017631044932 -4.1842553455478035e-18 0.4658329622184858 1.4250017572422806 -4.3371589190041005e-18 0.46458440979044896 1.4000017579020028 -4.4874339572997497e-18 0.46335732098084054 1.3750017445539506 -4.6350164257833059e-18 0.46215221867227035 1.3500017497104364 -4.7799060410146469e-18 0.46096910517920264 1.3250017471876288 -4.9220205291513537e-18 0.45980865231854395 1.3000017496015197 -5.0613222355506718e-18 0.45867116756378606 1.2750017563136899 -5.1977499999333204e-18 0.45755715032650884 1.250001754931374 -5.3312187525097374e-18 0.45646729525425994 1.2250017689786827 -5.4616892010639862e-18 0.4554019231923308 1.2000017686255569 -5.5890359269764081e-18 0.45436205826147008 1.1750017818589338 -5.7132124239976073e-18 0.45334808021402279 1.1500017859313909 -5.8340889622012194e-18 0.45236104837522834 1.1250017860804482 -5.9515699570538769e-18 0.45140174325203325 1.1000018021641198 -6.0655795402276286e-18 0.45047078435634891 1.0750017928663855 -6.1759287614508636e-18 0.44956971458423128 1.0500018025965248 -6.282567992417936e-18 0.44869893918154929 1.0250017955897455 -6.3852964181495131e-18 0.44786009792704962 1.0000017949895996 -6.4840120337924506e-18 0.44705402375356817 0.97500177720689996 -6.5785152579025412e-18 0.44628234636727293 0.95000175948450394 -6.668677918570597e-18 0.44554611237122743 0.925001742705295 -6.7543303362736803e-18 0.44484670730388298 0.90000171052912803 -6.8352506919978078e-18 0.44418594245494475 0.87500166602524854 -6.9112469896499084e-18 0.44356538559148817 0.8500016172191377 -6.9821333060719608e-18 0.44298655489131078 0.82500155544598208 -7.0476695585710873e-18 0.44245141077837341 0.80000147606193306 -7.1076059596080991e-18 0.44196199292272115 0.77500138440576327 -7.1617121545291489e-18 0.44152018231284801 0.75000127037909226 -7.2096896886731792e-18 0.44112841601600683 0.72500113731331339 -7.2512727033315819e-18 0.44078886493329994 0.70000097733603439 -7.2861329724045402e-18 0.44050420923422628 0.67500078743695668 -7.3139387294167085e-18 0.44027715799764505 0.65000056581052568 -7.3343611918086362e-18 0.44011039593689294 0.62500030381966976 -7.3469896673176114e-18 0.44000727660879146 0.599999996268608 -7.3514441628827603e-18 0.43997090289215535 0.57499963477466864 -7.3472302378954178e-18 0.44000531220094768 0.54999920910997702 -7.3339130573379564e-18 0.44011405523254449 0.52499870845281615 -7.3109336871371137e-18 0.44030169603001235 0.49999811730067484 -7.2777289464650523e-18 0.44057283331380043 0.4749974183402747 -7.2336560828597772e-18 0.44093271555605967 0.44999658353944116 -7.178027176664841e-18 0.44138696004707267 0.424995582936119 -7.1100466859224339e-18 0.44194206287990362 0.3999943881877619 -7.0288063085164628e-18 0.44260544090418402 0.37499291692528286 -6.9333678030051947e-18 0.4433847554426919 0.34999113520357911 -6.8225094255365161e-18 0.44428998279106585 0.32498888741612902 -6.6949963156204392e-18 0.44533120635042073 0.29998607841486952 -6.5491907978994316e-18 0.44652179875487991 0.27498244677018918 -6.38331220910214e-18 0.44787630022348934 0.24997768313638297 -6.19507571883185e-18 0.44941336781229413 0.22497127548260265 -5.9817709389340966e-18 0.45115513352013953 0.19996213187796777 -5.739853504143992e-18 0.45313053928577363 0.17494949942983198 -5.4648609407550467e-18 0.45537602397230048 0.1499272098226376 -5.1502598549346268e-18 0.45794493678895465 0.12490064235761028 -4.7884830559303316e-18 0.46089906853744067 0.099808093953660026 -4.361690926776562e-18 0.46438409074507569 0.074767428700107594 -3.8637709081745812e-18 0.46844991624634391 0.048425865581034788 -3.1716327642207901e-18 0.4741016530935368 0.023319592761920326 -2.4339552601114606e-18 0.4801252470360754 -0.012153554555310392 5.4512201713697857e-33 0.5 0.023319592761920586 2.4339552601114683e-18 0.5198747529639246 0.048425865581034538 3.1716327642207832e-18 0.52589834690646309 0.074767428700108135 3.8637709081745951e-18 0.5315500837536562 0.099808093953660582 4.3616909267765721e-18 0.53561590925492442 0.12490064235761013 4.7884830559303285e-18 0.53910093146255933 0.14992720982263738 5.1502598549346238e-18 0.54205506321104535 0.17494949942983262 5.4648609407550537e-18 0.54462397602769963 0.1999621318779676 5.7398535041439905e-18 0.54686946071422637 0.22497127548260321 5.9817709389341035e-18 0.54884486647986053 0.24997768313638269 6.1950757188318508e-18 0.55058663218770587 0.27498244677018924 6.38331220910214e-18 0.55212369977651066 0.29998607841486929 6.5491907978994293e-18 0.55347820124512004 0.32498888741612902 6.6949963156204408e-18 0.55466879364957933 0.34999113520357894 6.8225094255365153e-18 0.55571001720893409 0.37499291692528275 6.9333678030051955e-18 0.55661524455730804 0.39999438818776234 7.0288063085164658e-18 0.55739455909581603 0.42499558293611889 7.1100466859224324e-18 0.55805793712009633 0.44999658353944089 7.1780271766648425e-18 0.55861303995292733 0.47499741834027437 7.2336560828597741e-18 0.55906728444394027 0.49999811730067484 7.2777289464650508e-18 0.55942716668619952 0.52499870845281615 7.3109336871371153e-18 0.55969830396998765 0.54999920910997679 7.3339130573379548e-18 0.55988594476745546 0.57499963477466931 7.3472302378954209e-18 0.55999468779905237 0.599999996268608 7.3514441628827588e-18 0.56002909710784465 0.62500030381967009 7.3469896673176145e-18 0.55999272339120854 0.65000056581052534 7.3343611918086346e-18 0.55988960406310706 0.67500078743695691 7.31393872941671e-18 0.559722842002355 0.70000097733603461 7.2861329724045417e-18 0.55949579076577372 0.7250011373133135 7.2512727033315819e-18 0.55921113506670006 0.75000127037909203 7.2096896886731839e-18 0.55887158398399317 0.77500138440576372 7.1617121545291443e-18 0.55847981768715194 0.80000147606193273 7.1076059596081007e-18 0.55803800707727891 0.8250015554459823 7.0476695585710873e-18 0.55754858922162653 0.85000161721913681 6.9821333060719638e-18 0.55701344510868922 0.87500166602524831 6.9112469896499069e-18 0.55643461440851183 0.90000171052912781 6.8352506919978078e-18 0.5558140575450552 0.92500174270529456 6.7543303362736803e-18 0.55515329269611702 0.95000175948450472 6.6686779185705986e-18 0.55445388762877257 0.97500177720689929 6.5785152579025381e-18 0.55371765363272707 1.0000017949895996 6.4840120337924514e-18 0.55294597624643183 1.025001795589745 6.3852964181495185e-18 0.55213990207295038 1.0500018025965248 6.2825679924179353e-18 0.55130106081845076 1.0750017928663853 6.1759287614508621e-18 0.55043028541576877 1.1000018021641207 6.0655795402276332e-18 0.54952921564365109 1.1250017860804482 5.9515699570538707e-18 0.54859825674796669 1.1500017859313905 5.8340889622012224e-18 0.54763895162477172 1.1750017818589333 5.7132124239976096e-18 0.54665191978597727 1.2000017686255571 5.5890359269764058e-18 0.54563794173852997 1.2250017689786832 5.4616892010639854e-18 0.54459807680766914 1.2500017549313736 5.3312187525097358e-18 0.54353270474574011 1.2750017563136899 5.1977499999333235e-18 0.54244284967349121 1.3000017496015199 5.0613222355506718e-18 0.54132883243621399 1.3250017471876288 4.9220205291513553e-18 0.54019134768145605 1.3500017497104357 4.77990604101465e-18 0.53903089482079736 1.3750017445539517 4.6350164257833028e-18 0.53784778132772959 1.4000017579020023 4.4874339572997512e-18 0.53664267901915941 1.4250017572422806 4.3371589190041005e-18 0.53541559020955098 1.450001763104493 4.1842553455478005e-18 0.5341670377815142 1.4750017821129255 4.0287604010661964e-18 0.53289732520324373 1.5000018018735206 3.8706815939158486e-18 0.53160651378512369 1.5250018140314483 3.7100255075435627e-18 0.53029465728507708 1.5500018412141516 3.5468319332920984e-18 0.52896208062407502 1.5750018611397998 3.3810816848744244e-18 0.52760862713419465 1.6000019045915126 3.2128119407522838e-18 0.52623460040061487 1.6250019261465989 3.0419662649459686e-18 0.5248395395885892 1.6500019715086836 2.8685869706598525e-18 0.52342379021165186 1.6750020183358314 2.6926339321358916e-18 0.52198702461812341 1.7000020588965701 2.5140787056672038e-18 0.52052901054751133 1.725002124924 2.3329283928381576e-18 0.51904980598865269 1.7500021769862495 2.1491089571273606e-18 0.51754880638747147 1.7750022467495106 1.9626161964141317e-18 0.5160259774310485 1.8000023192107328 1.773393462605671e-18 0.51448085655260256 1.8250023767545402 1.5813779477376319e-18 0.51291293088618406 1.8500024852095796 1.3865837818195567e-18 0.51132231581207699 1.8750025592158885 1.1888690135049825e-18 0.5097078522095736 1.9000026586304333 9.8823053613704303e-19 0.50806951471089534 1.9250027614527676 7.8457921347277634e-19 0.50640657546338286 1.958338158692654 5.0892263559563549e-19 0.50415566868708572 1.9833361382086643 2.9747251477337214e-19 0.50242904741988048 2 1.5430549669256652e-19 0.50126000000000004
+ 0 4 0.012452510310546802 1 0.018677614857744929 1 0.024901893757601767 1 0.031125378346555464 1 0.037348095999604043 1 0.043570075113847251 1 0.049791339500195375 1 0.056011913149960867 1 0.062231815808235363 1 0.068451066858882623 1 0.074669685244485792 1 0.080887686651431687 1 0.08710508649930275 1 0.093321898035032894 1 0.099538132537115312 1 0.10575380112979828 1 0.11196891216324817 1 0.11818347489459106 1 0.12439749509416391 1 0.13061097866015836 1 0.13682393075929764 1 0.14303635595040423 1 0.14924825660435104 1 0.15545963458681322 1 0.16167049062719938 1 0.16788082595175882 1 0.17409064000736726 1 0.18029993217511717 1 0.18650870178585921 1 0.19271694686181326 1 0.19892466633475747 1 0.20513185742090337 1 0.21133851853776786 1 0.21754464800816323 1 0.22375024377726974 1 0.22995530530011266 1 0.23615983082387357 1 0.24236382118925689 1 0.24856727602526885 1 0.2547701979496666 1 0.26097259061276307 1 0.26717445922337818 1 0.27337581033797814 1 0.27957665321447084 1 0.28577700036420717 1 0.29197686716772447 1 0.29817627241382305 1 0.30437523915085124 1 0.31057379564635523 1 0.31677197539920571 1 0.32296981870659941 1 0.3291673733598543 1 0.33536469590555762 1 0.34156185297660818 1 0.3477589228080224 1 0.3539559974761281 1 0.36015318523435597 1 0.36635061342357461 1 0.37254843209516636 1 0.37874681826072149 1 0.38494598201014885 1 0.3911461739362776 1 0.39734769357731764 1 0.40355090353038436 1 0.40975624333451433 1 0.41596425703125028 1 0.42217562044401452 1 0.42839119284530658 1 0.43461208040781973 1 0.44083974809739779 1 0.44707618887467077 1 0.45332422063984379 1 0.45958800969994934 1 0.4658741366469345 1 0.47219396540652614 1 0.47857010393931909 1 0.48506103813163204 1 0.49221420845893199 1 0.5 1 0.50778579154106807 1 0.51493896186836796 1 0.52142989606068102 1 0.52780603459347386 1 0.53412586335306556 1 0.54041199030005072 1 0.54667577936015621 1 0.55292381112532929 1 0.55916025190260221 1 0.56538791959218027 1 0.57160880715469353 1 0.57782437955598553 1 0.58403574296874983 1 0.59024375666548579 1 0.59644909646961564 1 0.60265230642268242 1 0.60885382606372229 1 0.61505401798985115 1 0.62125318173927857 1 0.62745156790483381 1 0.63364938657642544 1 0.63984681476564409 1 0.64604400252387206 1 0.65224107719197777 1 0.65843814702339198 1 0.66463530409444249 1 0.67083262664014576 1 0.67703018129340053 1 0.68322802460079435 1 0.68942620435364477 1 0.69562476084914859 1 0.70182372758617684 1 0.70802313283227536 1 0.71422299963579272 1 0.72042334678552888 1 0.72662418966202169 1 0.73282554077662143 1 0.73902740938723666 1 0.74522980205033329 1 0.75143272397473082 1 0.75763617881074286 1 0.76384016917612618 1 0.77004469469988723 1 0.77624975622273007 1 0.78245535199183658 1 0.78866148146223181 1 0.79486814257909633 1 0.80107533366524242 1 0.8072830531381866 1 0.81349129821414046 1 0.81970006782488258 1 0.82590935999263237 1 0.83211917404824076 1 0.83832950937280026 1 0.84454036541318656 1 0.85075174339564885 1 0.85696364404959546 1 0.86317606924070223 1 0.86938902133984142 1 0.87560250490583569 1 0.88181652510540864 1 0.88803108783675144 1 0.8942461988702014 1 0.90046186746288448 1 0.90667810196496701 1 0.91289491350069707 1 0.91911231334856824 1 0.92533031475551419 1 0.9315489331411172 1 0.93776818419176455 1 0.9439880868500391 1 0.95020866049980468 1 0.95642992488615264 1 0.96265190400039602 1 0.9688746216534444 1 0.97509810624239812 1 0.98132238514225512 1 0.98754748968945316 1 1 4
+Triangulations 0
+
+TShapes 29
+Ve
+2e-07
+5.15079620502503e-18 -2.12442355226077e-34 0.5
+0 0
+
+0101101
+*
+Ed
+ 2e-07 1 1 0
+1 1 0 0 6.28318530717959
+2 1 2 0 0 6.28318530717959
+2 2 1 0 0 6.28318530717959
+4 C2 2 0 1 0
+0
+
+0101000
++29 0 -29 0 *
+Ve
+2e-07
+2 1.54305496692567e-19 0.50126
+0 0
+
+0101101
+*
+Ed
+ 1e-07 1 1 0
+1 2 0 0.5 1
+3 3 4C0 1 0 0.5 1
+0
+
+0101000
++29 0 -27 0 *
+Ed
+ 2e-07 1 1 0
+1 3 0 0 6.28318530717959
+2 5 1 0 0 6.28318530717959
+2 6 3 0 0 6.28318530717959
+4 C0 1 0 3 0
+0
+
+0101000
++27 0 -27 0 *
+Wi
+
+0101100
++28 0 +26 0 -25 0 -26 0 *
+Fa
+0 0 1 0
+
+0111000
++24 0 *
+Ve
+5.000001e-06
+0.00399980079999999 -8.77137581722074e-19 0.492837628103607
+0 0
+
+0101101
+*
+Ed
+ 5e-06 1 1 0
+1 4 0 0 1
+2 7 2 0 0 1
+2 8 4 1 0 1
+0
+
+0101000
++22 0 -22 0 *
+Ed
+ 1e-07 1 1 0
+1 5 0 0.49722879746868 0.5
+3 9 10C2 2 0 0.49722879746868 0.5
+0
+
+0101000
++22 0 -29 0 *
+Wi
+
+0101100
+-21 0 -20 0 -28 0 +20 0 *
+Fa
+0 1e-07 2 0
+
+0101000
++19 0 *
+Ve
+2e-07
+2 -1.54305496692567e-19 0.49874
+0 0
+
+0101101
+*
+Ed
+ 2e-07 1 1 0
+1 6 0 0 0.00252
+3 11 12CN 3 0 0 0.00252
+0
+
+0101000
++27 0 -17 0 *
+Ed
+ 2e-07 1 1 0
+1 7 0 0 6.28318530717959
+2 13 3 0 0 6.28318530717959
+2 14 5 0 0 6.28318530717959
+4 C0 3 0 5 0
+0
+
+0101000
++17 0 -17 0 *
+Wi
+
+0101100
++25 0 +16 0 -15 0 -16 0 *
+Fa
+0 2e-07 3 0
+
+0111000
++14 0 *
+Wi
+
+0101100
+-21 0 *
+Fa
+0 1e-07 4 1
+
+0101000
++12 0 *
+Ve
+5.000001e-06
+1.9960001992 -1.88627210317514e-19 0.49845974193989
+0 0
+
+0101101
+*
+Ed
+ 1e-07 1 1 0
+1 8 0 0 0.000996307026022182
+3 15 16C2 5 0 0 0.000996307026022182
+0
+
+0101000
++17 0 -10 0 *
+Ed
+ 5e-06 1 1 0
+1 9 0 0 1
+2 17 5 0 0 1
+2 18 4 3 0 1
+0
+
+0101000
++10 0 -10 0 *
+Wi
+
+0101100
+-9 0 +8 0 +9 0 +15 0 *
+Fa
+0 1e-07 5 0
+
+0101000
++7 0 *
+Wi
+
+0101100
+-8 0 *
+Fa
+0 1e-07 4 3
+
+0101000
++5 0 *
+Sh
+
+0101100
++23 0 +18 0 +13 0 -11 0 +6 0 +4 0 *
+So
+
+0100000
++3 0 *
+Co
+
+1100000
++2 0 *
+
++1 0
\ No newline at end of file
diff --git a/ceasiompy/CPACS2GMSH/tests/ToolInput/test_close_engine/config_engines.cfg b/ceasiompy/CPACS2GMSH/tests/ToolInput/test_close_engine/config_engines.cfg
new file mode 100644
index 000000000..ed9779381
--- /dev/null
+++ b/ceasiompy/CPACS2GMSH/tests/ToolInput/test_close_engine/config_engines.cfg
@@ -0,0 +1,3 @@
+SimpleEngine_DOUBLE_FLUX = 0
+SimpleEngine_fanCowl_INTAKE_X = 0.003999800800000002
+SimpleEngine_fanCowl_EXHAUST_X = 1.9960001992000003
diff --git a/ceasiompy/CPACS2GMSH/tests/test_advancemeshing.py b/ceasiompy/CPACS2GMSH/tests/test_advancemeshing.py
index f62d5775c..2baf49558 100644
--- a/ceasiompy/CPACS2GMSH/tests/test_advancemeshing.py
+++ b/ceasiompy/CPACS2GMSH/tests/test_advancemeshing.py
@@ -5,7 +5,6 @@
Test functions for 'ceasiompy/CPACS2GMSH/advancemeshing.py'
-Python version: >=3.8
| Author : Tony Govoni
| Creation: 2022-04-09
@@ -33,15 +32,11 @@
from ceasiompy.utils.commonpaths import CPACS_FILES_PATH
from cpacspy.cpacspy import CPACS
+
MODULE_DIR = Path(__file__).parent
CPACS_IN_PATH = Path(CPACS_FILES_PATH, "simple_sharp_airfoil.xml")
TEST_OUT_PATH = Path(MODULE_DIR, "ToolOutput")
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-
# =================================================================================================
# FUNCTIONS
# =================================================================================================
@@ -218,8 +213,7 @@ def test_refine_wing_section():
export_brep(cpacs, TEST_OUT_PATH)
generate_gmsh(
- cpacs=cpacs,
- cpacs_path=CPACS_IN_PATH,
+ tixi=cpacs.tixi,
brep_dir=TEST_OUT_PATH,
results_dir=TEST_OUT_PATH,
open_gmsh=False,
@@ -281,8 +275,7 @@ def test_auto_refine():
export_brep(cpacs, TEST_OUT_PATH)
generate_gmsh(
- cpacs=cpacs,
- cpacs_path=CPACS_IN_PATH,
+ tixi=cpacs.tixi,
brep_dir=TEST_OUT_PATH,
results_dir=TEST_OUT_PATH,
open_gmsh=False,
diff --git a/ceasiompy/CPACS2GMSH/tests/test_engineconversion.py b/ceasiompy/CPACS2GMSH/tests/test_engineconversion.py
index f0cd2b992..8c4607974 100644
--- a/ceasiompy/CPACS2GMSH/tests/test_engineconversion.py
+++ b/ceasiompy/CPACS2GMSH/tests/test_engineconversion.py
@@ -5,7 +5,6 @@
Test functions for 'ceasiompy/CPACS2GMSH/engineconversion.py'
-Python version: >=3.8
| Author : Tony Govoni
| Creation: 2022-05-17
@@ -30,11 +29,6 @@
TEST_IN_PATH = Path(MODULE_DIR, "ToolInput")
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-
# =================================================================================================
# FUNCTIONS
# =================================================================================================
diff --git a/ceasiompy/CPACS2GMSH/tests/test_exportbrep.py b/ceasiompy/CPACS2GMSH/tests/test_exportbrep.py
index 86bf648ef..871481615 100644
--- a/ceasiompy/CPACS2GMSH/tests/test_exportbrep.py
+++ b/ceasiompy/CPACS2GMSH/tests/test_exportbrep.py
@@ -5,7 +5,6 @@
Test functions for 'ceasiompy/CPACS2GMSH/exportbrep.py'
-Python version: >=3.8
| Author : Tony Govoni
| Creation: 2022-03-22
diff --git a/ceasiompy/CPACS2GMSH/tests/test_generatemesh.py b/ceasiompy/CPACS2GMSH/tests/test_generatemesh.py
index 9bd034bce..59f4a1ca7 100644
--- a/ceasiompy/CPACS2GMSH/tests/test_generatemesh.py
+++ b/ceasiompy/CPACS2GMSH/tests/test_generatemesh.py
@@ -5,7 +5,6 @@
Test functions for 'ceasiompy/CPACS2GMSH/generategmesh.py'
-Python version: >=3.8
| Author : Tony Govoni
| Creation: 2022-03-22
@@ -24,9 +23,9 @@
from ceasiompy.CPACS2GMSH.func.generategmesh import (
ModelPart,
generate_gmsh,
- get_entities_from_volume,
)
-from ceasiompy.SU2Run.func.su2utils import get_mesh_markers
+from ceasiompy.CPACS2GMSH.func.wingclassification import get_entities_from_volume
+from ceasiompy.SU2Run.func.utils import get_mesh_markers
from ceasiompy.utils.ceasiompyutils import remove_file_type_in_dir
from ceasiompy.utils.commonpaths import CPACS_FILES_PATH
from cpacspy.cpacspy import CPACS
@@ -63,8 +62,7 @@ def test_generate_gmsh():
export_brep(cpacs, TEST_OUT_PATH)
generate_gmsh(
- cpacs=cpacs,
- cpacs_path=CPACS_IN_PATH,
+ tixi=cpacs.tixi,
brep_dir=TEST_OUT_PATH,
results_dir=TEST_OUT_PATH,
open_gmsh=False,
@@ -106,8 +104,7 @@ def test_generate_gmsh_symm():
export_brep(cpacs, TEST_OUT_PATH)
generate_gmsh(
- cpacs=cpacs,
- cpacs_path=CPACS_IN_PATH,
+ tixi=cpacs.tixi,
brep_dir=TEST_OUT_PATH,
results_dir=TEST_OUT_PATH,
open_gmsh=False,
@@ -199,65 +196,6 @@ def test_ModelPart_clean_inside_entities():
gmsh.finalize()
-def test_assignation():
- """
- Test if the assignation mechanism is correct on all parts
- test if the assignation of the entities of wing1 is correct
- """
-
- if TEST_OUT_PATH.exists():
- shutil.rmtree(TEST_OUT_PATH)
- TEST_OUT_PATH.mkdir()
-
- cpacs = CPACS(CPACS_IN_PATH)
-
- export_brep(cpacs, TEST_OUT_PATH)
-
- _, aircraft_parts = generate_gmsh(
- cpacs=cpacs,
- cpacs_path=CPACS_IN_PATH,
- brep_dir=TEST_OUT_PATH,
- results_dir=TEST_OUT_PATH,
- open_gmsh=False,
- farfield_factor=5,
- symmetry=False,
- farfield_size_factor=17,
- n_power_factor=2,
- n_power_field=0.9,
- fuselage_mesh_size_factor=1,
- wing_mesh_size_factor=1,
- mesh_size_engines=0.5,
- mesh_size_propellers=0.5,
- refine_factor=1.0,
- refine_truncated=False,
- auto_refine=False,
- testing_gmsh=False,
- )
-
- fuselage1_child = set([(3, 2)])
- wing1_m_child = set([(3, 5)])
-
- wing1_child = set([(3, 3)])
- wing1_volume_tag = [3]
- wing1_surfaces_tags = [5, 6, 7, 12, 13, 14, 19]
- wing1_lines_tags = [7, 8, 9, 15, 16, 17, 18, 19, 20, 29, 30, 31, 32, 33, 34]
- wing1_points_tags = [5, 6, 7, 12, 13, 14, 19, 20, 21]
-
- for part in aircraft_parts:
- if part.uid == "Wing_mirrored":
- assert part.children_dimtag == wing1_m_child
- if part.uid == "SimpleFuselage":
- assert part.children_dimtag == fuselage1_child
- if part.uid == "Wing":
- assert part.children_dimtag == wing1_child
- assert part.volume_tag == wing1_volume_tag
- assert part.surfaces_tags == wing1_surfaces_tags
- assert part.lines_tags == wing1_lines_tags
- assert part.points_tags == wing1_points_tags
-
- remove_file_type_in_dir(TEST_OUT_PATH, [".brep", ".su2", ".cfg"])
-
-
def test_define_engine_bc():
"""
Test if the engine bc are correctly assigned
@@ -273,8 +211,7 @@ def test_define_engine_bc():
export_brep(cpacs, TEST_OUT_PATH)
generate_gmsh(
- cpacs=cpacs,
- cpacs_path=CPACS_IN_SIMPLE_ENGINE_PATH,
+ tixi=cpacs.tixi,
brep_dir=TEST_OUT_PATH,
results_dir=TEST_OUT_PATH,
open_gmsh=False,
@@ -338,8 +275,7 @@ def test_define_doubleflux_engine_bc():
export_brep(cpacs, TEST_OUT_PATH)
generate_gmsh(
- cpacs=cpacs,
- cpacs_path=CPACS_IN_SIMPLE_ENGINE_PATH,
+ tixi=cpacs.tixi,
brep_dir=TEST_OUT_PATH,
results_dir=TEST_OUT_PATH,
open_gmsh=False,
@@ -399,8 +335,7 @@ def test_disk_actuator_conversion():
export_brep(cpacs, TEST_OUT_PATH)
generate_gmsh(
- cpacs=cpacs,
- cpacs_path=CPACS_IN_PROPELLER_ENGINE_PATH,
+ tixi=cpacs.tixi,
brep_dir=TEST_OUT_PATH,
results_dir=TEST_OUT_PATH,
open_gmsh=False,
@@ -419,7 +354,8 @@ def test_disk_actuator_conversion():
testing_gmsh=True,
)
- physical_groups = gmsh.model.getPhysicalGroups(dim=-1)
+ physical_groups = gmsh.model.getPhysicalGroups()
+ print(physical_groups)
# Check if the disk actuator integration was correct
assert len(physical_groups) == 11
@@ -441,6 +377,7 @@ def test_disk_actuator_conversion():
# =================================================================================================
if __name__ == "__main__":
+ test_disk_actuator_conversion()
print("Test CPACS2GMSH")
print("To run test use the following command:")
print(">> pytest -v")
diff --git a/ceasiompy/CPACS2GMSH/tests/test_mesh_sizing.py b/ceasiompy/CPACS2GMSH/tests/test_mesh_sizing.py
index a243f1c59..e922b1c16 100644
--- a/ceasiompy/CPACS2GMSH/tests/test_mesh_sizing.py
+++ b/ceasiompy/CPACS2GMSH/tests/test_mesh_sizing.py
@@ -5,7 +5,6 @@
Test functions for 'ceasiompy/CPACS2GMSH/mesh_sizing.py'
-Python version: >=3.8
| Author : Giacomo Benedetti
| Creation: 2023-11-23
@@ -17,7 +16,7 @@
# =================================================================================================
from pathlib import Path
-
+from cpacspy.cpacspy import CPACS
from pytest import approx
from ceasiompy.CPACS2GMSH.func.mesh_sizing import fuselage_size, wings_size
from ceasiompy.utils.commonpaths import CPACS_FILES_PATH
@@ -40,7 +39,7 @@ def test_fuselage_size():
on which the mesh size is calculated
"""
- fuselage_maxlen, fuselage_minlen = fuselage_size(CPACS_IN_PATH)
+ fuselage_maxlen, fuselage_minlen = fuselage_size(CPACS(CPACS_IN_PATH).tixi)
assert fuselage_maxlen == approx(0.5, abs=1e-2)
assert fuselage_minlen == approx(0.05, abs=1e-3)
@@ -52,7 +51,17 @@ def test_wing_size():
on which the mesh size is calculated
"""
- wing_maxlen, wing_minlen = wings_size(CPACS_IN_PATH)
+ wing_maxlen, wing_minlen = wings_size(CPACS(CPACS_IN_PATH).tixi)
assert wing_maxlen == approx(0.15, abs=1e-2)
assert wing_minlen == approx(0.012, abs=1e-4)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ print("Test CPACS2GMSH")
+ print("To run test use the following command:")
+ print(">> pytest -v")
diff --git a/ceasiompy/CPACS2GMSH/tests/test_wingclassification.py b/ceasiompy/CPACS2GMSH/tests/test_wingclassification.py
index 4b9f13778..c51773c7b 100644
--- a/ceasiompy/CPACS2GMSH/tests/test_wingclassification.py
+++ b/ceasiompy/CPACS2GMSH/tests/test_wingclassification.py
@@ -5,7 +5,6 @@
Test functions for 'ceasiompy/CPACS2GMSH/wingclassification.py'
-Python version: >=3.8
| Author : Tony Govoni
| Creation: 2022-04-19
@@ -16,29 +15,19 @@
# IMPORTS
# =================================================================================================
-import shutil
from pathlib import Path
import gmsh
-import pytest
-from ceasiompy.CPACS2GMSH.func.exportbrep import export_brep
-from ceasiompy.CPACS2GMSH.func.generategmesh import generate_gmsh
from ceasiompy.CPACS2GMSH.func.wingclassification import (
detect_normal_profile,
detect_truncated_profile,
)
-from ceasiompy.utils.ceasiompyutils import remove_file_type_in_dir
from ceasiompy.utils.commonpaths import CPACS_FILES_PATH
-from cpacspy.cpacspy import CPACS
MODULE_DIR = Path(__file__).parent
CPACS_IN_PATH = Path(CPACS_FILES_PATH, "simpletest_cpacs.xml")
TEST_OUT_PATH = Path(MODULE_DIR, "ToolOutput")
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
# =================================================================================================
# FUNCTIONS
@@ -215,58 +204,6 @@ def test_detect_truncated_profile():
gmsh.finalize()
-def test_classify_wing():
- """
- Test if one of the wing of the simple test model is correctly classified
-
- This test import the whole simple.xml file and check if one of its wing is correctly
- classified
- """
-
- if TEST_OUT_PATH.exists():
- shutil.rmtree(TEST_OUT_PATH)
- TEST_OUT_PATH.mkdir()
-
- cpacs = CPACS(CPACS_IN_PATH)
-
- export_brep(cpacs, TEST_OUT_PATH)
-
- _, aircraft_parts = generate_gmsh(
- cpacs=cpacs,
- cpacs_path=CPACS_IN_PATH,
- brep_dir=TEST_OUT_PATH,
- results_dir=TEST_OUT_PATH,
- open_gmsh=False,
- farfield_factor=2,
- symmetry=False,
- farfield_size_factor=17,
- n_power_factor=2,
- n_power_field=0.9,
- fuselage_mesh_size_factor=1,
- wing_mesh_size_factor=1,
- mesh_size_engines=0.2,
- mesh_size_propellers=0.2,
- refine_factor=1.0,
- refine_truncated=False,
- auto_refine=False,
- testing_gmsh=False,
- )
-
- for part in aircraft_parts:
- if "Wing_mirrored" == part.uid:
- test_wingsection = part.wing_sections
-
- # Test if the wing1_m is correctly classified
- assert len(test_wingsection) == 2
-
- # Test if the wing1_m section 1 is correctly classified
- section1 = test_wingsection[0]
- assert section1["lines_tags"] == [21, 23, 25]
- assert pytest.approx(section1["mean_chord"], 0.01) == 1
-
- remove_file_type_in_dir(TEST_OUT_PATH, [".brep", ".su2", ".cfg"])
-
-
# =================================================================================================
# MAIN
# =================================================================================================
diff --git a/ceasiompy/CPACS2SUMO/README.md b/ceasiompy/CPACS2SUMO/README.md
index 80bf44297..bd1df4add 100644
--- a/ceasiompy/CPACS2SUMO/README.md
+++ b/ceasiompy/CPACS2SUMO/README.md
@@ -30,7 +30,7 @@
Other differences may appear:
-:warning: Even if the definition of SUMO is close to CPACS, they differ in some points, thus for complex geometries, the SUMO geometry obtained may not be exact.
+:warning: Even if the definition of SUMO is close to CPACS, they differ in some points, thus for complex geometries, the SUMO geometry obtained may not be exact.
:warning: The interpolation of surfaces between existing sections may differ.
@@ -44,4 +44,4 @@ Other differences may appear:
## References
-[1] M. Alder, E. Moerland, J. Jepsen and B. Nagel. Recent Advances in Establishing a Common Language for Aircraft Design with CPACS. Aerospace Europe Conference 2020, Bordeaux, France, 2020.
+[1] M. Alder, E. Moerland, J. Jepsen and B. Nagel. Recent Advances in Establishing a Common Language for Aircraft Design with CPACS. Aerospace Europe Conference 2020, Bordeaux, France, 2020.
diff --git a/ceasiompy/CPACS2SUMO/ToolInput/.directory b/ceasiompy/CPACS2SUMO/ToolInput/.directory
deleted file mode 100644
index f7d16b74a..000000000
--- a/ceasiompy/CPACS2SUMO/ToolInput/.directory
+++ /dev/null
@@ -1,3 +0,0 @@
-[Dolphin]
-Timestamp=2018,10,30,11,47,16
-Version=3
diff --git a/ceasiompy/CPACS2SUMO/ToolInput/.keep b/ceasiompy/CPACS2SUMO/ToolInput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/CPACS2SUMO/ToolInput/.keep
+++ b/ceasiompy/CPACS2SUMO/ToolInput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/CPACS2SUMO/ToolOutput/.keep b/ceasiompy/CPACS2SUMO/ToolOutput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/CPACS2SUMO/ToolOutput/.keep
+++ b/ceasiompy/CPACS2SUMO/ToolOutput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/CPACS2SUMO/__init__.py b/ceasiompy/CPACS2SUMO/__init__.py
index e69de29bb..179db01b2 100644
--- a/ceasiompy/CPACS2SUMO/__init__.py
+++ b/ceasiompy/CPACS2SUMO/__init__.py
@@ -0,0 +1,45 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for CPACS2SUMO module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+from ceasiompy import log
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = True
+
+# ===== Include GUI =====
+include_gui = True
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+# ===== Add a Results Directory =====
+RES_DIR = True
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/CPACS2SUMO/__specs__.py b/ceasiompy/CPACS2SUMO/__specs__.py
index 1f6979187..6d20af65d 100644
--- a/ceasiompy/CPACS2SUMO/__specs__.py
+++ b/ceasiompy/CPACS2SUMO/__specs__.py
@@ -1,38 +1,64 @@
-from pathlib import Path
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
-from ceasiompy.utils.commonxpath import (
- SUMO_INCLUDE_ENGINE_XPATH,
- SUMO_INCLUDE_PYLON_XPATH,
- SUMOFILE_XPATH,
-)
-from ceasiompy.utils.moduleinterfaces import CPACSInOut
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-# ===== Module Status =====
-# True if the module is active
-# False if the module is disabled (not working or not ready)
-module_status = True
+GUI Interface of CPACS2SUMO.
-# ===== Results directory path =====
-RESULTS_DIR = Path("Results", "SUMO")
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+"""
-# ===== CPACS inputs and outputs =====
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from ceasiompy.utils.moduleinterfaces import CPACSInOut
+
+from ceasiompy import log
+from ceasiompy.CPACSUpdater import include_gui
+
+from ceasiompy.utils.commonxpath import (
+ SUMOFILE_XPATH,
+ SUMO_INCLUDE_PYLON_XPATH,
+ SUMO_INCLUDE_ENGINE_XPATH,
+ CPACS2SUMO_SUMO_GUI_XPATH,
+)
+
+# ==============================================================================
+# VARIABLE
+# ==============================================================================
cpacs_inout = CPACSInOut()
-# ----- Input -----
+# ==============================================================================
+# GUI INPUTS
+# ==============================================================================
+
+cpacs_inout.add_input(
+ var_name="open_sumo_gui",
+ var_type=bool,
+ default_value=False,
+ unit=None,
+ descr="True -> Opens SUMO at the end of CPACS2SUMO module",
+ xpath=CPACS2SUMO_SUMO_GUI_XPATH,
+ gui=include_gui,
+ gui_name="Open SUMO's GUI",
+ gui_group="SUMO GUI Setting",
+)
cpacs_inout.add_input(
var_name="include_engine",
var_type=bool,
default_value=False,
- unit="1",
+ unit=None,
descr="Convert engine from CEASIOMpy",
xpath=SUMO_INCLUDE_ENGINE_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Include engine",
- gui_group="Option",
+ gui_group="Mesh Settings",
)
cpacs_inout.add_input(
@@ -42,13 +68,14 @@
unit="1",
descr="Convert engine from CEASIOMpy",
xpath=SUMO_INCLUDE_PYLON_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Include pylon",
- gui_group="Option",
+ gui_group="Mesh Settings",
)
-
-# ----- Output -----
+# ==============================================================================
+# GUI OUTPUTS
+# ==============================================================================
cpacs_inout.add_output(
var_name="sumo_file_path",
@@ -57,3 +84,10 @@
descr="Path to the SUMO file generated by CPACS2SUMO",
xpath=SUMOFILE_XPATH,
)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/CPACS2SUMO/cpacs2sumo.py b/ceasiompy/CPACS2SUMO/cpacs2sumo.py
index dbca904c1..912c130bd 100644
--- a/ceasiompy/CPACS2SUMO/cpacs2sumo.py
+++ b/ceasiompy/CPACS2SUMO/cpacs2sumo.py
@@ -5,13 +5,11 @@
Script to convert CPACS file geometry into SUMO geometry
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2017-03-03
TODO:
-
* Write some documentation and tutorial
* Improve testing script
* Use both for wing and fuselage, as they define which
@@ -26,81 +24,237 @@
# ==============================================================================
import math
-from pathlib import Path
+import subprocess
-from ceasiompy.CPACS2SUMO.func.engineclasses import Engine
+import numpy as np
+
+from ceasiompy.utils.mathsfunctions import euler2fix
+from ceasiompy.utils.ceasiompyutils import call_main, bool_
from ceasiompy.CPACS2SUMO.func.getprofile import get_profile_coord
+
+from cpacspy.cpacsfunctions import (
+ get_value,
+ open_tixi,
+ create_branch,
+ get_value_or_default,
+)
+from ceasiompy.utils.geometryfunctions import (
+ check_if_rotated,
+ elements_number,
+ get_positionings,
+ convert_fuselage_profiles,
+ corrects_airfoil_profile,
+)
from ceasiompy.CPACS2SUMO.func.sumofunctions import (
add_wing_cap,
+ sumo_str_format,
+ sumo_mirror_copy,
sumo_add_engine_bc,
sumo_add_nacelle_lip,
- sumo_mirror_copy,
- sumo_str_format,
)
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.ceasiompyutils import get_results_directory
+
+from pathlib import Path
+from cpacspy.cpacspy import CPACS
+from tixi3.tixi3wrapper import Tixi3
+from ceasiompy.CPACS2SUMO.func.engineclasses import Engine
+
+from ceasiompy.utils.generalclasses import (
+ Transformation,
+ Point,
+)
+
+from typing import (
+ List,
+ Tuple,
+)
+
+from ceasiompy import log
+
from ceasiompy.utils.commonxpath import (
+ WINGS_XPATH,
+ PYLONS_XPATH,
ENGINES_XPATH,
+ SUMOFILE_XPATH,
FUSELAGES_XPATH,
- PYLONS_XPATH,
- SUMO_INCLUDE_ENGINE_XPATH,
SUMO_INCLUDE_PYLON_XPATH,
- SUMOFILE_XPATH,
- WINGS_XPATH,
+ SUMO_INCLUDE_ENGINE_XPATH,
+ CPACS2SUMO_SUMO_GUI_XPATH,
)
-from ceasiompy.utils.generalclasses import SimpleNamespace, Transformation
-from ceasiompy.utils.mathfunctions import euler2fix
-from ceasiompy.utils.moduleinterfaces import get_toolinput_file_path, get_tooloutput_file_path
-from cpacspy.cpacsfunctions import create_branch, get_value_or_default, open_tixi
-
-log = get_logger()
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
+from ceasiompy.CPACS2SUMO import MODULE_NAME, MODULE_DIR
# =================================================================================================
-# CLASSES
+# FUNCTIONS
# =================================================================================================
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
+def normalize_profile(tixi: Tixi3, prof_uid: str) -> Tuple[List[float], List[float]]:
+ _, prof_vect_y, prof_vect_z = get_profile_coord(tixi, prof_uid)
+ prof_size_y = (max(prof_vect_y) - min(prof_vect_y)) / 2
+ prof_size_z = (max(prof_vect_z) - min(prof_vect_z)) / 2
+
+ prof_vect_y = [(y / prof_size_y) - 1 for y in prof_vect_y]
+ prof_vect_z = [(z / prof_size_z) - 1 for z in prof_vect_z]
+
+ return prof_vect_y, prof_vect_z, prof_size_y, prof_size_z
+
+
+def calculate_body_frame_center(
+ elem_transf, sec_transf, fus_transf,
+ pos_x_list, pos_y_list, pos_z_list, i_sec
+) -> Tuple[float, float, float]:
+ body_frm_center_x = (
+ elem_transf.translation.x + sec_transf.translation.x + pos_x_list[i_sec]
+ ) * fus_transf.scaling.x
+ body_frm_center_y = (
+ elem_transf.translation.y * sec_transf.scaling.y + sec_transf.translation.y
+ + pos_y_list[i_sec]
+ ) * fus_transf.scaling.y
+ body_frm_center_z = (
+ elem_transf.translation.z * sec_transf.scaling.z + sec_transf.translation.z
+ + pos_z_list[i_sec]
+ ) * fus_transf.scaling.z
+
+ return body_frm_center_x, body_frm_center_y, body_frm_center_z
+
+
+def deal_with_elements(
+ tixi: Tixi3, sumo,
+ fus_xpath, body_xpath,
+ i_sec,
+ pos_x_list, pos_y_list, pos_z_list,
+ fus_transf: Transformation,
+) -> None:
+ sec_xpath = fus_xpath + "/sections/section[" + str(i_sec + 1) + "]"
+ sec_uid = tixi.getTextAttribute(sec_xpath, "uID")
+
+ sec_transf = Transformation()
+ sec_transf.get_cpacs_transf(tixi, sec_xpath)
+
+ check_if_rotated(sec_transf.rotation, sec_uid)
+
+ # Elements
+ elem_cnt = elements_number(tixi, sec_xpath + "/elements", "element", logg=False)
+
+ for i_elem in range(elem_cnt):
+ (
+ elem_transf,
+ prof_size_y, prof_size_z,
+ prof_vect_y, prof_vect_z,
+ ) = convert_fuselage_profiles(
+ tixi, sec_xpath, i_sec, i_elem, pos_y_list, pos_z_list
+ )
+ body_frm_center_x, body_frm_center_y, body_frm_center_z = calculate_body_frame_center(
+ elem_transf, sec_transf, fus_transf,
+ pos_x_list, pos_y_list, pos_z_list, i_sec
+ )
-def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
- """Function to convert a CPACS file geometry into a SUMO file geometry.
+ body_frm_height = (
+ prof_size_z
+ * 2
+ * elem_transf.scaling.z
+ * sec_transf.scaling.z
+ * fus_transf.scaling.z
+ )
- Function 'convert_cpacs_to_sumo' open an input cpacs file with TIXI handle
- and via two main loop, one for fuselage(s), one for wing(s) it convert
- every element (as much as possible) in the SUMO (.smx) format, which is
- also an xml file. Due to some differences between both format, some CPACS
- definition could lead to issues. The output sumo file is saved in the
- folder /ToolOutput
+ body_frm_height = max(body_frm_height, 0.005)
+ body_frm_width = (
+ prof_size_y
+ * 2
+ * elem_transf.scaling.y
+ * sec_transf.scaling.y
+ * fus_transf.scaling.y
+ )
+ body_frm_width = max(body_frm_width, 0.005)
+
+ # Convert the profile points in the SMX format
+ prof_str = ""
+ teta_list, teta_half = [], []
+ prof_vect_y_half, prof_vect_z_half = [], []
+ check_max, check_min = 0, 0
+
+ # Use polar angle to keep point in the correct order
+ for i, item in enumerate(prof_vect_y):
+ teta_list.append(math.atan2(prof_vect_z[i], item))
+
+ for t, teta in enumerate(teta_list):
+ HALF_PI = math.pi / 2
+ EPSILON = 0.04
+
+ if abs(teta) <= HALF_PI - EPSILON:
+ teta_half.append(teta)
+ prof_vect_y_half.append(prof_vect_y[t])
+ prof_vect_z_half.append(prof_vect_z[t])
+ elif abs(teta) < HALF_PI + EPSILON:
+ # Check if not the last element of the list
+ if not t == len(teta_list) - 1:
+ next_val = prof_vect_z[t + 1]
+ # Check if it is better to keep next point
+ if not abs(next_val) > abs(prof_vect_z[t]):
+ if prof_vect_z[t] > 0 and not check_max:
+ teta_half.append(teta)
+ # Force y=0, to get symmetrical profile
+ prof_vect_y_half.append(0)
+ prof_vect_z_half.append(prof_vect_z[t])
+ check_max = 1
+ elif prof_vect_z[t] < 0 and not check_min:
+ teta_half.append(teta)
+ # Force y=0, to get symmetrical profile
+ prof_vect_y_half.append(0)
+ prof_vect_z_half.append(prof_vect_z[t])
+ check_min = 1
+
+ # Sort points by teta value, to fit the SUMO profile format
+ teta_half, prof_vect_z_half, prof_vect_y_half = (
+ list(t)
+ for t in zip(*sorted(zip(teta_half, prof_vect_z_half, prof_vect_y_half)))
+ )
- Source:
- * CPACS documentation: https://www.cpacs.de/pages/documentation.html
+ # Write profile as a string and add y=0 point at the beginning
+ # and at the end to ensure symmetry
+ if not check_min:
+ prof_str += str(0) + " " + str(prof_vect_z_half[0]) + " "
+ for i, _ in enumerate(prof_vect_z_half):
+ prof_str += (
+ str(round(prof_vect_y_half[i], 4))
+ + " "
+ + str(round(prof_vect_z_half[i], 4))
+ + " "
+ )
+ if not check_max:
+ prof_str += str(0) + " " + str(prof_vect_z_half[i]) + " "
+
+ # Write the SUMO file
+ sumo.addTextElementAtIndex(
+ body_xpath, "BodyFrame", prof_str, i_sec + 1)
+ frame_xpath = body_xpath + "/BodyFrame[" + str(i_sec + 1) + "]"
+
+ body_center_str = sumo_str_format(
+ body_frm_center_x,
+ body_frm_center_y,
+ body_frm_center_z,
+ )
- Args:
- cpacs_path (Path): Path to the CPACS file
- cpacs_out_path (Path): Path to the CPACS file
+ sumo.addTextAttribute(frame_xpath, "center", str(body_center_str))
+ sumo.addTextAttribute(frame_xpath, "height", str(body_frm_height))
+ sumo.addTextAttribute(frame_xpath, "width", str(body_frm_width))
+ sumo.addTextAttribute(frame_xpath, "name", str(sec_uid))
- Returns:
- sumo_output_path (str): Path to the SUMO file
+def convert_fuselages(tixi: Tixi3, sumo: Tixi3) -> None:
"""
+ Convert fuselage from CPACS to SUMO.
- tixi = open_tixi(cpacs_path)
- sumo = open_tixi(Path(MODULE_DIR, "files", "sumo_empty.smx"))
+ Args:
+ tixi (Tixi3): TIXI Handle of the CPACS file.
+ sumo (Tixi3): TIXI Handle of the SUMO file.
- # Fuselage(s) ---------------------------------------------------------------
+ """
- if tixi.checkElement(FUSELAGES_XPATH):
- fus_cnt = tixi.getNamedChildrenCount(FUSELAGES_XPATH, "fuselage")
- log.info(str(fus_cnt) + " fuselage has been found.")
- else:
- fus_cnt = 0
- log.warning("No fuselage has been found in this CPACS file!")
+ element = "fuselage"
+
+ fus_cnt = elements_number(tixi, FUSELAGES_XPATH, element)
for i_fus in range(fus_cnt):
fus_xpath = FUSELAGES_XPATH + "/fuselage[" + str(i_fus + 1) + "]"
@@ -122,277 +276,34 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
body_tansf.rotation = euler2fix(fus_transf.rotation)
# Add body rotation
- body_rot_str = (
- str(math.radians(body_tansf.rotation.x))
- + " "
- + str(math.radians(body_tansf.rotation.y))
- + " "
- + str(math.radians(body_tansf.rotation.z))
+ body_rot_str = sumo_str_format(
+ math.radians(body_tansf.rotation.x),
+ math.radians(body_tansf.rotation.y),
+ math.radians(body_tansf.rotation.z),
)
+
sumo.addTextAttribute(body_xpath, "rotation", body_rot_str)
# Add body origin
- body_ori_str = (
- str(body_tansf.translation.x)
- + " "
- + str(body_tansf.translation.y)
- + " "
- + str(body_tansf.translation.z)
+ body_ori_str = sumo_str_format(
+ body_tansf.translation.x,
+ body_tansf.translation.y,
+ body_tansf.translation.z,
)
- sumo.addTextAttribute(body_xpath, "origin", body_ori_str)
-
- # Positionings
- if tixi.checkElement(fus_xpath + "/positionings"):
- pos_cnt = tixi.getNamedChildrenCount(fus_xpath + "/positionings", "positioning")
- log.info(str(fus_cnt) + ' "Positioning" has been found : ')
-
- pos_x_list = []
- pos_y_list = []
- pos_z_list = []
- from_sec_list = []
- to_sec_list = []
-
- for i_pos in range(pos_cnt):
- pos_xpath = fus_xpath + "/positionings/positioning[" + str(i_pos + 1) + "]"
-
- length = tixi.getDoubleElement(pos_xpath + "/length")
- sweep_deg = tixi.getDoubleElement(pos_xpath + "/sweepAngle")
- sweep = math.radians(sweep_deg)
- dihedral_deg = tixi.getDoubleElement(pos_xpath + "/dihedralAngle")
- dihedral = math.radians(dihedral_deg)
-
- # Get the corresponding translation of each positioning
- pos_x_list.append(length * math.sin(sweep))
- pos_y_list.append(length * math.cos(dihedral) * math.cos(sweep))
- pos_z_list.append(length * math.sin(dihedral) * math.cos(sweep))
-
- # Get which section are connected by the positioning
- if tixi.checkElement(pos_xpath + "/fromSectionUID"):
- from_sec = tixi.getTextElement(pos_xpath + "/fromSectionUID")
- else:
- from_sec = ""
- from_sec_list.append(from_sec)
- if tixi.checkElement(pos_xpath + "/toSectionUID"):
- to_sec = tixi.getTextElement(pos_xpath + "/toSectionUID")
- else:
- to_sec = ""
- to_sec_list.append(to_sec)
-
- # Re-loop though the positioning to re-order them
- for j_pos in range(pos_cnt):
- if from_sec_list[j_pos] == "":
- prev_pos_x = 0
- prev_pos_y = 0
- prev_pos_z = 0
-
- elif from_sec_list[j_pos] == to_sec_list[j_pos - 1]:
- prev_pos_x = pos_x_list[j_pos - 1]
- prev_pos_y = pos_y_list[j_pos - 1]
- prev_pos_z = pos_z_list[j_pos - 1]
-
- else:
- index_prev = to_sec_list.index(from_sec_list[j_pos])
- prev_pos_x = pos_x_list[index_prev]
- prev_pos_y = pos_y_list[index_prev]
- prev_pos_z = pos_z_list[index_prev]
-
- pos_x_list[j_pos] += prev_pos_x
- pos_y_list[j_pos] += prev_pos_y
- pos_z_list[j_pos] += prev_pos_z
-
- else:
- log.warning('No "positionings" have been found!')
- pos_cnt = 0
-
- # Sections
- sec_cnt = tixi.getNamedChildrenCount(fus_xpath + "/sections", "section")
- log.info(" -" + str(sec_cnt) + " fuselage sections have been found")
+ sumo.addTextAttribute(body_xpath, "origin", body_ori_str)
- if pos_cnt == 0:
- pos_x_list = [0.0] * sec_cnt
- pos_y_list = [0.0] * sec_cnt
- pos_z_list = [0.0] * sec_cnt
+ sec_cnt, pos_x_list, pos_y_list, pos_z_list = get_positionings(
+ tixi, fus_xpath, element)
for i_sec in range(sec_cnt):
- sec_xpath = fus_xpath + "/sections/section[" + str(i_sec + 1) + "]"
- sec_uid = tixi.getTextAttribute(sec_xpath, "uID")
-
- sec_transf = Transformation()
- sec_transf.get_cpacs_transf(tixi, sec_xpath)
-
- if sec_transf.rotation.x or sec_transf.rotation.y or sec_transf.rotation.z:
- log.warning(
- f"Sections '{sec_uid}' is rotated, it is"
- "not possible to take that into account in SUMO !"
- )
-
- # Elements
- elem_cnt = tixi.getNamedChildrenCount(sec_xpath + "/elements", "element")
-
- if elem_cnt > 1:
- log.warning(
- "Sections "
- + sec_uid
- + " contains multiple \
- element, it could be an issue for the conversion \
- to SUMO!"
- )
-
- for i_elem in range(elem_cnt):
- elem_xpath = sec_xpath + "/elements/element[" + str(i_elem + 1) + "]"
- elem_uid = tixi.getTextAttribute(elem_xpath, "uID")
-
- elem_transf = Transformation()
- elem_transf.get_cpacs_transf(tixi, elem_xpath)
-
- if elem_transf.rotation.x or elem_transf.rotation.y or elem_transf.rotation.z:
- log.warning(
- f"Element '{elem_uid}' is rotated, it is"
- "not possible to take that into account in SUMO !"
- )
-
- # Fuselage profiles
- prof_uid = tixi.getTextElement(elem_xpath + "/profileUID")
- prof_vect_x, prof_vect_y, prof_vect_z = get_profile_coord(tixi, prof_uid)
-
- prof_size_y = (max(prof_vect_y) - min(prof_vect_y)) / 2
- prof_size_z = (max(prof_vect_z) - min(prof_vect_z)) / 2
-
- prof_vect_y[:] = [y / prof_size_y for y in prof_vect_y]
- prof_vect_z[:] = [z / prof_size_z for z in prof_vect_z]
-
- prof_min_y = min(prof_vect_y)
- prof_min_z = min(prof_vect_z)
-
- prof_vect_y[:] = [y - 1 - prof_min_y for y in prof_vect_y]
- prof_vect_z[:] = [z - 1 - prof_min_z for z in prof_vect_z]
-
- # Could be a problem if they are less positionings than sections
- # TODO: solve that!
- pos_y_list[i_sec] += ((1 + prof_min_y) * prof_size_y) * elem_transf.scaling.y
- pos_z_list[i_sec] += ((1 + prof_min_z) * prof_size_z) * elem_transf.scaling.z
-
- # #To Plot a particular section
- # import matplotlib.pyplot as plt
- # if i_sec==5:
- # plt.plot(prof_vect_z, prof_vect_y,'x')
- # plt.xlabel('y')
- # plt.ylabel('z')
- # plt.grid(True)
- # plt.show
-
- # Put value in SUMO format
- body_frm_center_x = (
- elem_transf.translation.x + sec_transf.translation.x + pos_x_list[i_sec]
- ) * fus_transf.scaling.x
- body_frm_center_y = (
- elem_transf.translation.y * sec_transf.scaling.y
- + sec_transf.translation.y
- + pos_y_list[i_sec]
- ) * fus_transf.scaling.y
- body_frm_center_z = (
- elem_transf.translation.z * sec_transf.scaling.z
- + sec_transf.translation.z
- + pos_z_list[i_sec]
- ) * fus_transf.scaling.z
-
- body_frm_height = (
- prof_size_z
- * 2
- * elem_transf.scaling.z
- * sec_transf.scaling.z
- * fus_transf.scaling.z
- )
-
- if body_frm_height < 0.005:
- body_frm_height = 0.005
- body_frm_width = (
- prof_size_y
- * 2
- * elem_transf.scaling.y
- * sec_transf.scaling.y
- * fus_transf.scaling.y
- )
- if body_frm_width < 0.005:
- body_frm_width = 0.005
-
- # Convert the profile points in the SMX format
- prof_str = ""
- teta_list = []
- teta_half = []
- prof_vect_y_half = []
- prof_vect_z_half = []
- check_max = 0
- check_min = 0
-
- # Use polar angle to keep point in the correct order
- for i, item in enumerate(prof_vect_y):
- teta_list.append(math.atan2(prof_vect_z[i], prof_vect_y[i]))
-
- for t, teta in enumerate(teta_list):
- HALF_PI = math.pi / 2
- EPSILON = 0.04
-
- if abs(teta) <= HALF_PI - EPSILON:
- teta_half.append(teta)
- prof_vect_y_half.append(prof_vect_y[t])
- prof_vect_z_half.append(prof_vect_z[t])
- elif abs(teta) < HALF_PI + EPSILON:
- # Check if not the last element of the list
- if not t == len(teta_list) - 1:
- next_val = prof_vect_z[t + 1]
- # Check if it is better to keep next point
- if not abs(next_val) > abs(prof_vect_z[t]):
- if prof_vect_z[t] > 0 and not check_max:
- teta_half.append(teta)
- # Force y=0, to get symmetrical profile
- prof_vect_y_half.append(0)
- prof_vect_z_half.append(prof_vect_z[t])
- check_max = 1
- elif prof_vect_z[t] < 0 and not check_min:
- teta_half.append(teta)
- # Force y=0, to get symmetrical profile
- prof_vect_y_half.append(0)
- prof_vect_z_half.append(prof_vect_z[t])
- check_min = 1
-
- # Sort points by teta value, to fit the SUMO profile format
- teta_half, prof_vect_z_half, prof_vect_y_half = (
- list(t)
- for t in zip(*sorted(zip(teta_half, prof_vect_z_half, prof_vect_y_half)))
- )
-
- # Write profile as a string and add y=0 point at the beginning
- # and at the end to ensure symmetry
- if not check_min:
- prof_str += str(0) + " " + str(prof_vect_z_half[0]) + " "
- for i, item in enumerate(prof_vect_z_half):
- prof_str += (
- str(round(prof_vect_y_half[i], 4))
- + " "
- + str(round(prof_vect_z_half[i], 4))
- + " "
- )
- if not check_max:
- prof_str += str(0) + " " + str(prof_vect_z_half[i]) + " "
-
- # Write the SUMO file
- sumo.addTextElementAtIndex(body_xpath, "BodyFrame", prof_str, i_sec + 1)
- frame_xpath = body_xpath + "/BodyFrame[" + str(i_sec + 1) + "]"
-
- body_center_str = (
- str(body_frm_center_x)
- + " "
- + str(body_frm_center_y)
- + " "
- + str(body_frm_center_z)
- )
-
- sumo.addTextAttribute(frame_xpath, "center", body_center_str)
- sumo.addTextAttribute(frame_xpath, "height", str(body_frm_height))
- sumo.addTextAttribute(frame_xpath, "width", str(body_frm_width))
- sumo.addTextAttribute(frame_xpath, "name", sec_uid)
+ deal_with_elements(
+ tixi, sumo,
+ fus_xpath, body_xpath,
+ i_sec,
+ pos_x_list, pos_y_list, pos_z_list,
+ fus_transf
+ )
# Fuselage symmetry (mirror copy)
if tixi.checkAttribute(fus_xpath, "symmetry"):
@@ -405,14 +316,20 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
else:
sumo.removeElement("/Assembly/BodySkeleton[" + str(fus_cnt + 1) + "]")
- # Wing(s) ------------------------------------------------------------------
- if tixi.checkElement(WINGS_XPATH):
- wing_cnt = tixi.getNamedChildrenCount(WINGS_XPATH, "wing")
- log.info(str(wing_cnt) + " wings has been found.")
- else:
- wing_cnt = 0
- log.warning("No wings has been found in this CPACS file!")
+def convert_wings(tixi: Tixi3, sumo: Tixi3) -> None:
+ """
+ Convert wings from CPACS to SUMO.
+
+ Args:
+ tixi (Tixi3): TIXI Handle of the CPACS file.
+ sumo (Tixi3): TIXI Handle of the SUMO file.
+
+ """
+
+ element = "wing"
+
+ wing_cnt = elements_number(tixi, WINGS_XPATH, element)
for i_wing in range(wing_cnt):
wing_xpath = WINGS_XPATH + "/wing[" + str(i_wing + 1) + "]"
@@ -432,112 +349,45 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
# Convert WingSkeleton rotation and add it to SUMO
wg_sk_tansf.rotation = euler2fix(wing_transf.rotation)
- wg_sk_rot_str = (
- str(math.radians(wg_sk_tansf.rotation.x))
- + " "
- + str(math.radians(wg_sk_tansf.rotation.y))
- + " "
- + str(math.radians(wg_sk_tansf.rotation.z))
+ wg_sk_rot_str = sumo_str_format(
+ math.radians(wg_sk_tansf.rotation.x),
+ math.radians(wg_sk_tansf.rotation.y),
+ math.radians(wg_sk_tansf.rotation.z),
)
sumo.addTextAttribute(wg_sk_xpath, "rotation", wg_sk_rot_str)
# Add WingSkeleton origin
wg_sk_tansf.translation = wing_transf.translation
- wg_sk_ori_str = (
- str(wg_sk_tansf.translation.x)
- + " "
- + str(wg_sk_tansf.translation.y)
- + " "
- + str(wg_sk_tansf.translation.z)
+ wg_sk_ori_str = sumo_str_format(
+ wg_sk_tansf.translation.x,
+ wg_sk_tansf.translation.y,
+ wg_sk_tansf.translation.z,
)
+
sumo.addTextAttribute(wg_sk_xpath, "origin", wg_sk_ori_str)
if tixi.checkAttribute(wing_xpath, "symmetry"):
if tixi.getTextAttribute(wing_xpath, "symmetry") == "x-z-plane":
- sumo.addTextAttribute(wg_sk_xpath, "flags", "autosym,detectwinglet")
+ sumo.addTextAttribute(
+ wg_sk_xpath, "flags", "autosym,detectwinglet")
else:
sumo.addTextAttribute(wg_sk_xpath, "flags", "detectwinglet")
- # Positionings
- if tixi.checkElement(wing_xpath + "/positionings"):
- pos_cnt = tixi.getNamedChildrenCount(wing_xpath + "/positionings", "positioning")
- log.info(str(wing_cnt) + ' "positioning" has been found : ')
-
- pos_x_list = []
- pos_y_list = []
- pos_z_list = []
- from_sec_list = []
- to_sec_list = []
-
- for i_pos in range(pos_cnt):
- pos_xpath = wing_xpath + "/positionings/positioning[" + str(i_pos + 1) + "]"
-
- length = tixi.getDoubleElement(pos_xpath + "/length")
- sweep_deg = tixi.getDoubleElement(pos_xpath + "/sweepAngle")
- sweep = math.radians(sweep_deg)
- dihedral_deg = tixi.getDoubleElement(pos_xpath + "/dihedralAngle")
- dihedral = math.radians(dihedral_deg)
-
- # Get the corresponding translation of each positioning
- pos_x_list.append(length * math.sin(sweep))
- pos_y_list.append(length * math.cos(dihedral) * math.cos(sweep))
- pos_z_list.append(length * math.sin(dihedral) * math.cos(sweep))
-
- # Get which section are connected by the positioning
- if tixi.checkElement(pos_xpath + "/fromSectionUID"):
- from_sec = tixi.getTextElement(pos_xpath + "/fromSectionUID")
- else:
- from_sec = ""
- from_sec_list.append(from_sec)
+ sec_cnt, pos_x_list, pos_y_list, pos_z_list = get_positionings(
+ tixi, wing_xpath, "wing")
- if tixi.checkElement(pos_xpath + "/toSectionUID"):
- to_sec = tixi.getTextElement(pos_xpath + "/toSectionUID")
- else:
- to_sec = ""
- to_sec_list.append(to_sec)
-
- # Re-loop though the positioning to re-order them
- for j_pos in range(pos_cnt):
- if from_sec_list[j_pos] == "":
- prev_pos_x = 0
- prev_pos_y = 0
- prev_pos_z = 0
- elif from_sec_list[j_pos] == to_sec_list[j_pos - 1]:
- prev_pos_x = pos_x_list[j_pos - 1]
- prev_pos_y = pos_y_list[j_pos - 1]
- prev_pos_z = pos_z_list[j_pos - 1]
- else:
- index_prev = to_sec_list.index(from_sec_list[j_pos])
- prev_pos_x = pos_x_list[index_prev]
- prev_pos_y = pos_y_list[index_prev]
- prev_pos_z = pos_z_list[index_prev]
-
- pos_x_list[j_pos] += prev_pos_x
- pos_y_list[j_pos] += prev_pos_y
- pos_z_list[j_pos] += prev_pos_z
-
- else:
- log.warning('No "positionings" have been found!')
- pos_cnt = 0
-
- # Sections
- sec_cnt = tixi.getNamedChildrenCount(wing_xpath + "/sections", "section")
- log.info(" -" + str(sec_cnt) + " wing sections have been found")
wing_sec_index = 1
- if pos_cnt == 0:
- pos_x_list = [0.0] * sec_cnt
- pos_y_list = [0.0] * sec_cnt
- pos_z_list = [0.0] * sec_cnt
-
for i_sec in reversed(range(sec_cnt)):
- sec_xpath = wing_xpath + "/sections/section[" + str(i_sec + 1) + "]"
+ sec_xpath = wing_xpath + \
+ "/sections/section[" + str(i_sec + 1) + "]"
sec_uid = tixi.getTextAttribute(sec_xpath, "uID")
sec_transf = Transformation()
sec_transf.get_cpacs_transf(tixi, sec_xpath)
# Elements
- elem_cnt = tixi.getNamedChildrenCount(sec_xpath + "/elements", "element")
+ elem_cnt = tixi.getNamedChildrenCount(
+ sec_xpath + "/elements", "element")
if elem_cnt > 1:
log.warning(
@@ -546,58 +396,39 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
)
for i_elem in range(elem_cnt):
- elem_xpath = sec_xpath + "/elements/element[" + str(i_elem + 1) + "]"
- elem_uid = tixi.getTextAttribute(elem_xpath, "uID")
+ elem_xpath = sec_xpath + \
+ "/elements/element[" + str(i_elem + 1) + "]"
elem_transf = Transformation()
elem_transf.get_cpacs_transf(tixi, elem_xpath)
# Get wing profile (airfoil)
prof_uid = tixi.getTextElement(elem_xpath + "/airfoilUID")
- prof_vect_x, prof_vect_y, prof_vect_z = get_profile_coord(tixi, prof_uid)
+ prof_vect_x, prof_vect_y, prof_vect_z = get_profile_coord(
+ tixi, prof_uid)
- # Apply scaling
- for i, item in enumerate(prof_vect_x):
- prof_vect_x[i] = (
- item * elem_transf.scaling.x * sec_transf.scaling.x * wing_transf.scaling.x
- )
- for i, item in enumerate(prof_vect_y):
- prof_vect_y[i] = (
- item * elem_transf.scaling.y * sec_transf.scaling.y * wing_transf.scaling.y
- )
- for i, item in enumerate(prof_vect_z):
- prof_vect_z[i] = (
- item * elem_transf.scaling.z * sec_transf.scaling.z * wing_transf.scaling.z
- )
+ # Convert lists to NumPy arrays
+ prof_vect_x = np.array(prof_vect_x)
+ prof_vect_y = np.array(prof_vect_y)
+ prof_vect_z = np.array(prof_vect_z)
- # Plot setions (for tests)
- # import matplotlib.pyplot as plt
- # if (i_sec>8 and i_sec<=10):
- # plt.plot(prof_vect_x, prof_vect_z,'x')
- # plt.xlabel('x')
- # plt.ylabel('z')
- # plt.grid(True)
- # plt.show()
-
- prof_size_x = max(prof_vect_x) - min(prof_vect_x)
- prof_size_y = max(prof_vect_y) - min(prof_vect_y)
- prof_size_z = max(prof_vect_z) - min(prof_vect_z)
-
- if prof_size_y == 0:
- prof_vect_x[:] = [x / prof_size_x for x in prof_vect_x]
- prof_vect_z[:] = [z / prof_size_x for z in prof_vect_z]
- # Is it correct to divide by prof_size_x ????
-
- wg_sec_chord = prof_size_x
- else:
- log.error("An airfoil profile is not define correctly")
+ # Apply scaling
+ prof_vect_x *= elem_transf.scaling.x * \
+ sec_transf.scaling.x * wing_transf.scaling.x
+ prof_vect_y *= elem_transf.scaling.y * \
+ sec_transf.scaling.y * wing_transf.scaling.y
+ prof_vect_z *= elem_transf.scaling.z * \
+ sec_transf.scaling.z * wing_transf.scaling.z
+
+ wg_sec_chord = corrects_airfoil_profile(
+ prof_vect_x, prof_vect_y, prof_vect_z
+ )
# SUMO variable for WingSection
wg_sec_center_x = (
elem_transf.translation.x + sec_transf.translation.x + pos_x_list[i_sec]
) * wing_transf.scaling.x
wg_sec_center_y = (
- elem_transf.translation.y * sec_transf.scaling.y
- + sec_transf.translation.y
+ elem_transf.translation.y * sec_transf.scaling.y + sec_transf.translation.y
+ pos_y_list[i_sec]
) * wing_transf.scaling.y
wg_sec_center_z = (
@@ -608,7 +439,7 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
# Add roation from element and sections
# Adding the two angles: Maybe not work in every case!!!
- add_rotation = SimpleNamespace()
+ add_rotation = Point()
add_rotation.x = elem_transf.rotation.x + sec_transf.rotation.x
add_rotation.y = elem_transf.rotation.y + sec_transf.rotation.y
add_rotation.z = elem_transf.rotation.z + sec_transf.rotation.z
@@ -637,16 +468,23 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
if dx_squared + dz_squared > 1e-8:
prof_str += f"{round(prof_vect_x[i], 4)} {round(prof_vect_z[i], 4)} "
- sumo.addTextElementAtIndex(wg_sk_xpath, "WingSection", prof_str, wing_sec_index)
- wg_sec_xpath = wg_sk_xpath + "/WingSection[" + str(wing_sec_index) + "]"
+ sumo.addTextElementAtIndex(
+ wg_sk_xpath, "WingSection", prof_str, wing_sec_index)
+ wg_sec_xpath = wg_sk_xpath + \
+ "/WingSection[" + str(wing_sec_index) + "]"
sumo.addTextAttribute(wg_sec_xpath, "airfoil", prof_uid)
sumo.addTextAttribute(wg_sec_xpath, "name", sec_uid)
- wg_sec_center_str = (
- str(wg_sec_center_x) + " " + str(wg_sec_center_y) + " " + str(wg_sec_center_z)
+ wg_sec_center_str = sumo_str_format(
+ wg_sec_center_x,
+ wg_sec_center_y,
+ wg_sec_center_z,
)
- sumo.addTextAttribute(wg_sec_xpath, "center", wg_sec_center_str)
+
+ sumo.addTextAttribute(
+ wg_sec_xpath, "center", wg_sec_center_str)
sumo.addTextAttribute(wg_sec_xpath, "chord", str(wg_sec_chord))
- sumo.addTextAttribute(wg_sec_xpath, "dihedral", str(wg_sec_dihed))
+ sumo.addTextAttribute(
+ wg_sec_xpath, "dihedral", str(wg_sec_dihed))
sumo.addTextAttribute(wg_sec_xpath, "twist", str(wg_sec_twist))
sumo.addTextAttribute(wg_sec_xpath, "yaw", str(wg_sec_yaw))
sumo.addTextAttribute(wg_sec_xpath, "napprox", "-1")
@@ -658,16 +496,24 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
# Add Wing caps
add_wing_cap(sumo, wg_sk_xpath)
- # Engine pylon(s) ---------------------------------------------------------
- include_pylon = get_value_or_default(tixi, SUMO_INCLUDE_PYLON_XPATH, False)
+def convert_enginepylons(tixi: Tixi3, sumo: Tixi3) -> None:
+ """
+ Convert engine pylon(s) from CPACS to SUMO.
- if tixi.checkElement(PYLONS_XPATH) and include_pylon:
- pylon_cnt = tixi.getNamedChildrenCount(PYLONS_XPATH, "enginePylon")
- log.info(str(pylon_cnt) + " pylons has been found.")
+ Args:
+ tixi (Tixi3): TIXI Handle of the CPACS file.
+ sumo (Tixi3): TIXI Handle of the SUMO file.
+
+ """
+
+ element = "enginePylon"
+
+ include_pylon = get_value_or_default(tixi, SUMO_INCLUDE_PYLON_XPATH, False)
+ if include_pylon:
+ pylon_cnt = elements_number(tixi, PYLONS_XPATH, element)
else:
pylon_cnt = 0
- log.warning("No pylon has been found in this CPACS file!")
for i_pylon in range(pylon_cnt):
pylon_xpath = PYLONS_XPATH + "/enginePylon[" + str(i_pylon + 1) + "]"
@@ -702,94 +548,31 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
wg_sk_xpath,
"origin",
sumo_str_format(
- wg_sk_tansf.translation.x, wg_sk_tansf.translation.y, wg_sk_tansf.translation.z
+ wg_sk_tansf.translation.x,
+ wg_sk_tansf.translation.y,
+ wg_sk_tansf.translation.z,
),
)
sumo.addTextAttribute(wg_sk_xpath, "flags", "detectwinglet")
- # Positionings
- if tixi.checkElement(pylon_xpath + "/positionings"):
- pos_cnt = tixi.getNamedChildrenCount(pylon_xpath + "/positionings", "positioning")
- log.info(str(pylon_cnt) + ' "positioning" has been found : ')
-
- pos_x_list = []
- pos_y_list = []
- pos_z_list = []
- from_sec_list = []
- to_sec_list = []
-
- for i_pos in range(pos_cnt):
- pos_xpath = pylon_xpath + "/positionings/positioning[" + str(i_pos + 1) + "]"
-
- length = tixi.getDoubleElement(pos_xpath + "/length")
- sweep_deg = tixi.getDoubleElement(pos_xpath + "/sweepAngle")
- sweep = math.radians(sweep_deg)
- dihedral_deg = tixi.getDoubleElement(pos_xpath + "/dihedralAngle")
- dihedral = math.radians(dihedral_deg)
-
- # Get the corresponding translation of each positioning
- pos_x_list.append(length * math.sin(sweep))
- pos_y_list.append(length * math.cos(dihedral) * math.cos(sweep))
- pos_z_list.append(length * math.sin(dihedral) * math.cos(sweep))
-
- # Get which section are connected by the positioning
- if tixi.checkElement(pos_xpath + "/fromSectionUID"):
- from_sec = tixi.getTextElement(pos_xpath + "/fromSectionUID")
- else:
- from_sec = ""
- from_sec_list.append(from_sec)
-
- if tixi.checkElement(pos_xpath + "/toSectionUID"):
- to_sec = tixi.getTextElement(pos_xpath + "/toSectionUID")
- else:
- to_sec = ""
- to_sec_list.append(to_sec)
-
- # Re-loop though the positioning to re-order them
- for j_pos in range(pos_cnt):
- if from_sec_list[j_pos] == "":
- prev_pos_x = 0
- prev_pos_y = 0
- prev_pos_z = 0
- elif from_sec_list[j_pos] == to_sec_list[j_pos - 1]:
- prev_pos_x = pos_x_list[j_pos - 1]
- prev_pos_y = pos_y_list[j_pos - 1]
- prev_pos_z = pos_z_list[j_pos - 1]
- else:
- index_prev = to_sec_list.index(from_sec_list[j_pos])
- prev_pos_x = pos_x_list[index_prev]
- prev_pos_y = pos_y_list[index_prev]
- prev_pos_z = pos_z_list[index_prev]
-
- pos_x_list[j_pos] += prev_pos_x
- pos_y_list[j_pos] += prev_pos_y
- pos_z_list[j_pos] += prev_pos_z
-
- else:
- log.warning('No "positionings" have been found!')
- pos_cnt = 0
-
- # Sections
- sec_cnt = tixi.getNamedChildrenCount(pylon_xpath + "/sections", "section")
- log.info(" -" + str(sec_cnt) + " wing sections have been found")
- wing_sec_index = 1
-
- if pos_cnt == 0:
- pos_x_list = [0.0] * sec_cnt
- pos_y_list = [0.0] * sec_cnt
- pos_z_list = [0.0] * sec_cnt
+ sec_cnt, pos_x_list, pos_y_list, pos_z_list = get_positionings(
+ tixi, pylon_xpath, "pylon")
check_reversed_wing = []
+ wing_sec_index = 1
+
for i_sec in range(sec_cnt):
# for i_sec in reversed(range(sec_cnt)):
- sec_xpath = pylon_xpath + "/sections/section[" + str(i_sec + 1) + "]"
+ sec_xpath = pylon_xpath + \
+ "/sections/section[" + str(i_sec + 1) + "]"
sec_uid = tixi.getTextAttribute(sec_xpath, "uID")
sec_transf = Transformation()
sec_transf.get_cpacs_transf(tixi, sec_xpath)
# Elements
- elem_cnt = tixi.getNamedChildrenCount(sec_xpath + "/elements", "element")
+ elem_cnt = tixi.getNamedChildrenCount(
+ sec_xpath + "/elements", "element")
if elem_cnt > 1:
log.warning(
@@ -801,50 +584,41 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
)
for i_elem in range(elem_cnt):
- elem_xpath = sec_xpath + "/elements/element[" + str(i_elem + 1) + "]"
- elem_uid = tixi.getTextAttribute(elem_xpath, "uID")
+ elem_xpath = sec_xpath + \
+ "/elements/element[" + str(i_elem + 1) + "]"
elem_transf = Transformation()
elem_transf.get_cpacs_transf(tixi, elem_xpath)
# Get pylon profile (airfoil)
prof_uid = tixi.getTextElement(elem_xpath + "/airfoilUID")
- prof_vect_x, prof_vect_y, prof_vect_z = get_profile_coord(tixi, prof_uid)
+ prof_vect_x, prof_vect_y, prof_vect_z = get_profile_coord(
+ tixi, prof_uid)
- # Apply scaling
- for i, item in enumerate(prof_vect_x):
- prof_vect_x[i] = (
- item
- * elem_transf.scaling.x
- * sec_transf.scaling.x
- * pylon_transf.scaling.x
- )
- for i, item in enumerate(prof_vect_y):
- prof_vect_y[i] = (
- item
- * elem_transf.scaling.y
- * sec_transf.scaling.y
- * pylon_transf.scaling.y
- )
- for i, item in enumerate(prof_vect_z):
- prof_vect_z[i] = (
- item
- * elem_transf.scaling.z
- * sec_transf.scaling.z
- * pylon_transf.scaling.z
- )
-
- prof_size_x = max(prof_vect_x) - min(prof_vect_x)
- prof_size_y = max(prof_vect_y) - min(prof_vect_y)
- prof_size_z = max(prof_vect_z) - min(prof_vect_z)
+ # Convert lists to NumPy arrays
+ prof_vect_x = np.array(prof_vect_x)
+ prof_vect_y = np.array(prof_vect_y)
+ prof_vect_z = np.array(prof_vect_z)
- if prof_size_y == 0:
- prof_vect_x[:] = [x / prof_size_x for x in prof_vect_x]
- prof_vect_z[:] = [z / prof_size_x for z in prof_vect_z]
- # Is it correct to divide by prof_size_x ????
+ # Apply scaling
+ prof_vect_x *= (
+ elem_transf.scaling.x
+ * sec_transf.scaling.x
+ * pylon_transf.scaling.x
+ )
+ prof_vect_y *= (
+ elem_transf.scaling.y
+ * sec_transf.scaling.y
+ * pylon_transf.scaling.y
+ )
+ prof_vect_z *= (
+ elem_transf.scaling.z
+ * sec_transf.scaling.z
+ * pylon_transf.scaling.z
+ )
- wg_sec_chord = prof_size_x
- else:
- log.error("An airfoil profile is not define correctly")
+ wg_sec_chord = corrects_airfoil_profile(
+ prof_vect_x, prof_vect_y, prof_vect_z
+ )
# SUMO variable for WingSection
wg_sec_center_x = (
@@ -865,7 +639,7 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
# Add rotation from element and sections
# Adding the two angles: Maybe not work in every case!!!
- add_rotation = SimpleNamespace()
+ add_rotation = Point()
add_rotation.x = elem_transf.rotation.x + sec_transf.rotation.x
add_rotation.y = elem_transf.rotation.y + sec_transf.rotation.y
add_rotation.z = elem_transf.rotation.z + sec_transf.rotation.z
@@ -893,17 +667,21 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
if dx_squared + dz_squared > 1e-6:
prof_str += f"{round(prof_vect_x[i], 4)} {round(prof_vect_z[i], 4)} "
- sumo.addTextElementAtIndex(wg_sk_xpath, "WingSection", prof_str, wing_sec_index)
- wg_sec_xpath = wg_sk_xpath + "/WingSection[" + str(wing_sec_index) + "]"
+ sumo.addTextElementAtIndex(
+ wg_sk_xpath, "WingSection", prof_str, wing_sec_index)
+ wg_sec_xpath = wg_sk_xpath + \
+ "/WingSection[" + str(wing_sec_index) + "]"
sumo.addTextAttribute(wg_sec_xpath, "airfoil", prof_uid)
sumo.addTextAttribute(wg_sec_xpath, "name", sec_uid)
sumo.addTextAttribute(
wg_sec_xpath,
"center",
- sumo_str_format(wg_sec_center_x, wg_sec_center_y, wg_sec_center_z),
+ sumo_str_format(wg_sec_center_x,
+ wg_sec_center_y, wg_sec_center_z),
)
sumo.addTextAttribute(wg_sec_xpath, "chord", str(wg_sec_chord))
- sumo.addTextAttribute(wg_sec_xpath, "dihedral", str(wg_sec_dihed))
+ sumo.addTextAttribute(
+ wg_sec_xpath, "dihedral", str(wg_sec_dihed))
sumo.addTextAttribute(wg_sec_xpath, "twist", str(wg_sec_twist))
sumo.addTextAttribute(wg_sec_xpath, "yaw", str(wg_sec_yaw))
sumo.addTextAttribute(wg_sec_xpath, "napprox", "-1")
@@ -916,7 +694,8 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
if check_reversed_wing[0] < check_reversed_wing[1]:
log.info("Wing section order will be reversed.")
for i_sec in range(sec_cnt):
- wg_sec_xpath = wg_sk_xpath + "/WingSection[" + str(i_sec + 1) + "]"
+ wg_sec_xpath = wg_sk_xpath + \
+ "/WingSection[" + str(i_sec + 1) + "]"
sumo.removeAttribute(wg_sec_xpath, "reversed")
sumo.addTextAttribute(wg_sec_xpath, "reversed", "true")
@@ -927,16 +706,24 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
add_wing_cap(sumo, wg_sk_xpath)
- # Engine(s) ----------------------------------------------------------------
- include_engine = get_value_or_default(tixi, SUMO_INCLUDE_ENGINE_XPATH, False)
+def convert_engines(tixi: Tixi3, sumo: Tixi3) -> None:
+ """
+ Convert engine(s) from CPACS to SUMO.
+
+ Args:
+ tixi (Tixi3): TIXI Handle of the CPACS file.
+ sumo (Tixi3): TIXI Handle of the SUMO file.
+
+ """
+
+ include_engine = bool_(get_value_or_default(
+ tixi, SUMO_INCLUDE_ENGINE_XPATH, False))
- if tixi.checkElement(ENGINES_XPATH) and include_engine:
- engine_cnt = tixi.getNamedChildrenCount(ENGINES_XPATH, "engine")
- log.info(str(engine_cnt) + " engines has been found.")
+ if include_engine:
+ engine_cnt = elements_number(tixi, ENGINES_XPATH, "engine")
else:
engine_cnt = 0
- log.warning("No engine has been found in this CPACS file!")
for i_engine in range(engine_cnt):
engine_xpath = ENGINES_XPATH + "/engine[" + str(i_engine + 1) + "]"
@@ -948,7 +735,8 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
yengtransl = engine.transf.translation.y
zengtransl = engine.transf.translation.z
- engineparts = [engine.nacelle.fancowl, engine.nacelle.corecowl, engine.nacelle.centercowl]
+ engineparts = [engine.nacelle.fancowl,
+ engine.nacelle.corecowl, engine.nacelle.centercowl]
for engpart in engineparts:
if not engpart.isengpart:
@@ -1004,18 +792,21 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
# ax.grid()
# plt.show()
- sumo.createElementAtIndex("/Assembly", "BodySkeleton", i_fus + 1)
- body_xpath = "/Assembly/BodySkeleton[" + str(i_fus + 1) + "]"
+ sumo.createElementAtIndex(
+ "/Assembly", "BodySkeleton", i_engine + 1)
+ body_xpath = "/Assembly/BodySkeleton[" + str(i_engine + 1) + "]"
sumo.addTextAttribute(body_xpath, "akimatg", "false")
sumo.addTextAttribute(body_xpath, "name", engpart.uid)
# Add body rotation and origin
- sumo.addTextAttribute(body_xpath, "rotation", sumo_str_format(0, 0, 0))
+ sumo.addTextAttribute(body_xpath, "rotation",
+ sumo_str_format(0, 0, 0))
sumo.addTextAttribute(
body_xpath,
"origin",
- sumo_str_format(xengtransl + ysectransl, yengtransl, zengtransl),
+ sumo_str_format(xengtransl + ysectransl,
+ yengtransl, zengtransl),
)
# Add section
@@ -1023,7 +814,8 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
namesec = "section_" + str(i_sec + 1)
# Only circle profiles
prof_str = " 0 -1 0.7071 -0.7071 1 0 0.7071 0.7071 0 1"
- sumo.addTextElementAtIndex(body_xpath, "BodyFrame", prof_str, i_sec + 1)
+ sumo.addTextElementAtIndex(
+ body_xpath, "BodyFrame", prof_str, i_sec + 1)
frame_xpath = body_xpath + "/BodyFrame[" + str(i_sec + 1) + "]"
diam = (ycontours[i_sec] + zsectransl) * 2
@@ -1031,7 +823,8 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
diam = 0.005
sumo.addTextAttribute(
- frame_xpath, "center", sumo_str_format(xcontours[i_sec], 0, 0)
+ frame_xpath, "center", sumo_str_format(
+ xcontours[i_sec], 0, 0)
)
sumo.addTextAttribute(frame_xpath, "height", str(diam))
sumo.addTextAttribute(frame_xpath, "width", str(diam))
@@ -1044,18 +837,25 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
sumo_add_engine_bc(sumo, "Engine", engpart.uid)
if engine.sym:
- sumo.createElementAtIndex("/Assembly", "BodySkeleton", i_fus + 1)
- body_xpath = "/Assembly/BodySkeleton[" + str(i_fus + 1) + "]"
+ sumo.createElementAtIndex(
+ "/Assembly", "BodySkeleton", i_engine + 1)
+ body_xpath = "/Assembly/BodySkeleton[" + \
+ str(i_engine + 1) + "]"
sumo.addTextAttribute(body_xpath, "akimatg", "false")
sumo.addTextAttribute(body_xpath, "name", engpart.uid + "_sym")
# Add body rotation and origin
- sumo.addTextAttribute(body_xpath, "rotation", sumo_str_format(0, 0, 0))
+ sumo.addTextAttribute(
+ body_xpath, "rotation", sumo_str_format(0, 0, 0))
sumo.addTextAttribute(
body_xpath,
"origin",
- sumo_str_format(xengtransl + ysectransl, -yengtransl, zengtransl),
+ sumo_str_format(
+ xengtransl + ysectransl,
+ -yengtransl,
+ zengtransl
+ ),
)
# Add section
@@ -1063,15 +863,18 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
namesec = "section_" + str(i_sec + 1)
# Only circle profiles
prof_str = " 0 -1 0.7071 -0.7071 1 0 0.7071 0.7071 0 1"
- sumo.addTextElementAtIndex(body_xpath, "BodyFrame", prof_str, i_sec + 1)
- frame_xpath = body_xpath + "/BodyFrame[" + str(i_sec + 1) + "]"
+ sumo.addTextElementAtIndex(
+ body_xpath, "BodyFrame", prof_str, i_sec + 1)
+ frame_xpath = body_xpath + \
+ "/BodyFrame[" + str(i_sec + 1) + "]"
diam = (ycontours[i_sec] + zsectransl) * 2
if diam < 0.005:
diam = 0.005
sumo.addTextAttribute(
- frame_xpath, "center", sumo_str_format(xcontours[i_sec], 0, 0)
+ frame_xpath, "center", sumo_str_format(
+ xcontours[i_sec], 0, 0)
)
sumo.addTextAttribute(frame_xpath, "height", str(diam))
sumo.addTextAttribute(frame_xpath, "width", str(diam))
@@ -1081,35 +884,62 @@ def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
sumo_add_nacelle_lip(sumo, body_xpath)
if not engpart.iscone:
- sumo_add_engine_bc(sumo, "Engine_sym", engpart.uid + "_sym")
+ sumo_add_engine_bc(sumo, "Engine_sym",
+ engpart.uid + "_sym")
- # Get results directory
- results_dir = get_results_directory("CPACS2SUMO")
- sumo_file_path = Path(results_dir, "ToolOutput.smx")
- create_branch(tixi, SUMOFILE_XPATH)
- tixi.updateTextElement(SUMOFILE_XPATH, str(sumo_file_path))
+def main(cpacs: CPACS, wkdir: Path) -> None:
+ """
+ Converts a CPACS file geometry into a SUMO file geometry.
- # Save CPACS and SMX file
- tixi.save(str(cpacs_out_path))
- sumo.save(str(sumo_file_path))
+ Converts every elements in the SUMO .smx format (which is also an xml file).
+ Limitation: (What differences ?)
+ Due to some differences between both format, some CPACS definition could lead to issues.
-# =================================================================================================
-# MAIN
-# =================================================================================================
+ Source:
+ * CPACS documentation: https://www.cpacs.de/pages/documentation.html
+
+ Args:
+ cpacs_path (Path): Path to the CPACS file.
+ cpacs_out_path (Path): Path to the CPACS file.
+
+ Returns:
+ sumo_output_path (str): Path to the SUMO file, saved in the folder /ToolOutput.
+ """
-def main(cpacs_path, cpacs_out_path):
- log.info("----- Start of " + MODULE_NAME + " -----")
+ tixi = cpacs.tixi
+ sumo = open_tixi(Path(MODULE_DIR, "files", "sumo_empty.smx"))
- convert_cpacs_to_sumo(cpacs_path, cpacs_out_path)
+ # Convert all the different elements
+ convert_fuselages(tixi, sumo)
+ convert_wings(tixi, sumo)
+ convert_enginepylons(tixi, sumo)
+ convert_engines(tixi, sumo)
- log.info("----- End of " + MODULE_NAME + " -----")
+ # Get results directory
+ sumo_file_path = str(Path(wkdir, "ToolOutput.smx"))
+ log.info(f"Saving sumo at {sumo_file_path}.")
+ create_branch(tixi, SUMOFILE_XPATH)
+ tixi.updateTextElement(SUMOFILE_XPATH, sumo_file_path)
-if __name__ == "__main__":
- cpacs_path = get_toolinput_file_path(MODULE_NAME)
- cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
+ # Save SMX file
+ sumo.save(sumo_file_path)
+
+ if bool_(get_value(tixi, CPACS2SUMO_SUMO_GUI_XPATH)):
+ # Open SUMO
+ try:
+ log.info("To continue the workflow, please close the SUMO window.")
+ subprocess.run(["sumo", sumo_file_path], check=True)
+ except subprocess.CalledProcessError as e:
+ log.error(f"Failed to open SUMO: {e}.")
- main(cpacs_path, cpacs_out_path)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ call_main(main, MODULE_NAME)
diff --git a/ceasiompy/CPACS2SUMO/files/CPACS_3_0_ Documentation.chm b/ceasiompy/CPACS2SUMO/files/CPACS_3_0_ Documentation.chm
index 275031804..b2ad6ecb2 100644
Binary files a/ceasiompy/CPACS2SUMO/files/CPACS_3_0_ Documentation.chm and b/ceasiompy/CPACS2SUMO/files/CPACS_3_0_ Documentation.chm differ
diff --git a/ceasiompy/CPACS2SUMO/files/simpletest_cpacs2sumo.xml b/ceasiompy/CPACS2SUMO/files/simpletest_cpacs2sumo.xml
index f7569d19b..3afadcbce 100755
--- a/ceasiompy/CPACS2SUMO/files/simpletest_cpacs2sumo.xml
+++ b/ceasiompy/CPACS2SUMO/files/simpletest_cpacs2sumo.xml
@@ -544,7 +544,7 @@
-
+
Circle
Profile build up from set of Points on Circle where max Dimensions are 1..-1
diff --git a/ceasiompy/CPACS2SUMO/files/sumo_empty.smx b/ceasiompy/CPACS2SUMO/files/sumo_empty.smx
index fa2da6128..de949c8f5 100644
--- a/ceasiompy/CPACS2SUMO/files/sumo_empty.smx
+++ b/ceasiompy/CPACS2SUMO/files/sumo_empty.smx
@@ -1,5 +1,5 @@
-
+
0 -0.73374 0.000654 -0.73374 0.001305 -0.73368 0.001951 -0.73357 0.002588 -0.73342 0.003214 -0.73323 0.003827 -0.733 0.004423 -0.73273 0.005 -0.73242 0.005556 -0.73208 0.006088 -0.7317 0.006593 -0.73128 0.007071 -0.73084 0.007518 -0.73036 0.007934 -0.72985 0.008315 -0.72932 0.00866 -0.72876 0.008969 -0.72819 0.009239 -0.72759 0.009469 -0.72698 0.009659 -0.72635 0.009808 -0.72572 0.009914 -0.72507 0.009979 -0.72442 0.01 -0.72376 0.009979 -0.72311 0.009914 -0.72246 0.009808 -0.72181 0.009659 -0.72118 0.009469 -0.72055 0.009239 -0.71994 0.008969 -0.71934 0.00866 -0.71876 0.008315 -0.71821 0.007934 -0.71768 0.007518 -0.71717 0.007071 -0.71669 0.006593 -0.71625 0.006088 -0.71583 0.005556 -0.71545 0.005 -0.7151 0.004423 -0.71479 0.003827 -0.71452 0.003214 -0.7143 0.002588 -0.7141 0.001951 -0.71396 0.001305 -0.71385 0.000654 -0.71379 0 -0.71376
0 0.52531 0.020473 0.52531 0.061297 0.52779 0.10175 0.53274 0.14159 0.54014 0.18059 0.54992 0.21849 0.56204 0.25508 0.57643 0.29014 0.59299 0.32345 0.61163 0.35482 0.63223 0.38405 0.65468 0.41097 0.67883 0.43543 0.70454 0.45726 0.73165 0.47634 0.76001 0.49256 0.78944 0.50582 0.81977 0.51603 0.85081 0.52314 0.88238 0.5271 0.91428 0.5279 0.94632 0.52552 0.97832 0.51997 1.0101 0.51131 1.0414 0.49956 1.0721 0.48482 1.102 0.46715 1.1309 0.44668 1.1587 0.42352 1.1851 0.39781 1.2101 0.36971 1.2334 0.33939 1.2549 0.30703 1.2746 0.27281 1.2922 0.23696 1.3077 0.19969 1.3209 0.16121 1.3319 0.12176 1.3405 0.081585 1.3467 0.040916 1.3504 0 1.3516
@@ -8,4 +8,3 @@
-
\ No newline at end of file
diff --git a/ceasiompy/CPACS2SUMO/func/cst2coord.py b/ceasiompy/CPACS2SUMO/func/cst2coord.py
index a94257053..93b029f17 100644
--- a/ceasiompy/CPACS2SUMO/func/cst2coord.py
+++ b/ceasiompy/CPACS2SUMO/func/cst2coord.py
@@ -9,15 +9,14 @@
Output : coord = set of x-y coordinates of airfoil generated by CST
-Adapted from: Kulfan_CST/kulfan_to_coord.py
- by Ryan Barrett 'ryanbarr'
- https://github.com/Ry10/Kulfan_CST
+Adapted from: Kulfan_CST/kulfan_to_coord.py
+ by Ryan Barrett 'ryanbarr'
+ https://github.com/Ry10/Kulfan_CST
- Adapted from: Airfoil generation using CST parameterization method
- by Pramudita Satria Palar
- http://www.mathworks.com/matlabcentral/fileexchange/42239-airfoil-generation-using-cst-parameterization-method
+Adapted from: Airfoil generation using CST parameterization method
+ by Pramudita Satria Palar
+ http://www.mathworks.com/matlabcentral/fileexchange/42239-airfoil-generation-using-cst-parameterization-method
-Python version: >=3.8
| Author: Aidan Jungo
| Creation: 2021-04-26
@@ -32,10 +31,14 @@
# IMPORTS
# =================================================================================================
-from math import cos, factorial, pi
+from ceasiompy import log
+import numpy as np
+
+from math import cos, factorial
+
from pathlib import Path
-import numpy as np
+from math import pi
MODULE_DIR = Path(__file__).parent
@@ -76,7 +79,7 @@ def airfoil_coor(self):
for i in range(0, N):
zeta[i] = 2 * pi / N * i
- x[i] = 0.5 * (cos(zeta[i]) + 1)
+ x[i] = 0.5 * (cos(zeta[i][0]) + 1)
# N1 and N2 parameters (N1 = 0.5 and N2 = 1 for airfoil shape)
N1 = 0.5
@@ -90,12 +93,12 @@ def airfoil_coor(self):
xu = np.zeros(N - center_loc)
# Lower surface x-coordinates
- for i in range(len(xl)):
- xl[i] = x[i]
+ for i, _ in enumerate(xl):
+ xl[i] = x[i][0]
# Upper surface x-coordinates
- for i in range(len(xu)):
- xu[i] = x[i + center_loc]
+ for i, _ in enumerate(xu):
+ xu[i] = x[i + center_loc][0]
# Call ClassShape function to determine lower and upper surface y-coordinates
yl = self.__ClassShape(wl, xl, N1, N2, -dz)
@@ -135,16 +138,15 @@ def __ClassShape(w, x, N1, N2, dz):
# Calculate y output
y = np.zeros(len(x))
- for i in range(len(y)):
+ for i, _ in enumerate(y):
y[i] = C[i] * S[i] + x[i] * dz
return y
-
# =================================================================================================
# MAIN
# =================================================================================================
-if __name__ == "__main__":
- print("Nothing to execute!")
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/CPACS2SUMO/func/engineclasses.py b/ceasiompy/CPACS2SUMO/func/engineclasses.py
index c4e15ebcc..7589848a6 100644
--- a/ceasiompy/CPACS2SUMO/func/engineclasses.py
+++ b/ceasiompy/CPACS2SUMO/func/engineclasses.py
@@ -5,7 +5,6 @@
Classes to save engine/nacelle value for CPACS2SUMO
-Python version: >=3.8
| Author: Aidan Jungo
| Creation: 2021-02-25
@@ -21,12 +20,10 @@
# =================================================================================================
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from ceasiompy.utils.generalclasses import Transformation
from cpacspy.cpacsfunctions import get_float_vector
-log = get_logger()
-
# =================================================================================================
# CLASSES
@@ -164,11 +161,10 @@ def __init__(self, tixi, xpath):
self.xlist = get_float_vector(tixi, self.xpath + "/x")
self.ylist = get_float_vector(tixi, self.xpath + "/y")
-
# =================================================================================================
# MAIN
# =================================================================================================
-if __name__ == "__main__":
- print("Nothing to execute!")
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/CPACS2SUMO/func/getprofile.py b/ceasiompy/CPACS2SUMO/func/getprofile.py
index d8dc998cf..7566223d3 100644
--- a/ceasiompy/CPACS2SUMO/func/getprofile.py
+++ b/ceasiompy/CPACS2SUMO/func/getprofile.py
@@ -6,32 +6,22 @@
Functions used to get profile as list of point, either directly from
the list point in the CPACS file or from the CPACS CST2D curve
-Python version: >=3.8
| Author: Aidan Jungo
| Creation: 2021-04-26
-TODO:
-
- *
-
"""
# =================================================================================================
# IMPORTS
# =================================================================================================
-
-from ceasiompy.CPACS2SUMO.func.cst2coord import CST_shape
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from cpacspy.cpacsfunctions import get_float_vector
-log = get_logger()
-
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
+from tixi3.tixi3wrapper import Tixi3
+from typing import Tuple, List
+from ceasiompy.CPACS2SUMO.func.cst2coord import CST_shape
# =================================================================================================
@@ -39,17 +29,19 @@
# =================================================================================================
-def get_profile_coord(tixi, prof_uid):
- """Function to get profile coordinate point
+def get_profile_coord(tixi: Tixi3, prof_uid: str) -> Tuple[List, List, List]:
+ """
+ Get profile coordinate points.
Args:
- tixi (handles): TIXI Handle
- profileUID (uid): uID of the airfoil/profile to get
+ tixi (handles): TIXI Handle.
+ prof_uid (str): uID of the airfoil/profile to get.
Returns:
- prof_vect_x (list): list of point in x coordinate
- prof_vect_y (list): list of point in y coordinate
- prof_vect_z (list): list of point in z coordinate
+ (Tuple[List, List, List]): List of x, y, z coordinate points.
+ - List[float]: List of x-th coordinate points.
+ - List[float]: List of y-th coordinate points.
+ - List[float]: List of z-th coordinate points.
"""
@@ -131,11 +123,10 @@ def get_profile_coord(tixi, prof_uid):
return prof_vect_x, prof_vect_y, prof_vect_z
-
# =================================================================================================
# MAIN
# =================================================================================================
-if __name__ == "__main__":
- print("Nothing to execute!")
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/CPACS2SUMO/func/sumofunctions.py b/ceasiompy/CPACS2SUMO/func/sumofunctions.py
index 8bd79131f..9e8e0b18f 100644
--- a/ceasiompy/CPACS2SUMO/func/sumofunctions.py
+++ b/ceasiompy/CPACS2SUMO/func/sumofunctions.py
@@ -5,15 +5,10 @@
Functions used to help the cration of SUMO file
-Python version: >=3.8
| Author: Aidan Jungo
| Creation: 2021-02-25
-TODO:
-
- *
-
"""
# =================================================================================================
@@ -22,15 +17,10 @@
import json
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from cpacspy.cpacsfunctions import copy_branch
-log = get_logger()
-
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
+from tixi3.tixi3wrapper import Tixi3
# =================================================================================================
@@ -38,33 +28,39 @@
# =================================================================================================
-def sumo_str_format(x, y, z):
- """Function to get coordinate x,y,z into the string format which is use by SUMO.
+def sumo_str_format(x: float, y: float, z: float) -> str:
+ """
+ Get coordinate x,y,z into the string format which is use by SUMO.
Args:
- x (float): x coordinate
- y (float): y coordinate
- z (float): z coordinate
+ x (float): x coordinate.
+ y (float): y coordinate.
+ z (float): z coordinate.
Returns:
- sumo_str (str): String point format for SUMO
+ (str): String point format for SUMO.
"""
- sumo_str = str(x) + " " + str(y) + " " + str(z)
+ return str(x) + " " + str(y) + " " + str(z)
- return sumo_str
-
-def sumo_add_nacelle_lip(sumo, xpath, ax_offset=1.2, rad_offset=0.15, shape_coef=0.3):
- """Function to add nacelle lips option in SUMO.
+def sumo_add_nacelle_lip(
+ sumo: Tixi3,
+ xpath: str,
+ ax_offset: float = 1.2,
+ rad_offset: float = 0.15,
+ shape_coef: float = 0.3
+) -> None:
+ """
+ Add nacelle lips option in SUMO.
Args:
- sumo (obj): Tixi handle for SUMO
- xpath (str): xpath
- ax_offset (float): Axial offset of the nacelle lip
- rad_offset (float): Radial offset of the nacelle lip
- shape_coef (float): Shape coefficient of the nacelle lip
+ sumo (obj): Tixi handle for SUMO.
+ xpath (str): xpath.
+ ax_offset (float): Axial offset of the nacelle lip.
+ rad_offset (float): Radial offset of the nacelle lip.
+ shape_coef (float): Shape coefficient of the nacelle lip.
"""
@@ -74,13 +70,15 @@ def sumo_add_nacelle_lip(sumo, xpath, ax_offset=1.2, rad_offset=0.15, shape_coef
sumo.addTextAttribute(xpath + "/NacelleInletLip", "shapeCoef", str(shape_coef))
-def sumo_add_engine_bc(sumo, eng_name, part_uid):
- """Function to add engine boundary conditions in SUMO.
+def sumo_add_engine_bc(sumo: Tixi3, eng_name: str, part_uid: str) -> None:
+ """
+ Add engine boundary conditions in SUMO.
Args:
- sumo (obj): Tixi handle for SUMO
- eng_name (str): Name of this engine
- part_uid (str): Part of the nacelle (fan/core)
+ sumo (obj): Tixi handle for SUMO.
+ eng_name (str): Name of this engine.
+ part_uid (str): Part of the nacelle (fan/core).
+
"""
sumo.createElementAtIndex("/Assembly", "JetEngineSpec", 1)
@@ -117,7 +115,15 @@ def sumo_add_engine_bc(sumo, eng_name, part_uid):
sumo.addTextAttribute(jeregion_xpath, "type", "tail")
-def add_wing_cap(sumo, wg_sk_xpath):
+def add_wing_cap(sumo: Tixi3, wg_sk_xpath: str) -> None:
+ """
+ Add Wing caps.
+
+ Args:
+ sumo (Tixi3): Tixi handle for SUMO.
+ wg_sk_xpath (_type_): Wing skeleton xpath.
+
+ """
sumo.createElementAtIndex(wg_sk_xpath, "Cap", 1)
sumo.addTextAttribute(wg_sk_xpath + "/Cap[1]", "height", "0")
@@ -130,7 +136,17 @@ def add_wing_cap(sumo, wg_sk_xpath):
sumo.addTextAttribute(wg_sk_xpath + "/Cap[2]", "side", "north")
-def sumo_mirror_copy(sumo, xpath, uid, is_wing=True):
+def sumo_mirror_copy(sumo: Tixi3, xpath: str, uid: str, is_wing: bool = True) -> None:
+ """
+ If problem is symmetric in x-z-plane, copy attributes.
+
+ Args:
+ sumo (Tixi3): Tixi handle for SUMO.
+ xpath (str): xPath to element to copy.
+ uid (str): uid of element to copy.
+ is_wing (bool = True): True if wing.
+
+ """
skeleton = "BodySkeleton"
if is_wing:
@@ -184,11 +200,14 @@ def sumo_mirror_copy(sumo, xpath, uid, is_wing=True):
add_wing_cap(sumo, xpath_sym)
-
# =================================================================================================
# MAIN
# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
+
if __name__ == "__main__":
print("Nothing to execute!")
diff --git a/ceasiompy/CPACS2SUMO/tests/test_cpacs2sumo.py b/ceasiompy/CPACS2SUMO/tests/test_cpacs2sumo.py
index 70f463a8d..e037fb1e8 100644
--- a/ceasiompy/CPACS2SUMO/tests/test_cpacs2sumo.py
+++ b/ceasiompy/CPACS2SUMO/tests/test_cpacs2sumo.py
@@ -5,7 +5,6 @@
Test the module CPACS2SUMO (lib/CPACS2SUMO/cpacs2sumo.py')
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2018-10-26
diff --git a/ceasiompy/CPACSCreator/.gitignore b/ceasiompy/CPACSCreator/.gitignore
deleted file mode 100644
index e540f5b38..000000000
--- a/ceasiompy/CPACSCreator/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-tmp/*
\ No newline at end of file
diff --git a/ceasiompy/CPACSCreator/README.md b/ceasiompy/CPACSCreator/README.md
index 7b4e509ec..381b508bd 100644
--- a/ceasiompy/CPACSCreator/README.md
+++ b/ceasiompy/CPACSCreator/README.md
@@ -35,7 +35,7 @@ In a CEASIOMpy workflow `CPACSCreator` takes as input a CPACS file, the other in
## Limitations
-`CPACSCreator` allows you to modify the wings and fuselage of an aircraft. For now it is not possible to add or modify things like engines, control surfaces, landing gear, etc.
+`CPACSCreator` allows you to modify the wings and fuselage of an aircraft. For now it is not possible to add or modify things like engines, control surfaces, landing gear, etc.
## More information
diff --git a/ceasiompy/CPACSCreator/ToolInput/.keep b/ceasiompy/CPACSCreator/ToolInput/.keep
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/ceasiompy/CPACSCreator/ToolInput/.keep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ceasiompy/CPACSCreator/ToolOutput/.keep b/ceasiompy/CPACSCreator/ToolOutput/.keep
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/ceasiompy/CPACSCreator/ToolOutput/.keep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ceasiompy/CPACSCreator/__init__.py b/ceasiompy/CPACSCreator/__init__.py
index e69de29bb..ff61d0f98 100644
--- a/ceasiompy/CPACSCreator/__init__.py
+++ b/ceasiompy/CPACSCreator/__init__.py
@@ -0,0 +1,45 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for CPACSCreator module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+from ceasiompy import log
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = True
+
+# ===== Include GUI =====
+include_gui = True
+
+# ===== Add a Results Directory =====
+RES_DIR = True
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/CPACSCreator/__specs__.py b/ceasiompy/CPACSCreator/__specs__.py
index 42bebb7b4..a14cccb2c 100644
--- a/ceasiompy/CPACSCreator/__specs__.py
+++ b/ceasiompy/CPACSCreator/__specs__.py
@@ -1,23 +1,34 @@
-from pathlib import Path
-from ceasiompy.utils.moduleinterfaces import CPACSInOut
-# ===== Module Status =====
-# True if the module is active
-# False if the module is disabled (not working or not ready)
-module_status = True
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
-# ===== Results directory path =====
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-RESULTS_DIR = Path("Results", "CPACSCreator")
+GUI Interface of CPACSCreator.
-# ===== CPACS inputs and outputs =====
-cpacs_inout = CPACSInOut()
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
-# ----- Input -----
+from ceasiompy.utils.moduleinterfaces import CPACSInOut
+
+from ceasiompy import log
-# No inputs value for this modules
+# ==============================================================================
+# VARIABLE
+# ==============================================================================
+
+cpacs_inout = CPACSInOut()
-# ----- Output -----
+# =================================================================================================
+# MAIN
+# =================================================================================================
-# No outputs value for this modules
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/CPACSCreator/cpacscreator.py b/ceasiompy/CPACSCreator/cpacscreator.py
new file mode 100644
index 000000000..9da9f6b37
--- /dev/null
+++ b/ceasiompy/CPACSCreator/cpacscreator.py
@@ -0,0 +1,64 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+CPACS Creator python launcher
+
+
+| Author : Aidan Jungo
+| Creation: 2018-10-29
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+from ceasiompy.utils.ceasiompyutils import (
+ call_main,
+ run_software,
+ get_install_path,
+)
+
+from pathlib import Path
+from cpacspy.cpacspy import CPACS
+
+from ceasiompy import log
+from ceasiompy.CPACSCreator import MODULE_NAME
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+def main(cpacs: CPACS, wkdir: Path) -> None:
+ """
+ Runs CPACSCrator with an input CPACS file
+ and puts the output CPACS file in the folder /ToolInput.
+ CPACSCreator must be installed on your computer.
+
+ Source :
+ * For CPACSCreator https://github.com/cfsengineering/CPACSCreator
+ """
+
+ cpacs_in = cpacs.cpacs_file
+
+ # Get the name of CPACSCreator (several names exists, depending on the OS and the version)
+ cpacscreator_names = ["cpacscreator", "CPACS-Creator", "CPACSCreator"]
+
+ for name in cpacscreator_names:
+ install_path = get_install_path(name)
+ if install_path is not None:
+ software_name = name
+ break
+
+ if install_path is None:
+ log.warning("CPACSCreator is not installed on your computer.")
+
+ # Run CPACSCreator
+ run_software(software_name=software_name, arguments=[str(cpacs_in)], wkdir=wkdir)
+
+
+if __name__ == "__main__":
+ call_main(main, MODULE_NAME)
diff --git a/ceasiompy/CPACSCreator/cpacscreatorrun.py b/ceasiompy/CPACSCreator/cpacscreatorrun.py
deleted file mode 100644
index 16da56fa5..000000000
--- a/ceasiompy/CPACSCreator/cpacscreatorrun.py
+++ /dev/null
@@ -1,127 +0,0 @@
-"""
-CEASIOMpy: Conceptual Aircraft Design Software
-
-Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-
-CPACS Creator python launcher
-
-Python version: >=3.8
-
-| Author : Aidan Jungo
-| Creation: 2018-10-29
-
-TODO:
-
- *
-
-"""
-
-# =================================================================================================
-# IMPORTS
-# =================================================================================================
-
-
-import shutil
-from pathlib import Path
-
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.ceasiompyutils import (
- SoftwareNotInstalled,
- get_install_path,
- get_results_directory,
- run_software,
-)
-from ceasiompy.utils.moduleinterfaces import get_toolinput_file_path, get_tooloutput_file_path
-
-log = get_logger()
-
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
-
-
-def launch_cpacscreator(cpacs_path, cpacs_out_path):
- """Function to run CPACSCrator with an input CPACS file
-
- Function 'launch_cpacscreator' run CPACSCrator with an input CPACS file and
- put the output CPACS file in the folder /ToolInput. CPACSCreator must be
- installed on your computer to run this function. (If you install CEASIOMpy
- with Conda it should be installed automatically)
-
- Source :
- * For CPACSCreator https://github.com/cfsengineering/CPACSCreator
-
- Args:
- cpacs_path (Path): Path to the input CPACS file
- cpacs_out_path (Path): Path to the output CPACS file
-
- """
-
- # Get the name of CPACSCreator (several names exists, depending on the OS and the version)
- cpacscreator_names = ["cpacscreator", "CPACS-Creator", "CPACSCreator"]
-
- for name in cpacscreator_names:
- install_path = get_install_path(name)
- if install_path is not None:
- software_name = name
- break
-
- if install_path is None:
- raise SoftwareNotInstalled("CPACSCreator is not installed on your computer")
-
- # Create a temporary directory to run CPACSCreator
- results_dir = get_results_directory(MODULE_NAME)
- results_dir.mkdir(parents=True, exist_ok=True)
- tmp_dir = Path(results_dir, "tmp")
- tmp_dir.mkdir()
- log.info(f"A tmp directory has been create at: {tmp_dir}")
-
- # Copy CPACS input file (.xml) in /tmp directory
- cpacs_tmp = Path(tmp_dir, "cpacsTMP.xml")
- shutil.copy(cpacs_path, cpacs_tmp)
- log.info("The input CPACS file has been copied in tmp directory")
-
- # Run CPACSCreator
- run_software(software_name=software_name, arguments=[str(cpacs_tmp)], wkdir=tmp_dir)
-
- # Copy CPACS tmp file (.xml) from the temp directory to /ToolOutput
- if cpacs_tmp.is_file():
- shutil.copy(cpacs_tmp, cpacs_out_path)
- log.info("The output CPACS file has been copied in /ToolOutput")
- else:
- log.error("The Output CPACS file cannot be found!")
-
-
-# TODO: create a new function to export screenshots ...
-# # Run cpacscreator with a script to save a screenshot
-# # Problem: TIGLViewer in not close after the script in the shell
-# os.system('cpacscreator ' + cpacs_tmp + ' --script test_script.js')
-
-# =================================================================================================
-# MAIN
-# =================================================================================================
-
-
-def main(cpacs_path, cpacs_out_path):
-
- log.info("----- Start of " + MODULE_NAME + " -----")
-
- launch_cpacscreator(cpacs_path, cpacs_out_path)
-
- log.info("----- End of " + MODULE_NAME + " -----")
-
-
-if __name__ == "__main__":
-
- cpacs_path = get_toolinput_file_path(MODULE_NAME)
- cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
-
- main(cpacs_path, cpacs_out_path)
diff --git a/ceasiompy/CPACSUpdater/README.md b/ceasiompy/CPACSUpdater/README.md
new file mode 100644
index 000000000..59d3b9e3a
--- /dev/null
+++ b/ceasiompy/CPACSUpdater/README.md
@@ -0,0 +1,66 @@
+
+
+
+# CPACSUpdater
+
+**Categories:** CPACS modifier
+
+**State**: :heavy_check_mark:
+
+This is a template module. Its purpose is to illustrate how other modules of CEASIOMpy should be structured, set up and documented.
+
+
+
+
+
+## Inputs
+
+'CPACSUpdater' needs a CPACS file to modify.
+
+## Analyses
+
+'CPACSUpdater' computes nothing.
+
+For each wing segment, you can choose which type of control surfaces to add in CEASIOMpy's GUI.
+
+Fowler flap:
+
+
+
+
+
+Essentially 'CPACSUpdater':
+ 1. Copies and scales original wing to make the flap.
+ 2. Crops the original wing to make place for the flap.
+
+Plain flap/rudder/aileron: It is important to specify the correct type of control surface if you are going to deform them in the future.
+
+
+
+
+
+Essentially 'CPACSUpdater':
+ 1. Chooses a point in x to divide the main wing in two parts.
+ 2. Then uses a sin function to close these two parts.
+
+Please call 'CPACSCreator' after calling 'CPACSUpdater' to check if your CPACS file has been correctly modified.
+
+## Outputs
+
+Modified CPACS file with control surfaces.
+
+## Installation or requirements
+
+CPACSUpdater is a native CEASIOMpy module, hence it is available and installed by default.
+
+## Limitations
+
+1. Only adds control surfaces. No other operations implemented yet.
+2. Each wings are decomposed into several little wings. For example, if you had 1 wing with 3 segments, then CPACSUpdater will decompose this wing into 3 wings with 1 segment each. This is due to the limitation of the CPACS format.
+
+## More information
+
+1. This module was created to look at rudder/aileron/elevator deformations in SU2.
+2. Don't add control surfaces if you intend to use PyAVL !
+ - PyAVL automatically looks in the CPACS file for control surface definitions at the xPath: /wing/componentSegments/componentSegment/ControlSurfaces.
+ - CPACSUpdater automatically removes the /componentSegments element in the CPACS file for every wing.
\ No newline at end of file
diff --git a/ceasiompy/CPACSUpdater/ToolInput/.keep b/ceasiompy/CPACSUpdater/ToolInput/.keep
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/ceasiompy/CPACSUpdater/ToolInput/.keep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ceasiompy/CPACSUpdater/ToolOutput/.keep b/ceasiompy/CPACSUpdater/ToolOutput/.keep
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/ceasiompy/CPACSUpdater/ToolOutput/.keep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ceasiompy/CPACSUpdater/__init__.py b/ceasiompy/CPACSUpdater/__init__.py
index e69de29bb..244f3dd21 100644
--- a/ceasiompy/CPACSUpdater/__init__.py
+++ b/ceasiompy/CPACSUpdater/__init__.py
@@ -0,0 +1,48 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for CPACSUpdater module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+from ceasiompy import log
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = True
+
+# ===== Include GUI =====
+include_gui = True
+
+# ===== Add a Results Directory =====
+RES_DIR = False
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+# Specific to CPACSUpdater module
+CONTROL_SURFACES_LIST = ["none", "plain_aileron", "plain_rudder", "plain_flap", "fowler_flap"]
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/CPACSUpdater/__specs__.py b/ceasiompy/CPACSUpdater/__specs__.py
index 820ea69e3..9db6d8fbe 100644
--- a/ceasiompy/CPACSUpdater/__specs__.py
+++ b/ceasiompy/CPACSUpdater/__specs__.py
@@ -1,82 +1,76 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+GUI Interface of CPACSUpdater.
+
+
+| Author: Leon Deligny
+| Creation: 14-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+import streamlit as st
+
+from ceasiompy.utils.geometryfunctions import get_segments
+
from ceasiompy.utils.moduleinterfaces import CPACSInOut
-# ===== Module Status =====
-# True if the module is active
-# False if the module is disabled (not working or not ready)
-module_status = False
+from ceasiompy import log
+from ceasiompy.utils.commonxpath import (
+ CPACSUPDATER_CTRLSURF_XPATH,
+ CPACSUPDATER_ADD_CTRLSURFACES_XPATH,
+)
+from ceasiompy.CPACSUpdater import (
+ include_gui,
+ CONTROL_SURFACES_LIST,
+)
-# ===== CPACS inputs and outputs =====
+# ==============================================================================
+# VARIABLE
+# ==============================================================================
cpacs_inout = CPACSInOut()
-include_gui = False
-
-# ----- Input -----
-
-# TODO
-
-# * In the following example we add three (!) new entries to 'cpacs_inout'
-# * Try to use (readable) loops instead of copy-pasting three almost same entries :)
-# for direction in ['x', 'y', 'z']:
-# cpacs_inout.add_input(
-# var_name=direction,
-# var_type=float,
-# default_value=None,
-# unit='1',
-# descr=f"Fuselage scaling on {direction} axis",
-# xpath=AIRCRAFT_XPATH + f'/model/fuselages/fuselage/transformation/scaling/{direction}',
-# gui=include_gui,
-# gui_name=f'{direction.capitalize()} scaling',
-# gui_group='Fuselage scaling',
-# )
-#
-# cpacs_inout.add_input(
-# var_name='test',
-# var_type=str,
-# default_value='This is a test',
-# unit=None,
-# descr='This is a test of description',
-# xpath='/cpacs/toolspecific/CEASIOMpy/test/myTest',
-# gui=include_gui,
-# gui_name='My test',
-# gui_group='Group Test',
-# )
-#
-# cpacs_inout.add_input(
-# var_name='aeromap_uid',
-# var_type=list,
-# default_value=None,
-# xpath='/cpacs/toolspecific/CEASIOMpy/aerodynamics/su2/aeroMapUID',
-# gui=include_gui,
-# gui_name='__AEROMAP_SELECTION',
-# )
-#
-# cpacs_inout.add_input(
-# var_name='aeromap_uid',
-# var_type=list,
-# default_value=None,
-# xpath='/cpacs/toolspecific/CEASIOMpy/aerodynamics/skinFriction/aeroMapToCalculate',
-# gui=include_gui,
-# gui_name='__AEROMAP_CHECKBOX',
-# )
-#
-# cpacs_inout.add_input(
-# var_name='other_var',
-# var_type=list,
-# default_value= [2,33,444],
-# unit='[unit]',
-# xpath='/cpacs/toolspecific/CEASIOMpy/test/myList',
-# gui=include_gui,
-# gui_name='Choice',
-# gui_group='My Selection'
-# )
-#
-# # ----- Output -----
-#
-# cpacs_inout.add_output(
-# var_name='output',
-# default_value=None,
-# unit='1',
-# descr='Description of the output',
-# xpath='/cpacs/toolspecific/CEASIOMpy/test/myOutput',
-# )
+# ==============================================================================
+# CALL
+# ==============================================================================
+
+cpacs_inout.add_input(
+ var_name='add_control_surfaces',
+ var_type=bool,
+ default_value=True,
+ unit=None,
+ descr='Adds control surfaces',
+ xpath=CPACSUPDATER_ADD_CTRLSURFACES_XPATH,
+ gui=include_gui,
+ gui_name='Add Control Surfaces',
+ gui_group='Control Surfaces Settings',
+)
+
+segments_list = get_segments(st.session_state.cpacs.tixi)
+for (wing_name, segment_name) in segments_list:
+ cpacs_inout.add_input(
+ var_name=f"control_surface_{wing_name}_{segment_name}",
+ var_type=list,
+ default_value=CONTROL_SURFACES_LIST,
+ unit=None,
+ descr="Type of control surface to add at specific wing and segment of wing.",
+ xpath=CPACSUPDATER_CTRLSURF_XPATH + f"/{wing_name}/{segment_name}",
+ gui=include_gui,
+ gui_name=f"Control Surface for segment {segment_name} of wing {wing_name}",
+ gui_group='Control Surfaces Settings',
+ )
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/CPACSUpdater/cpacsupdater.py b/ceasiompy/CPACSUpdater/cpacsupdater.py
index 5bee0e3ce..81c22f633 100644
--- a/ceasiompy/CPACSUpdater/cpacsupdater.py
+++ b/ceasiompy/CPACSUpdater/cpacsupdater.py
@@ -3,16 +3,11 @@
Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-Update geometry of a CPACS file for simple modification or optimisation
+Update geometry of a CPACS file.
-Python version: >=3.8
-| Author: Aidan Jungo
-| Creation: 2019-11-11
-
-TODO:
-
- * This module is still a bit tricky to use, it will be simplified in the future
+| Author: Leon Deligny
+| Creation: 25-Feb-2025
"""
@@ -20,102 +15,31 @@
# IMPORTS
# ==============================================================================
-from ceasiompy.utils.ceasiomlogger import get_logger
-from cpacspy.cpacsfunctions import get_tigl_configuration, open_tigl, open_tixi
-
-log = get_logger()
-
-
-# ==============================================================================
-# CLASSES
-# ==============================================================================
-
+from cpacspy.cpacsfunctions import get_value
+from ceasiompy.utils.ceasiompyutils import call_main
+from ceasiompy.CPACSUpdater.func.controlsurfaces import add_control_surfaces
-# ==============================================================================
-# FUNCTIONS
-# ==============================================================================
-
-
-def update_cpacs_file(cpacs_path, cpacs_out_path, optim_var_dict):
- """Function to update a CPACS file with value from the optimiser
+from cpacspy.cpacspy import CPACS
- This function sets the new values of the design variables given by
- the routine driver to the CPACS file, using the Tigl and Tixi handler.
+from ceasiompy.CPACSUpdater import MODULE_NAME
+from ceasiompy.utils.commonxpath import CPACSUPDATER_ADD_CTRLSURFACES_XPATH
- Source:
- * See CPACSCreator api function,
+# =================================================================================================
+# MAIN
+# =================================================================================================
- Args:
- cpacs_path (Path): Path to CPACS file to update
- cpacs_out_path (Path):Path to the updated CPACS file
- optim_var_dict (dict): Dictionary containing all the variable
- value/min/max and command to modify a CPACS file
+def main(cpacs: CPACS) -> None:
"""
+ Checks GUI values and updates CPACS file accordingly.
+ """
+ # Define variables
+ tixi = cpacs.tixi
- log.info("----- Start of CPACSUpdater -----")
- log.info(f"{cpacs_path} will be updated.")
-
- tixi = open_tixi(cpacs_path)
- tigl = open_tigl(tixi)
-
- # Object seems to be unused, but are use in "eval" function
- aircraft = get_tigl_configuration(tigl)
- wings = aircraft.get_wings()
- fuselage = aircraft.get_fuselages().get_fuselage(1)
-
- # Perform update of all the variable contained in 'optim_var_dict'
- for name, variables in optim_var_dict.items():
-
- # Unpack the variables
- val_type, listval, _, _, getcommand, setcommand = variables
-
- if val_type == "des" and listval[0] not in ["-", "True", "False"]:
-
- if setcommand not in ["-", ""]:
-
- # Define variable (var1,var2,..)
- locals()[str(name)] = listval[-1]
-
- # Update value by using tigl configuration
- if ";" in setcommand: # if more than one command on the line
- command_split = setcommand.split(";")
- for setcommand in command_split:
- eval(setcommand)
- else:
- eval(setcommand)
- else:
-
- # Update value directly in the CPACS file
- xpath = getcommand
- tixi.updateTextElement(xpath, str(listval[-1]))
-
- aircraft.write_cpacs(aircraft.get_uid())
- tigl.close()
- tixi.save(str(cpacs_out_path))
-
- log.info(f"{cpacs_out_path} has been saved.")
- log.info("----- Start of CPACSUpdater -----")
+ # Update CPACS
+ if get_value(tixi, CPACSUPDATER_ADD_CTRLSURFACES_XPATH):
+ add_control_surfaces(tixi)
-# ==============================================================================
-# MAIN
-# ==============================================================================
-
if __name__ == "__main__":
-
- print("Nothing to execute!")
-
-# Other functions which could be useful
-# help(aircraft)
-# help(wings)
-# help(fuselage)
-# help(wings.get_wing(1).get_section(1))
-# uid = wings.get_wing(1).get_section(2).get_uid()
-# help(wings.get_wing(1).get_positioning_transformation(uid))
-# wings.get_wing(1).get_section(2).set_rotation(geometry.CTiglPoint(40,40,-4))
-# airfoil = wings.get_wing(1).get_section(1).get_section_element(1).get_airfoil_uid()
-# help(airfoil)
-# wings.get_wing(1).get_section(1).get_section_element(1).set_airfoil_uid('NACA0006')
-# scal = wings.get_wing(1).get_section(1).get_section_element(1).get_scaling()
-# help(wings.get_wing(1).get_section(2))
+ call_main(main, MODULE_NAME)
diff --git a/ceasiompy/CPACSUpdater/files/aileron.png b/ceasiompy/CPACSUpdater/files/aileron.png
new file mode 100644
index 000000000..10b1bd60b
Binary files /dev/null and b/ceasiompy/CPACSUpdater/files/aileron.png differ
diff --git a/ceasiompy/CPACSUpdater/files/ctrlsurf.png b/ceasiompy/CPACSUpdater/files/ctrlsurf.png
new file mode 100644
index 000000000..8b41a0086
Binary files /dev/null and b/ceasiompy/CPACSUpdater/files/ctrlsurf.png differ
diff --git a/ceasiompy/CPACSUpdater/files/fowlerflap.png b/ceasiompy/CPACSUpdater/files/fowlerflap.png
new file mode 100644
index 000000000..46143128c
Binary files /dev/null and b/ceasiompy/CPACSUpdater/files/fowlerflap.png differ
diff --git a/ceasiompy/CPACSUpdater/func/controlsurfaces.py b/ceasiompy/CPACSUpdater/func/controlsurfaces.py
new file mode 100644
index 000000000..f38929a5c
--- /dev/null
+++ b/ceasiompy/CPACSUpdater/func/controlsurfaces.py
@@ -0,0 +1,702 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Small description of the script
+
+
+| Author: Leon Deligny
+| Creation: 25-Feb-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+import math
+import numpy as np
+
+from numpy import array
+from cpacspy.cpacsfunctions import get_float_vector
+from ceasiompy.utils.mathsfunctions import (
+ rot,
+ rotate_2d_point,
+ get_rotation_matrix,
+)
+from ceasiompy.utils.geometryfunctions import (
+ get_uid,
+ elements_number,
+ find_wing_xpath,
+ get_segments_wing,
+)
+from ceasiompy.CPACSUpdater.func.utils import (
+ copy,
+ remove,
+ find_max_x,
+ find_min_x,
+ array_to_str,
+ interpolate_points,
+ symmetric_operation,
+)
+
+from numpy import ndarray
+from tixi3.tixi3wrapper import Tixi3
+from typing import (
+ List,
+ Dict,
+ Tuple,
+)
+
+from ceasiompy import log
+from ceasiompy.utils.commonxpath import (
+ WINGS_XPATH,
+ AIRFOILS_XPATH,
+ CPACSUPDATER_CTRLSURF_XPATH,
+)
+
+# ==============================================================================
+# FUNCTIONS
+# ==============================================================================
+
+
+def retrieve_gui_ctrlsurf(tixi: Tixi3) -> Dict[str, List]:
+ """
+ Retrieves all child xPaths under the given control surface xPath and returns a dictionary
+ with wing names as keys and lists of segment names as values,
+ excluding segments with value 'none'.
+
+ Args:
+ tixi (Tixi3): Tixi handle of CPACS file.
+ ctrlsurf_xpath (str): Base xPath for control surfaces.
+
+ Returns:
+ (dict): Keys are wing names, values are tuple(segment names, values).
+
+ """
+
+ # Load constants
+ wing_cnt = tixi.getNumberOfChilds(CPACSUPDATER_CTRLSURF_XPATH)
+
+ # Load variable
+ result = {}
+
+ #
+ for i in range(1, wing_cnt + 1):
+ wing_name = tixi.getChildNodeName(CPACSUPDATER_CTRLSURF_XPATH, i)
+ wing_xpath = f"{CPACSUPDATER_CTRLSURF_XPATH}/{wing_name}"
+ seg_cnt = tixi.getNumberOfChilds(wing_xpath)
+
+ wing_sgt_list = []
+ for j in range(1, seg_cnt + 1):
+ segment_name = tixi.getChildNodeName(wing_xpath, j)
+ segment_xpath = f"{wing_xpath}/{segment_name}"
+ segment_value = tixi.getTextElement(segment_xpath)
+ if segment_value != "none":
+ wing_sgt_list.append((segment_name, segment_value))
+ result[wing_name] = wing_sgt_list
+
+ if not result:
+ log.warning("You did not define any control surfaces.")
+
+ return result
+
+
+def get_point(tixi: Tixi3, xpath: str) -> Tuple[float, float, float]:
+ x = tixi.getDoubleElement(xpath + "/x")
+ y = tixi.getDoubleElement(xpath + "/y")
+ z = tixi.getDoubleElement(xpath + "/z")
+ return x, y, z
+
+
+def compute_abs_location(tixi: Tixi3, wing_xpath: str) -> Dict[str, Tuple[str, str, str]]:
+ """
+ Need to compute absolute locations for the main wing.
+ """
+ # Define constants
+ sec = "section"
+ secs_xpath = wing_xpath + f"/{sec}s"
+ secs_cnt = elements_number(tixi, secs_xpath, sec, logg=False)
+
+ # Define variables
+ result = {}
+ # TODO: Case with scale, where do we put the scaling operation.
+ # scale_xpath = wing_xpath + "/transformation/scaling/"
+ # sx, sy, sz = get_point(tixi, scale_xpath)
+
+ rot_xpath = wing_xpath + "/transformation/rotation/"
+ deg_x, deg_y, deg_z = get_point(tixi, rot_xpath)
+ rax, ray, raz = -math.radians(deg_x), -math.radians(deg_y), -math.radians(deg_z)
+
+ trans_xpath = wing_xpath + "/transformation/translation/"
+ tx, ty, tz = get_point(tixi, trans_xpath)
+
+ rx, ry, rz = get_rotation_matrix(rax, ray, raz)
+ rot = rz @ ry @ rx
+ x, y, z = tx, ty, tz
+
+ # First section is kept identical
+ ele_xpath = secs_xpath + f"/{sec}[1]/elements/element[1]"
+ element_uid = get_uid(tixi, ele_xpath)
+ result[element_uid] = (str(x), str(y), str(z))
+
+ if secs_cnt > 2:
+ for i_sec in range(2, secs_cnt):
+ sec_xpath = secs_xpath + f"/{sec}[{i_sec}]"
+ sec_uid = get_uid(tixi, sec_xpath)
+
+ pos = "positioning"
+ poss_xpath = wing_xpath + f"/{pos}s"
+ poss_cnt = elements_number(tixi, poss_xpath, pos, logg=False)
+
+ for i_pos in range(poss_cnt):
+ pos_xpath = poss_xpath + f"/{pos}[{i_pos + 1}]"
+ if tixi.getTextElement(pos_xpath + "/toSectionUID") == sec_uid:
+ length: float = tixi.getDoubleElement(pos_xpath + "/length")
+ sweep: float = math.radians(tixi.getDoubleElement(pos_xpath + "/sweepAngle"))
+ dih: float = math.radians(tixi.getDoubleElement(pos_xpath + "/dihedralAngle"))
+ break
+ else:
+ log.warning("Issue with positioning. Associated with wrong section uID.")
+
+ x_ = length * math.sin(sweep)
+ y_ = length * math.cos(dih) * math.cos(sweep)
+ z_ = length * math.sin(dih) * math.cos(sweep)
+
+ coord_ = array([x_, y_, z_])
+ x_, y_, z_ = rot.dot(coord_)
+
+ x += x_
+ y += y_
+ z += z_
+
+ ele_xpath = sec_xpath + "/elements/element"
+ element_uid = get_uid(tixi, ele_xpath)
+
+ result[element_uid] = (str(x), str(y), str(z))
+
+ return result
+
+
+def filter_sections(
+ tixi: Tixi3,
+ new_xpath: str,
+ wing_xpath: str,
+ uid_list: List,
+ removed_sec: List,
+ kept_sec: List,
+) -> None:
+ """
+ Remove sections where elements are not the one in seg_from_uid or seg_to_uid.
+ """
+ # Define constants
+ sec = "section"
+ secs_xpath = new_xpath + f"/{sec}s"
+ secs_cnt = elements_number(tixi, secs_xpath, sec, logg=False)
+
+ # Filter the sections
+ for i_sec in range(secs_cnt, 0, -1):
+ sec_xpath = secs_xpath + f"/{sec}[{i_sec}]"
+ org_sec_xpath = wing_xpath + f"/{sec}s/{sec}[{i_sec}]"
+ element_uid = get_uid(tixi, org_sec_xpath + "/elements/element")
+
+ # If it does not define the segment,
+ # remove the section.
+ sec_uid = get_uid(tixi, org_sec_xpath)
+ if element_uid not in uid_list:
+ removed_sec.append(sec_uid)
+ remove(tixi, sec_xpath)
+ else:
+ kept_sec.append(sec_uid)
+
+ if len(kept_sec) != 2:
+ log.warning("Issue with number of kept sections.")
+
+
+def filter_positionings(
+ tixi: Tixi3,
+ new_xpath: str,
+ wing_xpath: str,
+ kept_sec: List,
+) -> None:
+ """
+ Remove the correct positionings corresponding to the removed sections.
+ """
+ # Define constants
+ pos = "positioning"
+ poss_xpath = new_xpath + f"/{pos}s"
+ poss_cnt = elements_number(tixi, poss_xpath, pos, logg=False)
+
+ # Filter positionings
+ for i_pos in range(poss_cnt, 0, -1):
+ pos_xpath = poss_xpath + f"/{pos}[{i_pos}]"
+ org_pos_xpath = wing_xpath + f"/{pos}s/{pos}[{i_pos}]"
+ from_xpath = org_pos_xpath + "/fromSectionUID"
+ if tixi.checkElement(from_xpath):
+ from_sec_uid = tixi.getTextElement(from_xpath)
+ else:
+ from_sec_uid = ""
+ to_sec_uid = tixi.getTextElement(org_pos_xpath + "/toSectionUID")
+
+ # If it is not the first one
+ if from_sec_uid != "":
+ if not (
+ (from_sec_uid in kept_sec) and (to_sec_uid in kept_sec)
+ ):
+ remove(tixi, pos_xpath)
+
+ poss_cnt = elements_number(tixi, poss_xpath, pos, logg=False)
+ if poss_cnt != 2:
+ log.warning("Issue with number of positionings.")
+
+ # Update first positioning accordingly
+ for i_pos in range(poss_cnt, 0, -1):
+ pos_xpath = poss_xpath + f"/{pos}[{i_pos}]"
+ from_xpath = pos_xpath + "/fromSectionUID"
+ if tixi.checkElement(from_xpath):
+ from_sec_uid = tixi.getTextElement(from_xpath)
+ else:
+ from_sec_uid = ""
+ if from_sec_uid == "":
+ i_pos_null = i_pos
+ else:
+ sec_null = tixi.getTextElement(from_xpath)
+
+ tixi.updateTextElement(poss_xpath + f"/{pos}[{i_pos_null}]/toSectionUID", sec_null)
+
+
+def filter_segments(
+ tixi: Tixi3,
+ new_xpath: str,
+ wing_xpath: str,
+ seg_from_uid: str,
+ seg_to_uid: str,
+) -> None:
+ """
+ Keep only the Seg = [seg_from_uid, seg_to_uid] segment.
+ """
+ # Define constants
+ seg = "segment"
+ segs_xpath = new_xpath + f"/{seg}s"
+ segs_cnt = elements_number(tixi, segs_xpath, seg, logg=False)
+
+ #
+ # Filter segments iteratively
+ for i_seg in range(segs_cnt, 0, -1):
+ seg_xpath = segs_xpath + f"/{seg}[{i_seg}]"
+ org_seg_xpath = wing_xpath + f"/{seg}s/{seg}[{i_seg}]"
+
+ if not (
+ (tixi.getTextElement(org_seg_xpath + "/fromElementUID") == seg_from_uid)
+ and (tixi.getTextElement(org_seg_xpath + "/toElementUID") == seg_to_uid)
+ ):
+ remove(tixi, seg_xpath)
+
+ if elements_number(tixi, segs_xpath, seg, logg=False) != 1:
+ log.warning("Issue with number of segments.")
+
+
+def decompose_wing(tixi: Tixi3, wing_name: str) -> None:
+ """
+ Decompose the wing wing_name into many "sub"-wings with 1 segment each.
+ """
+ # Define constants
+ wing_xpath = find_wing_xpath(tixi, wing_name)
+ segments = get_segments_wing(tixi, wing_name)
+ loc = compute_abs_location(tixi, wing_xpath)
+
+ #
+ # Remove unecessary data
+ remove(tixi, wing_xpath + "/componentSegments")
+
+ # Copy wing times the number of sections with
+ for (seg_name, seg_from_uid, seg_to_uid) in segments:
+ uid_list = [seg_from_uid, seg_to_uid]
+ new_xpath = copy(tixi, wing_xpath, "wing", seg_name)
+ removed_sec = []
+ kept_sec = []
+
+ # Remove unneccessary sections
+ filter_sections(tixi, new_xpath, wing_xpath, uid_list, removed_sec, kept_sec)
+
+ # Remove positionings corresponding to the removed sections
+ # from the 'filter_sections' function.
+ filter_positionings(tixi, new_xpath, wing_xpath, kept_sec)
+
+ # Keep only the Seg = [seg_from_uid, seg_to_uid] segment
+ filter_segments(tixi, new_xpath, wing_xpath, seg_from_uid, seg_to_uid)
+
+ # Modify translate vector accordingly
+ trsl_xpath = new_xpath + "/transformation/translation"
+ update_xpath_at_xyz(
+ tixi, trsl_xpath, loc[seg_from_uid][0], loc[seg_from_uid][1], loc[seg_from_uid][2]
+ )
+
+ # Remove original wing
+ remove(tixi, wing_xpath)
+
+
+def fowler_transform(
+ x_values: ndarray,
+ z_values: ndarray,
+ x_ref: float
+) -> Tuple[ndarray, ndarray, ndarray]:
+ """
+ Transforms z values to 0.0 for x values greater than x_ref.
+ """
+
+ mask = (x_values > x_ref) & (z_values < 0)
+
+ # Apply the transformation to the z values where the mask is True
+ transformed_z = np.where(
+ mask,
+ z_values * np.exp(-100.0 * (x_values - x_ref) * (1 - x_values)),
+ z_values,
+ )
+
+ return transformed_z, x_values[mask], transformed_z[mask]
+
+
+def plain_transform(
+ x_values: ndarray,
+ z_values: ndarray,
+ x_ref: float,
+) -> Tuple[ndarray, ndarray, ndarray, ndarray]:
+ """
+ Transforms z values to create a rounded shape nearest to x_ref.
+ """
+
+ curve = -0.035
+
+ # Mask for values less than x_ref
+ mask = x_values < x_ref
+ newx_values = x_values[mask]
+ newz_values = z_values[mask]
+
+ # Create new x values using sine function
+ x = np.arange(0.0, 1.1, 0.2)
+ new_x = curve * np.sin(np.pi * x)
+ max_x_pos, z_pos, _, z_neg = find_max_x(newx_values, newz_values)
+
+ close_x = max_x_pos + new_x
+ close_z = np.linspace(z_pos, z_neg, len(close_x))
+ newx_values = np.concatenate((newx_values, close_x))
+ newz_values = np.concatenate((newz_values, close_z))
+
+ # Flap
+ x_flap = x_ref + 0.02
+ mask_flap = x_values > x_flap
+ newx_flapvalues = x_values[mask_flap]
+ newz_flapvalues = z_values[mask_flap]
+
+ _, z_pos, _, z_neg = find_min_x(newx_flapvalues, newz_flapvalues)
+
+ # Create new x values for the flap using sine function
+ x = np.arange(1.0, -0.1, -0.05)
+ new_x = curve * np.sin(np.pi * x)
+
+ arc_x = x_flap + new_x
+ arc_z = np.linspace(z_pos - 0.001, z_neg + 0.001, len(x))[::-1]
+
+ # Sort the values such that the pairs (x, z) are ordered by z in decreasing order
+ sorted_indices = np.argsort(newz_flapvalues)[::-1]
+ newx_flapvalues = newx_flapvalues[sorted_indices]
+ newz_flapvalues = newz_flapvalues[sorted_indices]
+
+ # Concatenate the transition points with the existing arrays
+ newx_flapvalues = np.concatenate((newx_flapvalues, arc_x))
+ newz_flapvalues = np.concatenate((newz_flapvalues, arc_z))
+
+ # Re-order for correct leading edge in CPACS format
+ min_x_index = np.argmin(newx_flapvalues)
+ newx_flapvalues = np.roll(newx_flapvalues, -min_x_index)
+ newz_flapvalues = np.roll(newz_flapvalues, -min_x_index)
+
+ return newx_values, newz_values, newx_flapvalues, newz_flapvalues
+
+
+def fowler_flap_scale(
+ x_airfoil: ndarray,
+ z_airfoil: ndarray,
+ x_flap: ndarray,
+ z_flap: ndarray,
+) -> Tuple[ndarray, ndarray]:
+ """
+ Transform and rotate the flap airfoil to fit correctly wrt main wing.
+ """
+
+ x_min, x_max = np.min(x_flap), np.max(x_flap)
+ z_min, z_max = np.min(z_flap), np.max(z_flap)
+
+ # Calculate the angle of rotation
+ delta_x = x_max - x_min
+ delta_z = z_max - z_min
+ angle = np.arctan2(delta_z, delta_x)
+
+ rot_matrix = rot(angle)
+
+ airfoil_points = np.vstack((x_airfoil, z_airfoil))
+ rotated_airfoil_points = rot_matrix @ airfoil_points
+
+ x_airfoil_rotated = rotated_airfoil_points[0, :]
+ z_airfoil_rotated = rotated_airfoil_points[1, :]
+
+ # Scale the rotated airfoil points
+ scale = 0.7
+ x_airfoil_scaled = scale * (x_airfoil_rotated - np.min(x_airfoil_rotated)) / \
+ (np.max(x_airfoil_rotated) - np.min(x_airfoil_rotated))
+ z_airfoil_scaled = scale * (z_airfoil_rotated - np.min(z_airfoil_rotated)) / \
+ (np.max(z_airfoil_rotated) - np.min(z_airfoil_rotated))
+
+ # Translate the scaled airfoil points to fit within the domain
+ scale_p = 0.15
+ x_airfoil_transformed = x_min * (1 + scale_p / 4) + (x_max - x_min) * x_airfoil_scaled
+ z_airfoil_transformed = z_min * (1 - scale_p) + (z_max - z_min) * z_airfoil_scaled
+
+ return x_airfoil_transformed, z_airfoil_transformed
+
+
+def transform_airfoil(tixi: Tixi3, sgt: str, ctrltype: str) -> None:
+ """
+ Transform the shape of wing's airfoil at segment sgt,
+ in format ctrltype.
+ """
+ # Define constants
+ wing_xpath = WINGS_XPATH + f"/wing[@uID='{sgt}']"
+ sec = "section"
+ secs_xpath = wing_xpath + f"/{sec}s"
+ secs_cnt = elements_number(tixi, secs_xpath, sec, logg=False)
+
+ ##########################
+ # Modify airfoil's shape #
+ ##########################
+
+ # Retrieve uIDs of section's airfoil
+ for i_sec in range(1, secs_cnt + 1):
+ airfoil_xpath = secs_xpath + f"/{sec}[{i_sec}]/elements/element[1]/airfoilUID"
+ airfoil_uid = tixi.getTextElement(airfoil_xpath)
+ wingairfoil_xpath = AIRFOILS_XPATH + f"/wingAirfoil[@uID='{airfoil_uid}']"
+ if tixi.checkElement(wingairfoil_xpath):
+ pointlist_xpath = wingairfoil_xpath + "/pointList"
+ x = array(get_float_vector(tixi, pointlist_xpath + "/x"))
+ z = array(get_float_vector(tixi, pointlist_xpath + "/z"))
+
+ # TODO: Add choice of different interpolation techniques
+ newx, newz = interpolate_points(x, z, max_dist=0.02)
+ # CAREFUL: if you want to interpolate airfoil coordinates,
+ # you need to apply the interpolation to all airfoils.
+ # newx, newz = x, z
+
+ if "fowler" in ctrltype:
+ # Store airfoil temporarily
+ x_airfoil, z_airfoil = newx, newz / 3.0
+
+ newz, xflap, zflap = fowler_transform(newx, newz, x_ref=0.6)
+ x_airfoil, z_airfoil = fowler_flap_scale(
+ x_airfoil, z_airfoil, xflap, zflap
+ )
+ elif "plain" in ctrltype:
+ newx, newz, x_airfoil, z_airfoil = plain_transform(
+ newx, newz, x_ref=0.7
+ )
+ else:
+ log.warning(f"Control surface {ctrltype} does not exist, or is not implemented.")
+
+ newx_str, newy_str, newz_str = array_to_str(newx, newz)
+ ids = "main_" + ctrltype + "_" + sgt + f'_{i_sec}'
+ new_airfoil_xpath = copy(tixi, wingairfoil_xpath, "wingAirfoil", ids, sym=False)
+ update_xpath_at_xyz(
+ tixi, new_airfoil_xpath + "/pointList", newx_str, newy_str, newz_str
+ )
+
+ # Update airfoil uID for each section
+ tixi.updateTextElement(airfoil_xpath, ids)
+
+ # Add x_airfoil, z_airfoil in wingAirfoils (small flap definition)
+ newx_str, newy_str, newz_str = array_to_str(x_airfoil, z_airfoil)
+ ids = ctrltype + "_" + sgt + f'_{i_sec}'
+ new_airfoil_xpath = copy(tixi, wingairfoil_xpath, "wingAirfoil", ids, sym=False)
+ update_xpath_at_xyz(
+ tixi, new_airfoil_xpath + "/pointList", newx_str, newy_str, newz_str
+ )
+
+ else:
+ log.warning(f"Airfoil uID {airfoil_uid} not found.")
+
+
+def update_xpath_at_xyz(tixi: Tixi3, xpath: str, x: str, y: str, z: str) -> None:
+ """
+ Helper Function.
+ """
+ tixi.updateTextElement(xpath + "/x", x)
+ tixi.updateTextElement(xpath + "/y", y)
+ tixi.updateTextElement(xpath + "/z", z)
+
+
+def createfrom_wing_airfoil(
+ tixi: Tixi3,
+ wingairfoil_xpath: str,
+ xlist: List,
+ zlist: List,
+ ids: str
+) -> None:
+ """
+ Creates a wingAirfoil.
+ """
+
+ newx_str, newy_str, newz_str = array_to_str(xlist, zlist)
+ new_airfoil_xpath = copy(tixi, wingairfoil_xpath, "wingAirfoil", ids, sym=False)
+ update_xpath_at_xyz(
+ tixi, new_airfoil_xpath + "/pointList", newx_str, newy_str, newz_str
+ )
+
+
+def add_airfoil(tixi: Tixi3, sgt: str, ctrltype: str) -> None:
+ """
+ Add the small airfoil flap behind/under the wing.
+
+ Args:
+ tixi (Tixi3): Tixi handle of CPACS file.
+ sgt (str): Segment of wing where we add the CPACS.
+ ctrltype (str): Type of the control surface.
+
+ """
+
+ if "aileron" in ctrltype:
+ sym = False
+ left_ctrltype = "left_" + ctrltype
+
+ # Add both right and left control surfaces
+ adding_airfoil(tixi, "right_" + ctrltype, sgt, sym)
+ adding_airfoil(tixi, left_ctrltype, sgt, sym)
+
+ # Modify the "left_" airfoil to put it on the left
+ wing_uid = left_ctrltype + "_" + sgt
+ wingxpath = WINGS_XPATH + f"/wing[@uID='{wing_uid}']"
+ symmetric_operation(tixi, wingxpath + "/transformation/scaling/y")
+ symmetric_operation(tixi, wingxpath + "/transformation/translation/y")
+
+ elif "rudder" in ctrltype:
+ adding_airfoil(tixi, ctrltype, sgt, sym=False)
+ else:
+ adding_airfoil(tixi, ctrltype, sgt, sym=True)
+
+
+def adding_airfoil(
+ tixi: Tixi3,
+ ctrltype: str,
+ sgt: str,
+ sym: bool
+) -> None:
+ # Define constants
+ wing_xpath = WINGS_XPATH + f"/wing[@uID='{sgt}']"
+ flap_xpath = copy(tixi, wing_xpath, "wing", ctrltype + "_" + sgt, sym=sym)
+ sec = "section"
+ secs_xpath = flap_xpath + f"/{sec}s"
+ secs_cnt = elements_number(tixi, secs_xpath, sec, logg=False)
+
+ #
+ # Retrieve uIDs of section's airfoil
+ for i_sec in range(1, secs_cnt + 1):
+ # Ensure ctrltype does not contain "left_" or "right_"
+ ctrltype = ctrltype.replace("left_", "").replace("right_", "")
+
+ ele_xpath = secs_xpath + f"/{sec}[{i_sec}]/elements/element[1]"
+ ids = ctrltype + "_" + sgt + f'_{i_sec}'
+ tixi.updateTextElement(ele_xpath + "/airfoilUID", ids)
+
+ # Scale flap accordingly
+ sign = 1 if i_sec % 2 != 0 else -1
+ tixi.updateTextElement(ele_xpath + "/transformation/translation/y", str(0.1 * sign))
+
+
+def deflection_angle(tixi: Tixi3, wing_uid: str, angle: float) -> None:
+ # Define constants
+ wing_xpath = WINGS_XPATH + f"/wing[@uID='{wing_uid}']"
+ sec = "section"
+ secs_xpath = wing_xpath + f"/{sec}s"
+ secs_cnt = elements_number(tixi, secs_xpath, sec, logg=False)
+
+ ##########################
+ # Modify airfoil's shape #
+ ##########################
+
+ # Retrieve uIDs of section's airfoil
+ for i_sec in range(1, secs_cnt + 1):
+ airfoil_xpath = secs_xpath + f"/{sec}[{i_sec}]/elements/element[1]/airfoilUID"
+
+ # Modify wingAirfoil accordingly
+ if angle != 0.0:
+ airfoil_uid = tixi.getTextElement(airfoil_xpath)
+ wingairfoil_xpath = AIRFOILS_XPATH + f"/wingAirfoil[@uID='{airfoil_uid}']"
+ if tixi.checkElement(wingairfoil_xpath):
+ pointlist_xpath = wingairfoil_xpath + "/pointList"
+ x_list = get_float_vector(tixi, pointlist_xpath + "/x")
+ z_list = get_float_vector(tixi, pointlist_xpath + "/z")
+
+ # Find the point with the smallest x
+ min_x_index = x_list.index(min(x_list))
+ center_point = (x_list[min_x_index], z_list[min_x_index])
+
+ # Apply rotation to all points
+ rotated_points = [
+ rotate_2d_point((x, z), center_point, angle)
+ for x, z in zip(x_list, z_list)
+ ]
+
+ # Separate rotated points back into x and z lists
+ x_list_rotated, z_list_rotated = zip(*rotated_points)
+
+ newx_str, newy_str, newz_str = array_to_str(x_list_rotated, z_list_rotated)
+
+ ids = wing_uid + f"angle_{angle}" + f'_{i_sec}'
+ new_airfoil_xpath = copy(tixi, wingairfoil_xpath, "wingAirfoil", ids, sym=False)
+ update_xpath_at_xyz(
+ tixi, new_airfoil_xpath + "/pointList", newx_str, newy_str, newz_str
+ )
+
+ # Update airfoil uID for each section
+ tixi.updateTextElement(airfoil_xpath, ids)
+
+ # Re-use previously defined airfoil
+ else:
+ ids = wing_uid + f'_{i_sec}'
+ tixi.updateTextElement(airfoil_xpath, ids)
+
+
+def add_control_surfaces(tixi: Tixi3) -> None:
+ """
+ Adds control surfaces for each wings in CPACS file.
+ """
+
+ ctrlsurf = retrieve_gui_ctrlsurf(tixi)
+ log.info(f"Modifying {ctrlsurf}.")
+
+ if ctrlsurf:
+ for wing_name, wing_data in ctrlsurf.items():
+ decompose_wing(tixi, wing_name)
+ for (sgt, ctrltype) in wing_data:
+
+ # Transform and scale original airfoil
+ transform_airfoil(tixi, sgt, ctrltype)
+
+ # Add small airfoil that will act as a control surface
+ add_airfoil(tixi, sgt, ctrltype)
+
+ # Test deflection function
+ # deflection_angle(tixi, ctrltype + "_" + sgt, angle=-20.0)
+ # deflection_angle(tixi, "right_" + ctrltype + "_" + sgt, angle=20.0)
+ # deflection_angle(tixi, "left_" + ctrltype + "_" + sgt, angle=-20.0)
+
+ log.info("Finished adding control surfaces.")
+ else:
+ log.warning("No control surfaces to add.")
+
+
+# ==============================================================================
+# MAIN
+# ==============================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/CPACSUpdater/func/utils.py b/ceasiompy/CPACSUpdater/func/utils.py
new file mode 100644
index 000000000..1571e5c14
--- /dev/null
+++ b/ceasiompy/CPACSUpdater/func/utils.py
@@ -0,0 +1,411 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Utils for CPACSUpdater.
+
+
+| Author: Leon Deligny
+| Creation: 2025-Feb-26
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+import os
+import math
+import matplotlib
+
+import numpy as np
+import matplotlib.pyplot as plt
+
+from numpy import array
+
+from numpy import ndarray
+from typing import Optional
+from tixi3.tixi3wrapper import Tixi3
+
+from typing import (
+ List,
+ Tuple,
+)
+
+from ceasiompy import log
+
+# =================================================================================================
+# BACKEND SETTING
+# =================================================================================================
+
+if os.environ.get('DISPLAY', '') == '':
+ matplotlib.use('Agg')
+else:
+ matplotlib.use('TkAgg')
+
+# ==============================================================================
+# FUNCTIONS
+# ==============================================================================
+
+
+def copy_children(tixi: Tixi3, source_xpath: str, target_xpath: str, copy_id: str) -> None:
+ """
+ Modifies tixi handle of CPACS file.
+ Copies all childrens of attribute at source_xpath to target_xpath.
+ Recursive call until there are no more childrens.
+
+ Args:
+ tixi (Tixi3): Tixi handle of CPACS file.
+ source_xpath (str): xPath of attribute to copy.
+ target_xpath (str): xPath to copy's destination.
+ copy_id (str): Copy identifier to modify the uIDs in the CPACS.
+
+ """
+ # Get the number of children at the source XPath
+ num_children = tixi.getNumberOfChilds(source_xpath)
+
+ for i in range(1, num_children + 1):
+ (
+ child_name, child_xpath, num_same_name_children
+ ) = get_same_child(tixi, source_xpath, i, num_children)
+
+ if num_same_name_children > 1:
+ child_xpath += f"[{i}]"
+
+ if (child_name != "#text") and (child_name != "#comment"):
+ # Create the child element at the target XPath
+ new_child_xpath = f"{target_xpath}/{child_name}"
+ if num_same_name_children > 1:
+ new_child_xpath += f"[{i}]"
+
+ tixi.createElement(target_xpath, child_name)
+
+ # Check if the child element exists before copying attributes
+ if tixi.checkElement(child_xpath):
+
+ # Copy the attributes of the child element
+ num_attributes = tixi.getNumberOfAttributes(child_xpath)
+ for j in range(1, num_attributes + 1):
+ attr_name = tixi.getAttributeName(child_xpath, j)
+ attr_value = tixi.getTextAttribute(child_xpath, attr_name)
+ if attr_name == "uID":
+ attr_value += copy_id
+
+ tixi.addTextAttribute(new_child_xpath, attr_name, attr_value)
+
+ # Recursively copy the children of the child element
+ copy_children(tixi, child_xpath, new_child_xpath, copy_id)
+ elif (child_name == "#text"):
+ # Copy the text value if the child is a text node
+ text_value = tixi.getTextElement(source_xpath)
+ tixi.updateTextElement(target_xpath, text_value)
+
+
+def array_to_str(x: ndarray, z: ndarray) -> Tuple[str, str, str]:
+ length = len(x)
+ y = np.zeros(length)
+
+ # Convert back to strings
+ x_str = ';'.join(map(str, x))
+ y_str = ';'.join(map(str, y))
+ z_str = ';'.join(map(str, z))
+
+ return x_str, y_str, z_str
+
+
+def copy(tixi: Tixi3, xpath: str, copy_name: str, ids: str, sym: bool = True) -> str:
+ """
+ Copies an element and its children from a given xpath
+ to a new element with the specified copy_name.
+
+ Args:
+ tixi (Tixi3): Tixi handle of CPACS file.
+ xpath (str): xPath of the element to copy.
+ copy_name (str): Name for the new copied element.
+
+ Returns:
+ (str): New xPath to copied element.
+
+ """
+ # Create the new element at the same level as the source element
+ parent_xpath = xpath.rsplit('/', 1)[0]
+ new_xpath = f"{parent_xpath}/{ids}"
+ tixi.createElement(parent_xpath, ids)
+
+ # Copy the children of the source element to the new element
+ copy_children(tixi, xpath, new_xpath, '_' + ids)
+
+ update_uids(tixi, new_xpath, '_' + ids)
+
+ # Modify name and description
+ modify_element(tixi, new_xpath + "/name", ids)
+ modify_element(tixi, new_xpath + "/description", ids)
+
+ if sym:
+ modify_attribute(tixi, new_xpath, "symmetry", "x-z-plane")
+ modify_attribute(tixi, new_xpath, "uID", ids)
+
+ if copy_name == "wing":
+ modify_attribute(tixi, new_xpath, "xsi:type", "wingType")
+
+ parent_xpath = new_xpath.rsplit('/', 1)[0]
+
+ tixi.renameElement(parent_xpath, ids, copy_name)
+
+ ret_xpath = parent_xpath + f"/{copy_name}[@uID='{ids}']"
+
+ return ret_xpath
+
+
+def symmetric_operation(tixi: Tixi3, xpath: str) -> None:
+ """
+ Replaces value x to -x at specified xPath.
+ Symmetry operation centered at 0.
+ """
+ nb = float(tixi.getTextElement(xpath))
+ nb = -nb
+ tixi.updateTextElement(xpath, str(nb))
+
+
+def symmetry(tixi: Tixi3, xpath: str) -> None:
+ """
+ Apply symmetry transformation to all children of the given xpath.
+ Specifically, for any child element named "y", multiply its value by -1.
+
+ Args:
+ tixi (Tixi3): Tixi handle of CPACS file.
+ xpath (str): xPath to the parent element.
+
+ """
+ # Get the number of children at the given XPath
+ num_children = tixi.getNumberOfChilds(xpath)
+
+ for i in range(1, num_children + 1):
+ (
+ child_name, child_xpath, num_same_name_children
+ ) = get_same_child(tixi, xpath, i, num_children)
+
+ if num_same_name_children > 1:
+ child_xpath += f"[{i}]"
+
+ if child_name == "y":
+ # Apply symmetry transformation to the "y" element
+ y_value_str = tixi.getTextElement(child_xpath)
+ y_value = float(y_value_str)
+ y_value = -y_value
+ tixi.updateTextElement(child_xpath, str(y_value))
+
+ if (child_name != "#text") and (child_name != "#comment"):
+ # Recursively apply symmetry to the child's children
+ symmetry(tixi, child_xpath)
+
+
+def update_uid_element(tixi: Tixi3, xpath: str, element_name: str, uids_identifier: str) -> None:
+ """
+ Helper function to update a specific UID element if it exists.
+
+ Args:
+ tixi (Tixi3): Tixi handle of CPACS file.
+ xpath (str): xPath to the element.
+ element_name (str): Name of the UID element to update.
+ uids_identifier (str): uID identifier to add.
+
+ """
+ element_xpath = f"{xpath}/{element_name}"
+ if tixi.checkElement(element_xpath):
+ uid_value = tixi.getTextElement(element_xpath)
+ if uid_value:
+ modify_element(tixi, element_xpath, uid_value + uids_identifier)
+
+
+def update_uids(tixi: Tixi3, xpath: str, uids_identifier: str) -> None:
+ """
+ Recursively update the specified elements with uids_identifier suffix.
+
+ Args:
+ tixi (Tixi3): Tixi handle of CPACS file.
+ xpath (str): xPath to start the search.
+ uids_identifier (str): uID identifier to add.
+
+ """
+
+ num_children = tixi.getNumberOfChilds(xpath)
+ for i in range(1, num_children + 1):
+ (
+ child_name, child_xpath, num_same_name_children
+ ) = get_same_child(tixi, xpath, i, num_children)
+
+ if num_same_name_children > 1:
+ child_xpath += f"[{i}]"
+
+ if child_name not in ["#text", "#comment"]:
+ if tixi.checkElement(child_xpath):
+ uid_elements = [
+ "fromSectionUID", "toSectionUID", "fromElementUID",
+ "toElementUID", "parentUID", "referenceUID"
+ ]
+ for element in uid_elements:
+ update_uid_element(tixi, child_xpath, element, uids_identifier)
+
+ update_uids(tixi, child_xpath, uids_identifier)
+
+
+def get_same_child(tixi: Tixi3, xpath: str, i: int, num_children: int) -> Tuple[str, str, int]:
+ child_name = tixi.getChildNodeName(xpath, i)
+ child_xpath = f"{xpath}/{child_name}"
+ num_same_name_children = sum(1 for j in range(
+ 1, num_children + 1) if tixi.getChildNodeName(xpath, j) == child_name)
+ return child_name, child_xpath, num_same_name_children
+
+
+def remove(tixi: Tixi3, xpath: str, attr_name: Optional[str] = None) -> None:
+ """
+ Remove element/attribute and its childrens at specified xpath.
+ """
+ try:
+ if tixi.checkElement(xpath):
+ tixi.removeElement(xpath)
+ elif attr_name is not None:
+ if tixi.checkAttribute(xpath, attr_name):
+ tixi.removeAttribute(xpath, attr_name)
+ log.warning(f"Attribute {attr_name} not found at '{xpath}'.")
+ else:
+ log.warning(f"No elements found at '{xpath}'.")
+
+ except Exception as e:
+ log.warning(f"An error occurred while removing the element at '{xpath}': {e}.")
+
+
+def modify_element(tixi: Tixi3, xpath: str, new_value: str) -> None:
+ try:
+ if tixi.checkElement(xpath):
+ tixi.updateTextElement(xpath, new_value)
+ else:
+ raise ValueError(f"Element at xpath '{xpath}' not found.")
+ except Exception as e:
+ log.warning(f"An error occurred while modifying an element in the CPACS file: {e}")
+
+
+def modify_attribute(tixi: Tixi3, xpath: str, attribute: str, new_value: str) -> None:
+ try:
+ if tixi.checkElement(xpath):
+ tixi.addTextAttribute(xpath, attribute, new_value)
+ else:
+ raise ValueError(f"Element at xpath '{xpath}' not found.")
+ except Exception as e:
+ log.warning(f"An error occurred while modifying an attribute in the CPACS file: {e}")
+
+
+def interpolate(
+ x1: float,
+ z1: float,
+ x2: float,
+ z2: float,
+ max_dist: float
+) -> List[Tuple[float, float]]:
+
+ distance = math.sqrt((x2 - x1) ** 2 + (z2 - z1) ** 2)
+ if distance > max_dist:
+ mid_x = (x1 + x2) / 2
+ mid_z = (z1 + z2) / 2
+ return interpolate(x1, z1, mid_x, mid_z, max_dist) + \
+ [(mid_x, mid_z)] + interpolate(mid_x, mid_z, x2, z2, max_dist)
+ else:
+ return [(x2, z2)]
+
+
+def interpolate_points(
+ x: ndarray,
+ z: ndarray,
+ max_dist: float,
+) -> Tuple[ndarray, ndarray]:
+ """
+ Interpolate points through
+
+ """
+ new_x = [x[0]]
+ new_z = [z[0]]
+
+ for (x1, z1), (x2, z2) in zip(zip(x[:-1], z[:-1]), zip(x[1:], z[1:])):
+ interpolated_points = interpolate(x1, z1, x2, z2, max_dist)
+ for x_, z_ in interpolated_points:
+ new_x.append(x_)
+ new_z.append(z_)
+
+ return array(new_x), array(new_z)
+
+
+def find_max_x(x: ndarray, z: ndarray) -> Tuple[float, float, float, float]:
+ """
+ Finds the biggest x_value such that z is positive/negative.
+ """
+ pos_mask = z > 0
+ neg_mask = z < 0
+
+ if np.any(pos_mask):
+ max_x_pos = np.max(x[pos_mask])
+ z_pos = z[pos_mask][np.argmax(x[pos_mask])]
+ else:
+ max_x_pos, z_pos = None, None
+
+ if np.any(neg_mask):
+ max_x_neg = np.max(x[neg_mask])
+ z_neg = z[neg_mask][np.argmax(x[neg_mask])]
+ else:
+ max_x_neg, z_neg = None, None
+
+ return max_x_pos, z_pos, max_x_neg, z_neg
+
+
+def find_min_x(x: ndarray, z: ndarray) -> Tuple[float, float, float, float]:
+ """
+ Finds the smallest x_value such that z is positive/negative.
+ """
+ pos_mask = z > 0
+ neg_mask = z < 0
+
+ if np.any(pos_mask):
+ min_x_pos = np.min(x[pos_mask])
+ z_pos = z[pos_mask][np.argmin(x[pos_mask])]
+ else:
+ min_x_pos, z_pos = None, None
+
+ if np.any(neg_mask):
+ min_x_neg = np.min(x[neg_mask])
+ z_neg = z[neg_mask][np.argmin(x[neg_mask])]
+ else:
+ min_x_neg, z_neg = None, None
+
+ return min_x_pos, z_pos, min_x_neg, z_neg
+
+
+def plot_values(
+ x: ndarray,
+ z: ndarray,
+ x_flap: ndarray = None,
+ z_flap: ndarray = None,
+) -> None:
+ plt.plot(x, z, marker='o', label='X vs Z values')
+
+ if x_flap is not None and z_flap is not None:
+ plt.plot(
+ x_flap,
+ z_flap,
+ marker='x',
+ label='X Flap vs Z Flap values'
+ )
+
+ plt.xlabel('X values')
+ plt.ylabel('Z values')
+ plt.title('Plot of X vs Z values')
+ plt.legend()
+ plt.grid(True)
+ plt.show()
+
+
+# ==============================================================================
+# MAIN
+# ==============================================================================
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/Database/README.md b/ceasiompy/Database/README.md
new file mode 100644
index 000000000..0dd9fa087
--- /dev/null
+++ b/ceasiompy/Database/README.md
@@ -0,0 +1,33 @@
+
+
+
+# Database
+
+**Categories:** Database, Data
+
+**State**: :heavy_check_mark:
+
+## Inputs
+
+Needs a workflow to be generated and done.
+
+## Analyses
+
+Analyses nothing. It stores automatically the "important" data in a preSQL database.
+
+## Outputs
+
+Outputs nothing.
+
+## Installation or requirements
+
+Database is a native CEASIOMpy module.
+
+## Limitations
+
+- Exists only one table per module data.
+- Not compatible with Weight and Balance modules of CEASIOMpy.
+
+## More information
+
+NA
\ No newline at end of file
diff --git a/ceasiompy/Database/__init__.py b/ceasiompy/Database/__init__.py
new file mode 100644
index 000000000..710c94df7
--- /dev/null
+++ b/ceasiompy/Database/__init__.py
@@ -0,0 +1,44 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for Database module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+from ceasiompy import log
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = True
+
+# ===== Include GUI =====
+include_gui = True
+
+# ===== Add a Results Directory =====
+RES_DIR = False
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/Database/__specs__.py b/ceasiompy/Database/__specs__.py
new file mode 100644
index 000000000..6ac584def
--- /dev/null
+++ b/ceasiompy/Database/__specs__.py
@@ -0,0 +1,51 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+GUI Interface of Database.
+
+
+| Author: Leon Deligny
+| Creation: 25 March 2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from ceasiompy.utils.moduleinterfaces import CPACSInOut
+
+from ceasiompy import log
+from ceasiompy.Database import include_gui
+from ceasiompy.utils.commonxpath import DATABASE_STOREDATA_XPATH
+
+# ==============================================================================
+# VARIABLE
+# ==============================================================================
+
+# ===== CPACS inputs and outputs =====
+cpacs_inout = CPACSInOut()
+
+# ==============================================================================
+# GUI INPUTS
+# ==============================================================================
+
+cpacs_inout.add_input(
+ var_name="store",
+ var_type=bool,
+ default_value=True,
+ unit=None,
+ xpath=DATABASE_STOREDATA_XPATH,
+ gui=include_gui,
+ gui_name="Store data",
+ gui_group="Storing Settings",
+)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/Database/database.py b/ceasiompy/Database/database.py
new file mode 100644
index 000000000..7b0627261
--- /dev/null
+++ b/ceasiompy/Database/database.py
@@ -0,0 +1,46 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Main scripts for Database module.
+
+
+| Author: Leon Deligny
+| Creation: 03-Mar-2025
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+from cpacspy.cpacsfunctions import get_value
+from ceasiompy.utils.ceasiompyutils import bool_
+from ceasiompy.Database.func.storing import store_data
+
+from cpacspy.cpacspy import CPACS
+
+from ceasiompy import log
+from ceasiompy.utils.commonxpath import DATABASE_STOREDATA_XPATH
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+def main(cpacs: CPACS) -> None:
+ tixi = cpacs.tixi
+
+ # Check if we store data
+ if bool_(get_value(tixi, DATABASE_STOREDATA_XPATH)):
+ store_data(tixi)
+ else:
+ log.info("Did not call database.")
+
+
+if __name__ == "__main__":
+ # If you want to test your new functions
+ # or classes go in the tests folder of this module.
+ # You will add data to ceasiompy.db if you run this module.
+ log.info("Nothing to execute.")
diff --git a/ceasiompy/Database/func/__init__.py b/ceasiompy/Database/func/__init__.py
new file mode 100644
index 000000000..0898aacf7
--- /dev/null
+++ b/ceasiompy/Database/func/__init__.py
@@ -0,0 +1,152 @@
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from ceasiompy import log
+from ceasiompy.PyAVL import MODULE_NAME as PYAVL_NAME
+# from ceasiompy.SU2Run import MODULE_NAME as SU2RUN_NAME
+from ceasiompy.CPACS2GMSH import MODULE_NAME as CPACS2GMSH_NAME
+from ceasiompy.DynamicStability import MODULE_NAME as DYNSTAB_NAME
+
+# ==============================================================================
+# CONSTANTS
+# ==============================================================================
+
+# Tables structure of ceasiompy.db
+TABLE_DICT = {
+ f"{PYAVL_NAME}": [
+ "avl_data",
+ """
+ aircraft TEXT,
+
+ alt REAL,
+ mach REAL,
+ alpha REAL,
+ beta REAL,
+
+ pb_2V REAL,
+ qc_2V REAL,
+ rb_2V REAL,
+
+ flap REAL,
+ aileron REAL,
+ elevator REAL,
+ rudder REAL,
+
+ xref REAL,
+ yref REAL,
+ zref REAL,
+
+ cd REAL,
+ cs REAL,
+ cl REAL,
+
+ cmd REAL,
+ cms REAL,
+ cml REAL,
+
+ cmd_a REAL,
+ cms_a REAL,
+ cml_a REAL,
+
+ cmd_b REAL,
+ cms_b REAL,
+ cml_b REAL,
+ """
+ ],
+ f"{CPACS2GMSH_NAME}": [
+ "gmsh_data",
+ """
+ aircraft TEXT,
+ deformation TEXT,
+ angle REAL,
+ su2_file_data BLOB,
+ """
+ ],
+ f"{DYNSTAB_NAME}": [
+ "derivatives_data",
+ """
+ aircraft TEXT,
+
+ method TEXT,
+
+ chord INT,
+ span INT,
+
+ mach REAL,
+
+ x_ref REAL,
+ y_ref REAL,
+ z_ref REAL,
+
+ cm_alphaprim REAL,
+ cz_alphaprim REAL,
+ cx_alphaprim REAL,
+
+ cy_betaprim REAL,
+ cl_betaprim REAL,
+ cn_betaprim REAL,
+ """
+ ],
+}
+
+ALLOWED_TABLES = [value[0] for value in TABLE_DICT.values()]
+
+TABLE_TO_MODULE = {item[0]: key for key, item in TABLE_DICT.items()}
+
+ALLOWED_COLUMNS = {
+ table: [
+ line.strip().split()[0] # Extract column names from the SQL-like definitions
+ for line in TABLE_DICT[TABLE_TO_MODULE[table]][1].splitlines()
+ if line.strip() and not line.strip().startswith("--") # Ignore empty lines and comments
+ ]
+ for table in ALLOWED_TABLES
+}
+
+# How to extract aerodynamic coefficients in st.txt
+PYAVL_ST = {
+ "Alpha": (1, "alpha"),
+ "Beta": (1, "beta"),
+ "Mach": (1, "mach"),
+
+ "pb/2V": (2, "pb_2V"),
+ "qc/2V": (2, "qc_2V"),
+ "rb/2V": (2, "rb_2V"),
+
+ "flap": (1, "flap"),
+ "aileron": (1, "aileron"),
+ "elevator": (1, "elevator"),
+ "rudder": (1, "rudder"),
+
+ "Xref": (1, "xref"),
+ "Yref": (2, "yref"),
+ "Zref": (3, "zref"),
+
+ "CDtot": (1, "cd"),
+ "CYtot": (1, "cs"),
+ "CLtot": (1, "cl"),
+
+ "Cltot": (2, "cmd"),
+ "Cmtot": (2, "cms"),
+ "Cntot": (2, "cml"),
+
+ "Cla": (1, "cmd_a"),
+ "Cma": (1, "cms_a"),
+ "Cna": (1, "cml_a"),
+
+ "Clb": (2, "cmd_b"),
+ "Cmb": (2, "cms_b"),
+ "Cnb": (2, "cml_b"),
+}
+
+# pyavl.py
+PYAVL_CTRLSURF = ["flap", "aileron", "elevator", "rudder"]
+
+
+# ==============================================================================
+# MAIN
+# ==============================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/Database/func/cpacs2gmsh.py b/ceasiompy/Database/func/cpacs2gmsh.py
new file mode 100644
index 000000000..fb7085849
--- /dev/null
+++ b/ceasiompy/Database/func/cpacs2gmsh.py
@@ -0,0 +1,86 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Store data from workflow modules.
+
+
+| Author: Leon Deligny
+| Creation: 2025-Mar-03
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from ceasiompy.Database.func.utils import data_to_db
+from ceasiompy.utils.ceasiompyutils import aircraft_name
+
+from pathlib import Path
+from sqlite3 import Cursor
+from tixi3.tixi3wrapper import Tixi3
+
+from ceasiompy import log
+
+# ==============================================================================
+# FUNCTIONS
+# ==============================================================================
+
+
+def store_cpacs2gmsh_data(
+ cursor: Cursor,
+ wkdir: Path,
+ tixi: Tixi3,
+ table_name: str,
+) -> None:
+ """
+ Store data from Results/GMSH.
+
+ Args:
+ cursor (Cursor): gmsh_data table from 'ceasiompy.db' cursor.
+ wkdir (Path): Results/GMSH directory.
+ tixi (Tixi3): Tixi handle of CPACS file.
+
+ """
+ table_name = "gmsh_data"
+ files_list = list(wkdir.glob("*.su2"))
+
+ name = str(aircraft_name(tixi))
+
+ for file in sorted(files_list):
+ file_name = str(file.name)
+
+ if "_" in str(file_name):
+ deformation: str = file_name.split("_")[1]
+ angle = float(file_name.split("_")[2].replace(".su2", ""))
+ else:
+ deformation: str = "no_deformation"
+ angle: float = 0.0
+
+ log.info(
+ f"Storing file {file_name}, "
+ f"aircraft={name}, "
+ f"deformation={deformation}, "
+ f"angle={angle}."
+ )
+
+ with open(file, 'rb') as file:
+ file_data = file.read()
+ data = {
+ "su2_file_data": file_data,
+ "aircraft": name,
+ "deformation": deformation,
+ "angle": angle,
+ }
+
+ data_to_db(cursor, data, table_name)
+
+# ==============================================================================
+# MAIN
+# ==============================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/Database/func/dynamicstability.py b/ceasiompy/Database/func/dynamicstability.py
new file mode 100644
index 000000000..c00dfcd6b
--- /dev/null
+++ b/ceasiompy/Database/func/dynamicstability.py
@@ -0,0 +1,107 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Store data from workflow modules.
+
+
+| Author: Leon Deligny
+| Creation: 10-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+import pandas as pd
+
+from cpacspy.cpacsfunctions import get_value
+from ceasiompy.Database.func.utils import data_to_db
+from ceasiompy.utils.ceasiompyutils import aircraft_name
+
+from pathlib import Path
+from sqlite3 import Cursor
+from tixi3.tixi3wrapper import Tixi3
+
+from ceasiompy import log
+
+from ceasiompy.utils.commonxpath import (
+ DYNAMICSTABILITY_NSPANWISE_XPATH,
+ DYNAMICSTABILITY_NCHORDWISE_XPATH,
+)
+
+# ==============================================================================
+# FUNCTIONS
+# ==============================================================================
+
+
+def store_dynstab_data(
+ cursor: Cursor,
+ wkdir: Path,
+ tixi: Tixi3,
+ table_name: str,
+) -> None:
+ """
+ Store data from Results/DynamicStability.
+
+ Args:
+ cursor (Cursor): avl_data table from 'ceasiompy.db' cursor.
+ wkdir (Path): Results/PyAVL directory.
+ tixi (Tixi3): Tixi handle of CPACS file.
+
+ Raises:
+ FileNotFoundError: If no Force files are found.
+
+ """
+ table_name = "derivatives_data"
+ name = str(aircraft_name(tixi))
+
+ # Append data to it
+ data = {}
+
+ # Add aircraft name
+ data["aircraft"] = name
+
+ # Define the path to the SDSA_input.xml file
+ csv = wkdir / "alpha_beta_dot_derivatives.csv"
+
+ # For sure is DLM Method
+ if csv.exists():
+ # Load the CSV file into a DataFrame
+ df = pd.read_csv(csv)
+
+ chord = get_value(tixi, DYNAMICSTABILITY_NCHORDWISE_XPATH)
+ span = get_value(tixi, DYNAMICSTABILITY_NSPANWISE_XPATH)
+ method = "DLM"
+
+ # Iterate over the rows of the DataFrame
+ for _, row in df.iterrows():
+ # Populate the data dictionary with the values from the DataFrame row
+ data = {
+ "aircraft": name,
+ "method": method,
+ "chord": chord,
+ "span": span,
+ "mach": row['mach'],
+ "x_ref": row['x_ref'],
+ "y_ref": row['y_ref'],
+ "z_ref": row['z_ref'],
+ "cm_alphaprim": row['cm_alphaprim'],
+ "cz_alphaprim": row['cz_alphaprim'],
+ "cx_alphaprim": row['cx_alphaprim'],
+ "cy_betaprim": row['cy_betaprim'],
+ "cl_betaprim": row['cl_betaprim'],
+ "cn_betaprim": row['cn_betaprim'],
+ }
+
+ data_to_db(cursor, data, table_name)
+
+# ==============================================================================
+# MAIN
+# ==============================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/Database/func/pyavl.py b/ceasiompy/Database/func/pyavl.py
new file mode 100644
index 000000000..810e3c079
--- /dev/null
+++ b/ceasiompy/Database/func/pyavl.py
@@ -0,0 +1,136 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Store data from workflow modules.
+
+
+| Author: Leon Deligny
+| Creation: 2025-Mar-03
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from ceasiompy.utils.ceasiompyutils import aircraft_name
+
+from ceasiompy.Database.func.utils import (
+ data_to_db,
+ split_line,
+)
+
+from typing import Dict
+from pathlib import Path
+from sqlite3 import Cursor
+from tixi3.tixi3wrapper import Tixi3
+
+from ceasiompy import log
+from ceasiompy.Database.func import (
+ PYAVL_ST,
+ PYAVL_CTRLSURF,
+)
+
+
+# ==============================================================================
+# FUNCTIONS
+# ==============================================================================
+
+
+def store_results(
+ results: Dict,
+ key: str,
+ line: str,
+ index: int,
+ var_name: str,
+) -> None:
+ parts = line.split('=')
+
+ # Handle special case for keys like "Clb" and "Cnb"
+ if key in ["Clb", "Cnb"]:
+ if len(parts) > 2:
+ results[var_name] = split_line(line, index)
+
+ # Handle control surface keys or general case
+ elif key in PYAVL_CTRLSURF:
+ if len(parts) == 2:
+ results[var_name] = split_line(line, index)
+
+ # General case for other keys
+ else:
+ results[var_name] = split_line(line, index)
+
+
+def get_avl_data(force_file: Path) -> Dict:
+ """
+ Get aerodynamic coefficients and velocity
+ from AVL total forces file (sb.txt).
+ """
+
+ results = {var_name: None for _, var_name in PYAVL_ST.values()}
+
+ with open(force_file) as f:
+ for line in f.readlines():
+ for key, (index, var_name) in PYAVL_ST.items():
+ if key in line:
+ store_results(
+ results,
+ key,
+ line,
+ index,
+ var_name,
+ )
+ return results
+
+
+def store_pyavl_data(
+ cursor: Cursor,
+ wkdir: Path,
+ tixi: Tixi3,
+ table_name: str,
+) -> None:
+ """
+ Store data from Results/PyAVL.
+
+ Args:
+ cursor (Cursor): avl_data table from 'ceasiompy.db' cursor.
+ wkdir (Path): Results/PyAVL directory.
+ tixi (Tixi3): Tixi handle of CPACS file.
+
+ Raises:
+ FileNotFoundError: If no Force files are found.
+
+ """
+
+ case_dir_list = [case_dir for case_dir in wkdir.iterdir() if "Case" in case_dir.name]
+ txt_file_name = "st.txt"
+ name = str(aircraft_name(tixi))
+
+ for config_dir in sorted(case_dir_list):
+ # Checks if config_dir is a directory
+ if not config_dir.is_dir():
+ continue
+
+ alt = float(config_dir.name.split("_")[1].split("alt")[1])
+ file_path = Path(config_dir, txt_file_name)
+
+ if not file_path.exists():
+ raise FileNotFoundError(
+ f"No result total forces '{txt_file_name}' file have been found!")
+
+ # Append data to it
+ data = get_avl_data(file_path)
+ data["aircraft"] = name
+ data["alt"] = alt
+
+ data_to_db(cursor, data, table_name)
+
+# ==============================================================================
+# MAIN
+# ==============================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/Database/func/storing.py b/ceasiompy/Database/func/storing.py
new file mode 100644
index 000000000..5ed4b52ce
--- /dev/null
+++ b/ceasiompy/Database/func/storing.py
@@ -0,0 +1,216 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Store data from workflow modules.
+
+
+| Author: Leon Deligny
+| Creation: 2025-Mar-03
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from sqlite3 import connect
+from ceasiompy.Database.func.utils import create_db
+from ceasiompy.Database.func.pyavl import store_pyavl_data
+from ceasiompy.Database.func.su2run import store_su2run_data
+from ceasiompy.utils.ceasiompyutils import get_results_directory
+from ceasiompy.Database.func.cpacs2gmsh import store_cpacs2gmsh_data
+from ceasiompy.Database.func.dynamicstability import store_dynstab_data
+
+from pathlib import Path
+from sqlite3 import Cursor
+from sqlite3 import Connection
+from tixi3.tixi3wrapper import Tixi3
+
+from typing import (
+ List,
+ Tuple,
+ Callable,
+)
+
+from ceasiompy import log
+from ceasiompy.PyAVL import MODULE_NAME as PYAVL_NAME
+from ceasiompy.SU2Run import MODULE_NAME as SU2RUN_NAME
+from ceasiompy.utils.commonpaths import CEASIOMPY_DB_PATH
+from ceasiompy.CPACS2GMSH import MODULE_NAME as CPACS2GMSH_NAME
+from ceasiompy.DynamicStability import MODULE_NAME as DYNSTAB_NAME
+
+from ceasiompy.Database.func import TABLE_DICT, ALLOWED_TABLES, ALLOWED_COLUMNS
+
+# ==============================================================================
+# CLASS
+# ==============================================================================
+
+
+class CeasiompyDb:
+ """ceasiompy.db class manager"""
+
+ # Load constants
+ table_dict = TABLE_DICT
+
+ def __init__(self: 'CeasiompyDb', db_path: Path = CEASIOMPY_DB_PATH) -> None:
+ # Initialize constants
+ self.db_path = db_path
+ self.db_name = self.db_path.name
+
+ # Create db if not exists
+ if not self.db_path.exists():
+ create_db(self.db_path)
+
+ self.connection: Connection = connect(self.db_path)
+ self.cursor: Cursor = self.connection.cursor()
+
+ log.info(
+ f"Connecting to database {self.db_name} at path {self.db_path}")
+
+ def connect_to_table(self, module_name: str) -> str:
+ table_name, table_schema = self.get_table_parameters(module_name)
+ # Codacy: Table and column names are strictly validated against whitelisted values.
+ # Table names are validated against ALLOWED_TABLES.
+
+ # Validate table name
+ if table_name not in ALLOWED_TABLES:
+ raise ValueError(f"Invalid table name: {table_name}")
+
+ create_table_query = f"""
+ CREATE TABLE IF NOT EXISTS {table_name} (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ {table_schema}
+ timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ )
+ """
+
+ self.cursor.execute(create_table_query)
+ self.commit()
+
+ return table_name
+
+ def get_table_name(self, module_name: str) -> str:
+ return self.table_dict[module_name][0]
+
+ def get_table_schema(self, module_name: str) -> str:
+ return self.table_dict[module_name][1]
+
+ def get_table_parameters(self, module_name: str) -> Tuple[str, str]:
+ return self.get_table_name(module_name), self.get_table_schema(module_name)
+
+ def commit(self) -> None:
+ self.connection.commit()
+
+ def close(self) -> None:
+ log.info(f"Closing connection to database {self.db_name}.")
+ self.connection.close()
+
+ def get_data(
+ self,
+ table_name: str,
+ columns: List[str],
+ db_close=False,
+ filters: List[str] = None,
+ ) -> List[Tuple]:
+ # Codacy: Table and column names are strictly validated against whitelisted values.
+ # Table and column names are validated against ALLOWED_TABLES and ALLOWED_COLUMNS.
+
+ # Validate table name
+ if table_name not in ALLOWED_TABLES:
+ raise ValueError(f"Invalid table name: {table_name}")
+
+ # Validate column names
+ if not all(col in ALLOWED_COLUMNS[table_name] for col in columns):
+ raise ValueError(f"Invalid column name(s): {columns}")
+
+ # Safely construct the query
+ columns_str = ", ".join([f"`{col}`" for col in columns]) # Escape column names
+ query = f"SELECT {columns_str} FROM `{table_name}`" # nosec
+
+ # Handle filters
+ params = []
+ if filters is not None:
+ filter_clauses = []
+ for column, value in filters:
+ if column not in ALLOWED_COLUMNS:
+ raise ValueError(f"Invalid column name in filter: {column}")
+ filter_clauses.append(f"`{column}` = ?") # Escape column names
+ params.append(value)
+ query += " WHERE " + " AND ".join(filter_clauses)
+
+ # Execute the query with parameters
+ self.cursor.execute(query, params)
+
+ data = self.cursor.fetchall()
+
+ if db_close:
+ self.close()
+
+ return data
+
+# ==============================================================================
+# FUNCTIONS
+# ==============================================================================
+
+
+def call_store_data(
+ tixi: Tixi3,
+ store2db: Callable[[Cursor, Path, Tixi3, str], None],
+ wkdir: Path,
+ module_name: str,
+) -> None:
+ """
+ Stores data in correct table.
+ """
+
+ # Connect to db
+ ceasiompy_db = CeasiompyDb()
+ table_name = ceasiompy_db.connect_to_table(module_name)
+
+ # Store data
+ store2db(ceasiompy_db.cursor, wkdir, tixi, table_name)
+ log.info(f"Finished storing data in table {table_name}.")
+
+ # Commit changes and close the connection
+ ceasiompy_db.commit()
+ ceasiompy_db.close()
+
+
+def store_data(tixi: Tixi3) -> None:
+ """
+ Looks at the workflow and stores data.
+ Implemented for modules:
+ - 'PyAVL': Aerodynamic Coefficients.
+ - 'CPACS2GMSH': .su2 file.
+ - 'DynamicStability': Dot-derivatives coefficients from PanelAero.
+ - 'SU2Run': Forces, moments and dot-derivatives.
+ """
+
+ # You do not want to create a results directory "PyAVL"
+ # with get_results_dir("PyAVL") if it does not exist.
+ # Only access the path and then check if it exists.
+ avl_dir: Path = get_results_directory(PYAVL_NAME, create=False)
+ gmsh_dir: Path = get_results_directory(CPACS2GMSH_NAME, create=False)
+ dynstab_dir: Path = get_results_directory(DYNSTAB_NAME, create=False)
+ su2_dir: Path = get_results_directory(SU2RUN_NAME, create=False)
+
+ if avl_dir.is_dir():
+ call_store_data(tixi, store_pyavl_data, avl_dir, PYAVL_NAME)
+ if gmsh_dir.is_dir():
+ call_store_data(tixi, store_cpacs2gmsh_data, gmsh_dir, CPACS2GMSH_NAME)
+ if dynstab_dir.is_dir():
+ call_store_data(tixi, store_dynstab_data, dynstab_dir, DYNSTAB_NAME)
+ if su2_dir.is_dir():
+ call_store_data(tixi, store_su2run_data, su2_dir, SU2RUN_NAME)
+ log.warning("Not implemented yet.")
+
+
+# ==============================================================================
+# MAIN
+# ==============================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/Database/func/su2run.py b/ceasiompy/Database/func/su2run.py
new file mode 100644
index 000000000..2e9a10b42
--- /dev/null
+++ b/ceasiompy/Database/func/su2run.py
@@ -0,0 +1,53 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Store data from SU2Run results.
+
+
+| Author: Leon Deligny
+| Creation: 2025-Mar-03
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+from sqlite3 import Cursor
+from tixi3.tixi3wrapper import Tixi3
+
+from ceasiompy import log
+
+# ==============================================================================
+# FUNCTIONS
+# ==============================================================================
+
+
+def store_su2run_data(
+ cursor: Cursor,
+ wkdir: Path,
+ tixi: Tixi3,
+ table_name: str,
+) -> None:
+ """
+ Store data from Results/SU2.
+
+ Args:
+ cursor (Cursor): gmsh_data table from 'ceasiompy.db' cursor.
+ wkdir (Path): Results/SU2 directory.
+ tixi (Tixi3): Tixi handle of CPACS file.
+
+ """
+ raise NotImplementedError("store_su2run_data not implemented")
+
+
+# ==============================================================================
+# MAIN
+# ==============================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/Database/func/utils.py b/ceasiompy/Database/func/utils.py
new file mode 100644
index 000000000..3a554e25b
--- /dev/null
+++ b/ceasiompy/Database/func/utils.py
@@ -0,0 +1,132 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Utils for Database module.
+
+
+| Author: Leon Deligny
+| Creation: 2025-Mar-03
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+import sqlite3
+
+from pathlib import Path
+from sqlite3 import Cursor
+
+from typing import (
+ List,
+ Dict,
+)
+
+from ceasiompy import log
+from ceasiompy.utils.commonpaths import CEASIOMPY_DB_PATH
+
+from ceasiompy.Database.func import (
+ ALLOWED_TABLES,
+ ALLOWED_COLUMNS,
+)
+
+# ==============================================================================
+# FUNCTIONS
+# ==============================================================================
+
+
+def split_line(line: str, index: int) -> float:
+ """
+ Splits a line with "=" sign and keeps only the value at specified index.
+ """
+ return float(line.split("=")[index].strip().split()[0])
+
+
+def create_db(path: Path = CEASIOMPY_DB_PATH) -> None:
+ """
+ Creates a database at specified path.
+ """
+
+ # Make sure parent directory exists
+ if not path.parent.is_dir():
+ log.info(f"Creating directory at {path.parent}")
+ path.parent.mkdir(parents=True, exist_ok=True)
+
+ # Create database if it does not exist
+ conn = sqlite3.connect(path)
+ conn.close()
+ log.info(f"Database created at {path}.")
+
+
+def check_in_table(cursor: Cursor, data: Dict, columns: List, table_name: str) -> bool:
+ """
+ Checks if data is already in the table using a raw cursor.
+ """
+ # Codacy: Table and column names are strictly validated against whitelisted values.
+ # Validate table name
+ if table_name not in ALLOWED_TABLES:
+ raise ValueError(f"Invalid table name: {table_name}")
+
+ # Validate column names
+ if not all(col in ALLOWED_COLUMNS[table_name] for col in columns):
+ raise ValueError(f"Invalid column name(s): {columns}")
+
+ # Build the WHERE clause dynamically
+ where_clause = " AND ".join([f"{col} = ?" for col in columns])
+ query = f"SELECT 1 FROM {table_name} WHERE {where_clause} LIMIT 1" # nosec
+ params = tuple(data[col] for col in columns)
+
+ cursor.execute(query, params)
+ return cursor.fetchone() is not None
+
+
+def data_to_db(cursor: Cursor, data: Dict, table_name: str) -> None:
+ """
+ Inserts one line at a time if not already in table.
+ """
+
+ # Table names are validated against ALLOWED_TABLES.
+ columns = list(data.keys())
+
+ # Validate table name
+ if table_name not in ALLOWED_TABLES:
+ raise ValueError(f"Invalid table name: {table_name}")
+
+ # Validate column names
+ invalid_columns = [col for col in columns if col not in ALLOWED_COLUMNS[table_name]]
+ if invalid_columns:
+ raise ValueError(f"Invalid column name(s): {invalid_columns}")
+
+ if check_in_table(cursor, data, columns, table_name):
+ log.info(f"{data.values()} already in {table_name}.")
+ else:
+ # Safely escape table and column names
+ escaped_table_name = f'"{table_name}"'
+ escaped_columns = ", ".join(f'"{col}"' for col in columns)
+
+ # Create the SQL statement dynamically
+ placeholders = ", ".join(["?" for _ in columns])
+
+ # Codacy: Table and column names are strictly validated against whitelisted values.
+ query = f"""
+ INSERT INTO {escaped_table_name} (
+ {escaped_columns}
+ ) VALUES (
+ {placeholders}
+ )
+ """ # nosec
+
+ # Execute the statement with values
+ cursor.execute(query, tuple(data.values()))
+
+
+# ==============================================================================
+# MAIN
+# ==============================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/Database/tests/databases/testceasiompy.db b/ceasiompy/Database/tests/databases/testceasiompy.db
new file mode 100644
index 000000000..e043f825d
Binary files /dev/null and b/ceasiompy/Database/tests/databases/testceasiompy.db differ
diff --git a/ceasiompy/Database/tests/test_utils.py b/ceasiompy/Database/tests/test_utils.py
new file mode 100644
index 000000000..47972b1e3
--- /dev/null
+++ b/ceasiompy/Database/tests/test_utils.py
@@ -0,0 +1,85 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Test functions for Database module.
+
+
+| Author: Leon Deligny
+| Creation: 25 March 2025
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+from ceasiompy.utils.decorators import log_test
+
+from ceasiompy.Database.func.utils import (
+ create_db,
+ data_to_db,
+)
+
+from pathlib import Path
+from unittest import main
+from ceasiompy.Database.func.storing import CeasiompyDb
+from ceasiompy.utils.ceasiompytest import CeasiompyTest
+
+from ceasiompy import log
+from ceasiompy.PyAVL import MODULE_NAME as PYAVL_NAME
+from ceasiompy.utils.commonpaths import TESTCEASIOMPY_DB_PATH
+
+# =================================================================================================
+# CLASSES
+# =================================================================================================
+
+
+class TestDatabase(CeasiompyTest):
+
+ @classmethod
+ def setUpClass(cls: 'TestDatabase') -> None:
+ cls.testceasiompy_db_path: Path = TESTCEASIOMPY_DB_PATH
+
+ @log_test
+ def test_create_db(self: 'TestDatabase') -> None:
+ create_db(path=self.testceasiompy_db_path)
+ self.assertTrue(self.testceasiompy_db_path.exists(), "Database file was not created")
+
+ # Remove if exists
+ if self.testceasiompy_db_path.exists():
+ self.testceasiompy_db_path.unlink()
+
+ @log_test
+ def test_data_to_db(self: 'TestDatabase') -> None:
+ # Define constants
+ module_name = PYAVL_NAME # test on AVL module
+ data = {
+ "aircraft": "test_aircraft",
+ "mach": 0.0, "alpha": 0.0, "beta": 0.0,
+ "pb_2V": 0.0, "qc_2V": 0.0, "rb_2V": 0.0,
+ "flap": 0.0, "aileron": 0.0, "elevator": 0.0, "rudder": 0.0,
+ "xref": 0.0, "yref": 0.0, "zref": 0.0,
+ "cd": 0.0, "cs": 0.0, "cl": 0.0,
+ "cmd": 0.0, "cms": 0.0, "cml": 0.0,
+ "cmd_a": 0.0, "cms_a": 0.0, "cml_a": 0.0,
+ "cmd_b": 0.0, "cms_b": 0.0, "cml_b": 0.0,
+ }
+
+ testceasiompy_db = CeasiompyDb(db_path=self.testceasiompy_db_path)
+ table_name = testceasiompy_db.connect_to_table(module_name)
+
+ data_to_db(testceasiompy_db.cursor, data, table_name)
+ log.info(f"Successfully stored data in table {table_name}.")
+
+ testceasiompy_db.commit()
+ testceasiompy_db.close()
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ main(verbosity=0)
diff --git a/ceasiompy/DynamicStability/README.md b/ceasiompy/DynamicStability/README.md
new file mode 100644
index 000000000..75d9574f5
--- /dev/null
+++ b/ceasiompy/DynamicStability/README.md
@@ -0,0 +1,46 @@
+
+
+# DynamicStability
+
+**Categories:** Aerodynamics, Dynamic, Stability
+
+**State**: :heavy_check_mark:
+
+## Inputs
+
+Before hand, you need to use the PyAVL module and store enough data into ceasiompy.db to use this module.
+
+Once you done that, you can:
+ 1. Choose the different mach numbers for your stability analysis.
+ 2. Choose the aircraft
+ 3. If you haven't computed any dot-derivatives 'DynamicStability' will use the Doublet Lattice Method in the frequency domain to compute those.
+
+## Analyses
+
+1. Computes unsteady derivatives through the Doublet Lattice Method (PanelAero).
+2. Opens SDSA with all the necessary computed aerodynamic coefficients.
+
+## Outputs
+
+Outputs SDSA_Input.xml, which is given as input to SDSA.
+
+## Installation or requirements
+
+Following the automatic installation procedure on the [CEASIOMpy installation page](../../installation/INSTALLATION.md) should install `DynamicStability` automatically along with the other tools.
+
+## Not implemented
+
+1. Landing gear configuration
+2. Collision points
+
+## Limitations
+
+1. Depends on SDSA software.
+2. Altitude fixed at 1000 meters.
+3. Pilot Eye is by default the tip of fuselage.
+
+## More information
+
+Dynamic Stability Software: [SDSA](https://www.meil.pw.edu.pl/add/ADD/Teaching/Software/SDSA)
+Doublet Lattice Solver: [PanelAero](https://github.com/DLR-AE/PanelAero)
+Dot derivatives algorithm: [DotDerivatives](https://fr.overleaf.com/read/qjffvmtrzzhb#8fc920)
\ No newline at end of file
diff --git a/ceasiompy/DynamicStability/__init__.py b/ceasiompy/DynamicStability/__init__.py
new file mode 100644
index 000000000..ba0ddd22d
--- /dev/null
+++ b/ceasiompy/DynamicStability/__init__.py
@@ -0,0 +1,50 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for DynamicStability module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+from ceasiompy import log
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = True
+
+# ===== Include GUI =====
+include_gui = True
+
+# ===== Include Module"s name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+# ===== Add a Results Directory =====
+RES_DIR = True
+
+# Name of SDSA Software
+SOFTWARE_NAME = "SDSA"
+
+# Fixed Altitude
+ALT = 1000.0
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/DynamicStability/__specs__.py b/ceasiompy/DynamicStability/__specs__.py
new file mode 100644
index 000000000..943c79761
--- /dev/null
+++ b/ceasiompy/DynamicStability/__specs__.py
@@ -0,0 +1,122 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+GUI Interface of DynamicStability.
+
+
+| Author: Leon Deligny
+| Creation: 25 March 2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from ceasiompy.utils.moduleinterfaces import CPACSInOut
+
+from ceasiompy import log
+from ceasiompy.DynamicStability import include_gui
+from ceasiompy.PyAVL import SOFTWARE_NAME as AVL_SOFTWARE
+from ceasiompy.SU2Run import SOFTWARE_NAME as SU2_SOFTWARE
+from ceasiompy.DynamicStability import SOFTWARE_NAME as SDSA_SOFTWARE
+
+from ceasiompy.utils.commonxpath import (
+ DYNAMICSTABILITY_NCHORDWISE_XPATH,
+ DYNAMICSTABILITY_NSPANWISE_XPATH,
+ DYNAMICSTABILITY_VISUALIZATION_XPATH,
+ DYNAMICSTABILITY_CGRID_XPATH,
+ DYNAMICSTABILITY_SOFTWARE_XPATH,
+ DYNAMICSTABILITY_MACHLIST_XPATH,
+)
+
+# ==============================================================================
+# VARIABLE
+# ==============================================================================
+
+cpacs_inout = CPACSInOut()
+
+# ==============================================================================
+# GUI INPUTS
+# ==============================================================================
+
+cpacs_inout.add_input(
+ var_name="dynamic_stability_software_data",
+ var_type=list,
+ default_value=[f"{AVL_SOFTWARE}", f"{SU2_SOFTWARE}"],
+ unit=None,
+ descr="Which data from ceasiompy.db to use.",
+ xpath=DYNAMICSTABILITY_SOFTWARE_XPATH,
+ gui=include_gui,
+ gui_name="Use data from which software",
+ gui_group="Software Settings",
+)
+
+cpacs_inout.add_input(
+ var_name="mach_list",
+ var_type="multiselect",
+ default_value=[0.1], # [0.1, 0.2, 0.3, 0.4, 0.5, 0.6],
+ unit="mach",
+ descr=f"List of mach numbers used in {SDSA_SOFTWARE}",
+ xpath=DYNAMICSTABILITY_MACHLIST_XPATH,
+ gui=include_gui,
+ gui_name="Mach List",
+ gui_group="Mach Settings",
+)
+
+cpacs_inout.add_input(
+ var_name="chordwise_vort",
+ var_type=int,
+ default_value=12,
+ unit=None,
+ descr="Select the number of chordwise vortices",
+ xpath=DYNAMICSTABILITY_NCHORDWISE_XPATH,
+ gui=include_gui,
+ gui_name="Number of chordwise vortices",
+ gui_group="Chordwise Settings",
+)
+
+cpacs_inout.add_input(
+ var_name="spanwise_vort",
+ var_type=int,
+ default_value=20,
+ unit=None,
+ descr="Select the number of spanwise vortices",
+ xpath=DYNAMICSTABILITY_NSPANWISE_XPATH,
+ gui=include_gui,
+ gui_name="Number of spanwise vortices",
+ gui_group="Spanwise Settings",
+)
+
+cpacs_inout.add_input(
+ var_name="dlm_aerogrid_visualization",
+ var_type=bool,
+ default_value=False,
+ unit=None,
+ descr="Visualization of the aerogrid the DLM is going to use",
+ xpath=DYNAMICSTABILITY_VISUALIZATION_XPATH,
+ gui=include_gui,
+ gui_name="aerogrid visualization",
+ gui_group="Visualization",
+)
+
+cpacs_inout.add_input(
+ var_name="c_grid",
+ var_type=str,
+ default_value="C:/Program Files/SDSA/Grid/test.inp",
+ unit=None,
+ descr="Select the cGrid path",
+ xpath=DYNAMICSTABILITY_CGRID_XPATH,
+ gui=include_gui,
+ gui_name="cGrid path",
+ gui_group="cGrid Setting",
+)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/DynamicStability/dynamicstability.py b/ceasiompy/DynamicStability/dynamicstability.py
new file mode 100644
index 000000000..5d357ce0c
--- /dev/null
+++ b/ceasiompy/DynamicStability/dynamicstability.py
@@ -0,0 +1,61 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Dynamic Stability Module
+
+
+| Author: Leon Deligny
+| Creation: 2025-01-27
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+from ceasiompy.utils.ceasiompyutils import (
+ call_main,
+ run_software,
+)
+
+from pathlib import Path
+from cpacspy.cpacspy import CPACS
+from ceasiompy.DynamicStability.func.cpacs2sdsa import SDSAFile
+
+from ceasiompy import log
+
+from ceasiompy.DynamicStability import (
+ MODULE_NAME,
+ SOFTWARE_NAME,
+)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+def main(cpacs: CPACS, wkdir: Path) -> None:
+ """
+ Opens SDSA with CPACS file and ceasiompy.db's data.
+ """
+
+ # Create SDSAFile object from the CPACS file
+ sdsa_file = SDSAFile(cpacs, wkdir)
+
+ # Save the file in /Results/DynamicStability
+ input_xml: str = sdsa_file.generate_xml()
+
+ log.info("--- Calling SDSA ---")
+
+ run_software(
+ software_name=SOFTWARE_NAME,
+ arguments=["", f"{input_xml}", "1"],
+ wkdir=wkdir,
+ with_mpi=False,
+ )
+
+
+if __name__ == "__main__":
+ call_main(main, MODULE_NAME)
diff --git a/ceasiompy/DynamicStability/func/SDSAEmpty.xml b/ceasiompy/DynamicStability/func/SDSAEmpty.xml
new file mode 100644
index 000000000..cdccccf31
--- /dev/null
+++ b/ceasiompy/DynamicStability/func/SDSAEmpty.xml
@@ -0,0 +1,236 @@
+
+
+
+ 0
+
+
+ 1
+
+
+
+ 0
+
+
+
+ 1
+
+
+
+ 0
+
+
+
+ 1
+
+
+
+ 0
+
+
+
+ 1
+
+
+
+ 0
+
+
+
+ 1
+
+
+
+ 0
+
+
+
+ 1
+
+
+
+ 0
+
+
+
+ 1
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 19
+ 8
+ 12
+ 6
+ 6
+ 6
+
+
+
+
+ 19
+ 8
+ 6
+ 7
+ 6
+
+
+
+
+ [
+ ]
+
+
+
+
+ Dataset
+ 0
+
+
+
+
+
+
+ 0
+
+
+
+ 1
+ 196138 0 0 0 9.62355e+006 2.04062e+007
+ 2.80909e+007 0 0 0
+
+
+ 0
+
+
+
+
+ -25
+ 0
+ 0.05
+ 0.2
+
+
+ -21.5
+ 0
+ 0.05
+ 0.2
+
+
+ -30
+ 0
+ 0.05
+ 0
+
+
+
+ 0.5
+ 0.5
+ 0.5
+ 0.05
+ 0.05
+ 0.05
+ 5
+ 5
+ 5
+
+
+ 1
+ 1
+ 1
+ 0.05
+ 0.05
+ 0.05
+
+
+
+ 0.05
+ 0
+ 3.2
+ 4.43
+ 0
+ 0.1
+ 0.1
+ 0.4
+
+
+ 0.05
+ 0
+ 3.2
+ 4.43
+ 0
+ 0.1
+ 0.1
+ 0.5
+
+
+ 0.05
+ 0
+ 3.2
+ 4.43
+ 0
+ 0.1
+ 0.1
+ 0.5
+
+
+ ../fcs/FCSMatrices.out
+
+
+
+
+ 2
+ 35.167 35.167 -3 3 0.177 0.177 0 0 0 0 100
+ 100 100 100 3000 3000 3000 3000 1.5 1.5 2 2
+
+
+ 3
+ 0
+ 3
+ 2
+ 9
+ 11
+ 0.05 0.15 0.25 0.35 0.45 0.55 0.65 0.75 0.85
+ 0 1000 2000 3000 4000 5000 6000 7000 8000 9000
+ 10000
+ 58950 54280.8 49885.3 45753.2 41874.6 38239.6
+ 34838.7 31662.1 28700.6 25944.8 23385.8 56850 52347.2 48108.2 44123.3 40382.9
+ 36877.4 33597.6 30534.2 27678.1 25020.6 22552.7 54750 50413.5 46331.1 42493.4
+ 38891.2 35515.2 32356.5 29406.3 26655.7 24096.3 21719.6 52650 48479.8 44554 40863.5
+ 37399.4 34153 31115.4 28278.4 25633.3 23172.1 20886.5 50550 46546.2 42776.9 39233.6
+ 35907.7 32790.7 29874.4 27150.4 24610.9 22247.8 20053.5 48450 44612.5 40999.8
+ 37603.8 34416 31428.5 28633.3 26022.5 23588.5 21323.6 19220.4 46350 42678.8 39222.8
+ 35973.9 32924.3 30066.3 27392.2 24894.6 22566.1 20399.4 18387.3 44250 40745.1
+ 37445.7 34344 31432.6 28704.1 26151.2 23766.7 21543.7 19475.1 17554.2 42150 38811.5
+ 35668.6 32714.1 29940.9 27341.8 24910.1 22638.8 20521.3 18550.9 16721.1
+
+ 1
+ # We assume no landing
+ gear for now # Landing gear configuration file 0
+
+
+ 1
+
+
+
\ No newline at end of file
diff --git a/ceasiompy/DynamicStability/func/__init__.py b/ceasiompy/DynamicStability/func/__init__.py
new file mode 100644
index 000000000..c99c89194
--- /dev/null
+++ b/ceasiompy/DynamicStability/func/__init__.py
@@ -0,0 +1,64 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for DynamicStability.func.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from typing import List
+
+from ceasiompy import log
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# Initialze SDSA xPaths
+ROOT_XPATH = "/root"
+
+# In /root
+PILOTEYE_XPATH = ROOT_XPATH + "/GeoMass/PilotEye"
+CGRID_XPATH = ROOT_XPATH + "/cGrid"
+AERO_XPATH = ROOT_XPATH + "/Aero"
+
+# In /aero
+REF_XPATH = AERO_XPATH + "/Ref"
+STATUS_XPATH = AERO_XPATH + "/GEffect/Status"
+AEROTABLE_XPATH = AERO_XPATH + "/Tables/AeroTable/Table"
+CTRLTABLE_XPATH = AERO_XPATH + "/Tables/CtrlTable/Table"
+ALPHAMAX_XPATH = AERO_XPATH + "/AlphaMax"
+
+# In /DelCoeff
+DELCOEF_XPATH = AERO_XPATH + "/DelCoeff"
+FLAPS1_XPATH = DELCOEF_XPATH + "/Flaps1"
+FLAPS2_XPATH = DELCOEF_XPATH + "/Flaps2"
+AIRBRAKE_XPATH = DELCOEF_XPATH + "/AirBrake"
+LANDGEAR_XPATH = DELCOEF_XPATH + "/LandGear"
+
+
+# List of xpaths for derivatives
+XPATHS_PRIM: List = [
+ (AERO_XPATH + "/CMAlphaPrim", "cm_alphaprim"),
+ (AERO_XPATH + "/CZAlphaPrim", "cz_alphaprim"),
+ (AERO_XPATH + "/CXAlphaPrim", "cx_alphaprim"),
+ (AERO_XPATH + "/CYBetaPrim", "cy_betaprim"),
+ (AERO_XPATH + "/CLBetaPrim", "cl_betaprim"),
+ (AERO_XPATH + "/CNBetaPrim", "cn_betaprim"),
+]
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/DynamicStability/func/alphamax.py b/ceasiompy/DynamicStability/func/alphamax.py
new file mode 100644
index 000000000..1b27b6110
--- /dev/null
+++ b/ceasiompy/DynamicStability/func/alphamax.py
@@ -0,0 +1,111 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Compute AlphaMax for SDSA from ceasiomp.db.
+
+
+| Author: Leon Deligny
+| Creation: 25 March 2025
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+import numpy as np
+
+from pandas import DataFrame
+from ceasiompy.Database.func.storing import CeasiompyDb
+
+from ceasiompy import log
+from ceasiompy.DynamicStability import ALT
+from ceasiompy.PyAVL import SOFTWARE_NAME as AVL_SOFTWARE
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def get_alpha_max(self) -> DataFrame:
+ """
+ Compute alpha max, i.e. d Cm/ d alpha = 0.
+ alpha_max will correspond to the the smallest alpha
+ such that the derivative is positive:
+ alpha_max = argmax_alpha (d Cm/ d alpha) (alpha) < 0.
+
+ Assumptions:
+ beta = p = q = r = 0
+ alt = 1000.0
+
+ Returns:
+ DataFrame: AlphaMax per mach.
+
+ """
+
+ log.info("--- Computing AlphaMax per Mach ---")
+
+ if self.software_data == AVL_SOFTWARE:
+ table_name = "avl_data"
+ else:
+ log.warning(f"software {self.software_data} not implemented yet.")
+
+ # Retrieve data from db
+ ceasiompy_db = CeasiompyDb()
+ data = ceasiompy_db.get_data(
+ table_name=table_name,
+ columns=["mach", "alpha", "cms_a"],
+ filters=[
+ f"mach IN ({self.mach_str})",
+ f"aircraft = '{self.aircraft_name}'",
+ f"alt = {ALT}",
+ "beta = 0.0",
+ "pb_2V = 0.0",
+ "qc_2V = 0.0",
+ "rb_2V = 0.0",
+ ]
+ )
+ ceasiompy_db.close()
+
+ data_array = np.array(data)
+ mach_values = data_array[:, 0]
+ alpha_values = data_array[:, 1]
+ cms_a_values = data_array[:, 2]
+ unique_mach = np.unique(mach_values)
+
+ alpha_max_results = []
+
+ # Vectorized computation for each Mach number
+ for mach in unique_mach:
+ mask = mach_values == mach
+ alphas = alpha_values[mask]
+ cms_a = cms_a_values[mask]
+
+ sorted_indices = np.argsort(alphas)
+ alphas = alphas[sorted_indices]
+ cms_a = cms_a[sorted_indices]
+
+ positive_indices = np.where(cms_a < 0)[0]
+ if len(positive_indices) > 0:
+ # Largest alpha where cms_a < 0
+ alpha_max = alphas[positive_indices[-1]]
+ else:
+ alpha_max = 0.0
+
+ alpha_max_results.append({"mach": mach, "alpha_max": alpha_max})
+
+ alpha_max_df = DataFrame(alpha_max_results)
+
+ log.info("--- Finished computing AlphaMax ---")
+
+ return alpha_max_df
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute.")
diff --git a/ceasiompy/DynamicStability/func/cpacs2sdsa.py b/ceasiompy/DynamicStability/func/cpacs2sdsa.py
new file mode 100644
index 000000000..b2827f421
--- /dev/null
+++ b/ceasiompy/DynamicStability/func/cpacs2sdsa.py
@@ -0,0 +1,290 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Transfer CPACS unsteady data into SDSA with the correct file format (for reference: SDSAEmpty.xml).
+
+
+| Author: Leon Deligny
+| Creation: 27-Jan-2025
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+import shutil
+
+import numpy as np
+
+from ceasiompy.DynamicStability.func.utils import sdsa_format
+from ceasiompy.DynamicStability.func.alphamax import get_alpha_max
+from ceasiompy.DynamicStability.func.steadyderivatives import get_tables_values
+
+from cpacspy.cpacsfunctions import (
+ get_value,
+ open_tixi,
+)
+from ceasiompy.utils.ceasiompyutils import (
+ bool_,
+ aircraft_name,
+)
+from ceasiompy.DynamicStability.func.dotderivatives import (
+ get_main_wing_le,
+ compute_dot_derivatives,
+)
+
+from typing import List
+from pathlib import Path
+from ambiance import Atmosphere
+from cpacspy.cpacspy import CPACS
+
+from ceasiompy import log
+from ceasiompy.PyAVL import SOFTWARE_NAME as AVL_SOFTWARE
+from ceasiompy.DynamicStability import MODULE_DIR as DYNAMICSTABILITY_DIR
+
+from ceasiompy.utils.commonxpath import (
+ REF_XPATH as CPACS_REF_XPATH,
+ DYNAMICSTABILITY_CGRID_XPATH,
+ DYNAMICSTABILITY_SOFTWARE_XPATH,
+ DYNAMICSTABILITY_MACHLIST_XPATH,
+ DYNAMICSTABILITY_NSPANWISE_XPATH,
+ DYNAMICSTABILITY_NCHORDWISE_XPATH,
+ DYNAMICSTABILITY_VISUALIZATION_XPATH,
+)
+
+from ceasiompy.DynamicStability import ALT
+from ceasiompy.DynamicStability.func import (
+ ROOT_XPATH,
+ AERO_XPATH,
+ REF_XPATH,
+ PILOTEYE_XPATH,
+ AEROTABLE_XPATH,
+ CTRLTABLE_XPATH,
+ XPATHS_PRIM,
+ ALPHAMAX_XPATH,
+ FLAPS1_XPATH,
+ FLAPS2_XPATH,
+ AIRBRAKE_XPATH,
+ LANDGEAR_XPATH,
+ STATUS_XPATH,
+ CGRID_XPATH,
+)
+
+# ==============================================================================
+# CLASSES
+# ==============================================================================
+
+
+class SDSAFile:
+ """
+ This class contains the following public methods:
+ generate_xml(self) -> str:
+ Generates xml file for SDSA and returns it"s path.
+
+ update_alpha_max(self) -> None:
+ Updates SDSA file with AlphaMax values.
+
+ update_dot_derivatives(self) -> None:
+ Updates SDSA file with all of it"s dot derivatives.
+
+ update_tables(self) -> None:
+ Updates SDSA file with it"s table and ctrltable values.
+
+ Subclassing:
+ This class is not meant to be subclassed.
+
+ """
+
+ # SDSA xPath
+ root_xpath: str = ROOT_XPATH
+ aero_xpath: str = AERO_XPATH
+ ref_xpath: str = REF_XPATH
+ piloteye_xpath: str = PILOTEYE_XPATH
+ aerotable_xpath: str = AEROTABLE_XPATH
+ ctrltable_xpath: str = CTRLTABLE_XPATH
+
+ # CPACS xPath
+ area_xpath = CPACS_REF_XPATH + "/area"
+ length_xpath = CPACS_REF_XPATH + "/length"
+
+ # Dot derivatives xPaths
+ xpaths_prim: List = XPATHS_PRIM
+
+ # Set Atmospheric Conditions for Dynamic Stability
+ # TODO : Dynamic Stability at which altitude ???
+ Atm = Atmosphere(ALT)
+ density = Atm.density[0]
+ g = Atm.grav_accel[0]
+
+ dynstab_dir = DYNAMICSTABILITY_DIR
+
+ def __init__(self: "SDSAFile", cpacs: CPACS, wkdir: Path) -> None:
+
+ # Import input CPACS file
+ self.cpacs = cpacs
+ self.tixi = self.cpacs.tixi
+
+ # Import SDSAEmpty.xml file
+ self.empty_sdsa_path = self.dynstab_dir / "func/SDSAEmpty.xml"
+ self.empty_sdsa_file = open_tixi(self.empty_sdsa_path)
+
+ # Create a copy of the empty SDSA file in Results > DynamicStability
+ self.dynamic_stability_dir = wkdir
+ self.sdsa_path = str(self.dynamic_stability_dir) + "/SDSA_Input.xml"
+ shutil.copy(self.empty_sdsa_path, self.sdsa_path)
+ self.sdsa_file = open_tixi(str(self.sdsa_path))
+
+ # Get the reference dimensions
+ self.s = self.tixi.getDoubleElement(self.area_xpath)
+ self.c = self.tixi.getDoubleElement(self.length_xpath)
+ self.b = self.s / self.c
+
+ # Software for data used
+ self.software_data = str(get_value(self.tixi, DYNAMICSTABILITY_SOFTWARE_XPATH))
+
+ # Access CEASIOMpy input data
+ self.nchordwise = int(get_value(self.tixi, DYNAMICSTABILITY_NCHORDWISE_XPATH))
+ self.nspanwise = int(get_value(self.tixi, DYNAMICSTABILITY_NSPANWISE_XPATH))
+ self.cgrid = get_value(self.tixi, DYNAMICSTABILITY_CGRID_XPATH)
+ self.aircraft_name: str = aircraft_name(self.tixi)
+ mach_str: str = get_value(self.tixi, DYNAMICSTABILITY_MACHLIST_XPATH)
+
+ self.plot = bool_(get_value(self.tixi, DYNAMICSTABILITY_VISUALIZATION_XPATH))
+
+ log.info(f"self.plot {self.plot}")
+
+ # Extract and unique list of mach identifiers
+ self.mach_list = list(set([float(x) for x in str(mach_str).split(';')]))
+ self.mach_str = ",".join(str(mach) for mach in self.mach_list)
+ self.len_mach_list = len(self.mach_list)
+
+ # Initialize Doublet Lattice Model
+ self.model = None
+
+ def update_alpha_max(self: "SDSAFile") -> None:
+ """
+ Updates SDSA input file with AlphaMax values.
+ """
+
+ df_alpha_max = get_alpha_max(self)
+
+ aero_alpha_max_xpath = ALPHAMAX_XPATH
+ x_xpath = aero_alpha_max_xpath + "/X"
+ values_xpath = aero_alpha_max_xpath + "/Values"
+
+ self.update(x_xpath, sdsa_format(self.mach_list))
+ self.update_attribute(x_xpath, f"1 {self.len_mach_list}")
+ self.update(aero_alpha_max_xpath + "/NX", f"{self.len_mach_list}")
+ self.update(values_xpath, sdsa_format(np.radians(df_alpha_max["alpha_max"]).tolist()))
+ self.update_attribute(values_xpath, f"1 {self.len_mach_list}")
+
+ def update_dot_derivatives(self: "SDSAFile") -> None:
+ """
+ Updates SDSA input file with dot derivatives.
+
+ """
+
+ if self.software_data == AVL_SOFTWARE:
+ df_dot = compute_dot_derivatives(self)
+ for aero_prim_xpath, column_name in self.xpaths_prim:
+ x_xpath = aero_prim_xpath + "/X"
+ values_xpath = aero_prim_xpath + "/Values"
+
+ self.update(x_xpath, sdsa_format(self.mach_list))
+ self.update_attribute(x_xpath, f"1 {self.len_mach_list}")
+ self.update(aero_prim_xpath + "/NX", f"{self.len_mach_list}")
+ self.update(values_xpath, sdsa_format(df_dot[column_name].tolist()))
+ self.update_attribute(values_xpath, f"1 {self.len_mach_list}")
+
+ else:
+ log.warning(
+ "Computing dot-derivatives with "
+ f"software {self.software_data} is not implemented yet."
+ )
+
+ def update_tables(self: "SDSAFile") -> None:
+ """
+ Updates SDSA input file with table values.
+
+ """
+
+ table_df, ctrl_table_df = get_tables_values(self)
+
+ table_data = []
+ ctrl_table_data = []
+
+ # Update Table
+ for column in table_df.columns:
+ table_data.extend(table_df[column].tolist())
+
+ self.update(self.aerotable_xpath, sdsa_format(table_data))
+ self.update_attribute(self.aerotable_xpath, f"{len(table_df)} 12")
+
+ # Update CtrlTable
+ for column in ctrl_table_df.columns:
+ ctrl_table_data.extend(ctrl_table_df[column].tolist())
+
+ self.update(self.ctrltable_xpath, sdsa_format(ctrl_table_data))
+ self.update_attribute(self.ctrltable_xpath, f"{len(ctrl_table_df)} 11")
+
+ def update_piloteye(self: "SDSAFile") -> None:
+ if self.model is None:
+ log.warning("Issue with DLM model in cpacs2sdsa.py.")
+ else:
+ x_le, _, z_le = get_main_wing_le(self.model)
+ x_cg = x_le + (self.c / 4.0)
+
+ self.update(self.piloteye_xpath + "/X", f"{x_cg}")
+ self.update(self.piloteye_xpath + "/Z", f"{z_le}")
+
+ def update_ref(self: "SDSAFile") -> None:
+ self.update(self.ref_xpath + "/S", f"{self.s}")
+ self.update(self.ref_xpath + "/B", f"{self.b}")
+ self.update(self.ref_xpath + "/MAC", f"{self.c}")
+
+ def update_delcoeff(self: "SDSAFile") -> None:
+ zeros = "0 " * 5
+ self.update(FLAPS1_XPATH, zeros)
+ self.update(FLAPS2_XPATH, zeros)
+ self.update(AIRBRAKE_XPATH, zeros)
+ self.update(LANDGEAR_XPATH, zeros)
+
+ def generate_xml(self: "SDSAFile") -> str:
+ """
+ Generates xml file for SDSA and returns the path of where it is stored.
+
+ Returns:
+ self.sdsa_path (str): Path where the complete xml file is stored.
+
+ """
+
+ self.update_alpha_max()
+ self.update_tables()
+ self.update_dot_derivatives()
+ self.update_delcoeff()
+ self.update(STATUS_XPATH, "0") # Update GEffect
+ self.update_ref()
+ self.update(CGRID_XPATH, f"{self.cgrid}") # Update CGrid
+ self.update_piloteye()
+
+ self.sdsa_file.save(self.sdsa_path)
+
+ return self.sdsa_path
+
+ def update(self: "SDSAFile", xpath: str, ele: str) -> None:
+ self.sdsa_file.updateTextElement(xpath, ele)
+
+ def update_attribute(self: "SDSAFile", xpath: str, ele: str) -> None:
+ self.sdsa_file.addTextAttribute(xpath, "size", ele)
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute.")
diff --git a/ceasiompy/DynamicStability/func/dotderivatives.py b/ceasiompy/DynamicStability/func/dotderivatives.py
new file mode 100644
index 000000000..7be412b51
--- /dev/null
+++ b/ceasiompy/DynamicStability/func/dotderivatives.py
@@ -0,0 +1,734 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Unsteady-derivatives computations through PanelAero.
+Notation used:
+ 1. Subscripts "_alpha", "_beta" represent derivative wrt to alpha or beta.
+ 2. fx, fy, fz represent drag, side, lift force.
+ 3. cx, cy, cz represent drag, side, lift coefficient.
+
+
+| Author: Leon Deligny
+| Creation: 2025-Feb-12
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+import math
+import cmath
+
+import numpy as np
+
+from pandas import concat
+
+from ceasiompy.utils.geometryfunctions import (
+ wing_sections,
+ elements_number,
+ get_positionings,
+)
+from ceasiompy.DynamicStability.func.utils import (
+ beta,
+ alpha,
+ complex_cross,
+ complex_decomposition,
+)
+
+from panelaero import DLM
+from numpy import ndarray
+from pandas import DataFrame
+from ceasiompy.Database.func.storing import CeasiompyDb
+
+from ceasiompy.DynamicStability.func.panelaeroconfig import (
+ AeroModel,
+ DetailedPlots,
+)
+from typing import (
+ List,
+ Dict,
+ Tuple,
+)
+
+from ceasiompy import log
+from ceasiompy.utils.commonxpath import WINGS_XPATH
+
+# =================================================================================================
+# METHODS
+# =================================================================================================
+
+
+def load_geometry(self) -> List:
+ """
+ Load geometry in PanelAero's format.
+
+ Returns:
+ (List): Wings in PanelAsero's format.
+
+ """
+ wing_cnt = elements_number(self.tixi, WINGS_XPATH, "wing")
+ # used_wings = return_usedwings(self.tixi)
+
+ wing_lists = []
+
+ for i_wing in range(wing_cnt):
+ wing_xpath = WINGS_XPATH + "/wing[" + str(i_wing + 1) + "]"
+
+ name_wing = self.tixi.getTextElement(wing_xpath + "/name")
+
+ # if name_wing in used_wings.keys():
+ log.info(f"Name of the wing: {name_wing}")
+
+ # Wing Positionings
+ _, pos_x_list, pos_y_list, pos_z_list = get_positionings(self.tixi, wing_xpath, "wing")
+
+ # Wing Sections
+ le_list = wing_sections(
+ self.tixi,
+ wing_xpath,
+ pos_x_list,
+ pos_y_list,
+ pos_z_list,
+ list_type='first_n_last'
+ )
+ first_le = le_list[0]
+ last_le = le_list[1]
+
+ x1, y1, z1, d12 = first_le[0], first_le[1], first_le[2], first_le[3]
+ x4, y4, z4, d34 = last_le[0], last_le[1], last_le[2], last_le[3]
+
+ wing_dict = {
+ 'EID': i_wing, # str
+ 'CP': 0, # int
+ 'n_span': self.nchordwise, # int
+ 'n_chord': self.nspanwise, # int
+ 'X1': np.array([x1, y1, z1]),
+ 'length12': d12, # float
+ 'X4': np.array([x4, y4, z4]),
+ 'length43': d34, # float
+ }
+
+ wing_lists.append(wing_dict)
+
+ return wing_lists
+
+
+def compute_velocity_attributes(
+ self,
+ mach: float,
+ k_alpha_model: float,
+ k_beta_model: float,
+) -> Tuple[float, float, float]:
+ """
+ Compute arguments for PanelAero's model.
+
+ Args:
+ mach (float): Mach number.
+ k_alpha_model (float): Reduced frequency for alpha oscillations.
+ k_beta_model (float): Reduced frequency for beta oscillations.
+
+ Returns:
+ (Tuple[float, float, float]):
+ - Angular frequency for alpha oscillations.
+ - Angular frequency for beta oscillations.
+ - Dynamic pressure.
+
+ """
+
+ # Velocity in m/s in atmospheric environment
+ velocity = self.Atm.speed_of_sound[0] * mach
+
+ # Frequency
+ omegaalpha = k_alpha_model * velocity # 2 pi / T
+ omegabeta = k_beta_model * velocity
+
+ # Dynamic pressure
+ q_dyn = self.density * (velocity ** 2) / 2.0
+
+ return omegaalpha, omegabeta, q_dyn
+
+
+def scale_coefficients(
+ self, q_dyn: float,
+ x_alpha: float, y_alpha: float, z_alpha: float,
+ l_alpha: float, m_alpha: float, n_alpha: float,
+ x_beta: float, y_beta: float, z_beta: float,
+ l_beta: float, m_beta: float, n_beta: float,
+ m_alphadot: float, z_alphadot: float, x_alphadot: float,
+ y_betadot: float, l_betadot: float, n_betadot: float
+):
+ """
+ Scales coefficients accordingly.
+ """
+
+ log.info(
+ f"Scaling using dyn pres.: {q_dyn}, surface: {self.s}, chord: {self.c}, span: {self.b}."
+ )
+
+ # Define variables
+ qs = q_dyn * self.s
+ qsb = qs * self.b
+ qsc = qs * self.c
+
+ # Compute coefficients
+ cx_alpha = x_alpha / qs # Force
+ cy_alpha = y_alpha / qs # Force
+ cz_alpha = z_alpha / qs # Force
+
+ cl_alpha = l_alpha / qsb # Moment
+ cm_alpha = m_alpha / qsc # Moment
+ cn_alpha = n_alpha / qsb # Moment
+
+ cx_beta = x_beta / qs # Force
+ cy_beta = y_beta / qs # Force
+ cz_beta = z_beta / qs # Force
+
+ cl_beta = l_beta / qsb # Moment
+ cm_beta = m_beta / qsc # Moment
+ cn_beta = n_beta / qsb # Moment
+
+ # Compute dot coefficients
+ cm_alphadot = m_alphadot / qsc # Moment
+ cz_alphadot = z_alphadot / qs # Force
+ cx_alphadot = x_alphadot / qs # Force
+
+ cy_betadot = y_betadot / qs # Force
+ cl_betadot = l_betadot / qsb # Moment
+ cn_betadot = n_betadot / qsb # Moment
+
+ return (
+ cx_alpha, cy_alpha, cz_alpha,
+ cl_alpha, cm_alpha, cn_alpha,
+ cx_beta, cy_beta, cz_beta,
+ cl_beta, cm_beta, cn_beta,
+ cm_alphadot, cz_alphadot, cx_alphadot,
+ cy_betadot, cl_betadot, cn_betadot,
+ )
+
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def access_angle_derivatives(
+ f_real: ndarray,
+ f_img: ndarray,
+ omega: float,
+ t: float,
+ angle_0: float
+) -> Tuple[float, float, float]:
+ """
+ Decomposition of real and imaginary part of f in (cos, sin) basis.
+
+ Args:
+ f_real (ndarray): Real part of f.
+ f_img (ndarray): Imaginary part of f.
+ omega (float): Angular frequency.
+ t (float): Time.
+ angle_0 (float): Initial angle.
+
+ Returns:
+ (Tuple[float, float, float]): x, y, z components of this decomposition.
+
+ """
+
+ cos_omega_t = np.cos(omega * t)
+ sin_omega_t = np.sin(omega * t)
+
+ x = ((f_real[0] * cos_omega_t) + (f_img[0] * sin_omega_t)) / angle_0
+ y = ((f_real[1] * cos_omega_t) + (f_img[1] * sin_omega_t)) / angle_0
+ z = ((f_real[2] * cos_omega_t) + (f_img[2] * sin_omega_t)) / angle_0
+
+ return x, y, z
+
+
+def access_angle_derivatives_np(
+ f_real: ndarray,
+ f_img: ndarray,
+ omega: float,
+ t: float,
+ angle_0: float
+) -> ndarray:
+ """
+ Decomposition of real and imaginary part of f_derivative in (cos, sin) basis.
+
+ Args:
+ f_real (ndarray): Real part of f.
+ f_img (ndarray): Imaginary part of f.
+ omega (float): Angular frequency.
+ t (float): Time.
+ angle_0 (float): Initial angle.
+
+ Returns:
+ (Tuple[ndarray, ndarray, ndarray]): x, y, z components of this decomposition.
+
+ """
+ cos_omega_t = np.cos(omega * t)
+ sin_omega_t = np.sin(omega * t)
+
+ x = (f_real[0, :] * cos_omega_t + f_img[0, :] * sin_omega_t) / angle_0
+ y = (f_real[1, :] * cos_omega_t + f_img[1, :] * sin_omega_t) / angle_0
+ z = (f_real[2, :] * cos_omega_t + f_img[2, :] * sin_omega_t) / angle_0
+
+ return np.array([x, y, z])
+
+
+def access_angle_dot_derivatives(
+ f_real: ndarray,
+ f_img: ndarray,
+ omega: float,
+ t: float,
+ angle_0: float
+) -> Tuple[float, float, float]:
+ """
+ Decomposition of real and imaginary part of f_dot in (cos, sin) basis.
+
+ Args:
+ f_real (ndarray): Real part of f.
+ f_img (ndarray): Imaginary part of f.
+ omega (float): Angular frequency.
+ t (float): Time.
+ angle_0 (float): Initial angle.
+
+ Returns:
+ (Tuple[float, float, float]): x, y, z components of this decomposition.
+
+ """
+
+ cos_omega_t = np.cos(omega * t)
+ sin_omega_t = np.sin(omega * t)
+ scale = angle_0 * omega
+
+ x = (f_img[0] * cos_omega_t - f_real[0] * sin_omega_t) / scale
+ y = (f_img[1] * cos_omega_t - f_real[1] * sin_omega_t) / scale
+ z = (f_img[2] * cos_omega_t - f_real[2] * sin_omega_t) / scale
+
+ return x, y, z
+
+
+def access_angle_dot_derivatives_np(
+ f_real: ndarray,
+ f_img: ndarray,
+ omega: float,
+ t: float,
+ angle_0: float
+) -> ndarray:
+ """
+ Decomposition of real and imaginary part of f in (cos, sin) basis.
+
+ Args:
+ f_real (ndarray): Real part of f.
+ f_img (ndarray): Imaginary part of f.
+ omega (float): Angular frequency.
+ t (float): Time.
+ angle_0 (float): Initial angle.
+
+ Returns:
+ (Tuple[ndarray, ndarray, ndarray]): x, y, z components of this decomposition.
+
+ """
+ cos_omega_t = np.cos(omega * t)
+ sin_omega_t = np.sin(omega * t)
+ scale = angle_0 * omega
+
+ x = (f_img[0, :] * cos_omega_t - f_real[0, :] * sin_omega_t) / scale
+ y = (f_img[1, :] * cos_omega_t - f_real[1, :] * sin_omega_t) / scale
+ z = (f_img[2, :] * cos_omega_t - f_real[2, :] * sin_omega_t) / scale
+
+ return np.array([x, y, z])
+
+
+def compute_moments(
+ aerogrid: Dict,
+ forces: ndarray,
+ x_hinge: float
+) -> ndarray:
+ """
+ Compute Moments using formula, M = sum_{j: panel} r_j cross f_j.
+
+ Args:
+ aerogrid (Dict): PanelAero model.
+ forces (ndarray): Forces on each panel (aerogrid['n'], 3).
+ x_hinge (float): x-th coordinate of center of gravity.
+
+ Returns:
+ moments (ndarray): Total moments (3, ).
+
+ """
+
+ # Moment computed at each panel (aerogrid['n'], 3)
+ r = -aerogrid['offset_j']
+ r[:, 0] = r[:, 0] - x_hinge # MODIFY x_ref, y_ref, z_ref if you uncomment
+
+ if np.iscomplexobj(forces):
+ m = complex_cross(r, forces.T)
+ else:
+ m = np.cross(r, forces.T)
+
+ # Total moment (3, )
+ moments = m.sum(axis=0)
+
+ return moments
+
+
+def get_main_wing_le(model: AeroModel) -> Tuple[float, float, float]:
+ """
+ Get leading edge of main wing.
+
+ Args:
+ model (AeroModel): PanelAero model.
+
+ Returns:
+ (Tuple[float, float, float]): x, y, z-th coordinate of leading edge.
+
+ """
+
+ wings_list = model.wings_list
+ x1_values = [model.caerocards[i]['X1'][0] for i, _ in enumerate(wings_list)]
+ min_x1_index = x1_values.index(min(x1_values))
+
+ return model.caerocards[min_x1_index]['X1'][0], model.caerocards[
+ min_x1_index]['X1'][1], model.caerocards[min_x1_index]['X1'][2]
+
+
+def check_x_hinge(aerogrid: Dict, x_hinge: float) -> None:
+ """
+ Checks if x-th coordinate of hinge point in not in the aerogrid.
+
+ Args:
+ aerogrid (Dict): Aerogrid with coordinates of panels.
+ x_hinge (float): x-th coordinate of hinge point.
+
+ """
+
+ # Make sure x_hinge \notin x
+ if x_hinge in aerogrid['offset_j'][:, 0]:
+ log.warning(
+ "Error in PanelAero's Doublet Lattice Method."
+ "Hinge point can not be equal to downwash control point."
+ )
+
+
+def compute_panel_forces(
+ aerogrid: Dict,
+ q_alpha_jj: ndarray,
+ q_beta_jj: ndarray,
+ omegaalpha: float,
+ omegabeta: float,
+ q_dyn: float,
+ t: float,
+ alpha_0: float,
+ beta_0: float,
+) -> Tuple[ndarray, ndarray]:
+ """
+ Compute alpha and beta forces on panels.
+
+ Args:
+ aerogrid (Dict): Aerogrid of geometry.
+ q_alpha_jj (ndarray): PanelAero's DLM output for alpha oscillations.
+ q_beta_jj (ndarray): PanelAero's DLM output.
+ omegaalpha (float): Angular frequency of alpha.
+ omegabeta (float): Angular frequency of beta.
+ q_dyn (float): Dynamic pressure.
+ t (float): Time [s].
+ alpha_0 (float): Alpha amplitude.
+ beta_0 (float): Beta amplitude.
+
+ """
+
+ # Define the angles in time
+ alpha_angle = alpha(omegaalpha, t, alpha_0)
+ beta_angle = beta(omegabeta, t, beta_0)
+
+ c_a = cmath.cos(alpha_angle)
+ s_a = cmath.sin(alpha_angle)
+
+ # Rotations
+ rotation_alpha = np.array([ # -alpha(t) rotation
+ [c_a, 0, s_a],
+ [0, 1, 0],
+ [-s_a, 0, c_a],
+ ])
+
+ c_b = cmath.cos(beta_angle)
+ s_b = cmath.sin(beta_angle)
+
+ rotation_beta = np.array([ # -beta(t) rotation
+ [c_b, s_b, 0],
+ [-s_b, c_b, 0],
+ [0, 0, 1],
+ ])
+
+ # Apply the rotation to the normals (3, aerogrid['n'])
+ n_alpha = rotation_alpha.dot(aerogrid['N'].T)
+ n_beta = rotation_beta.dot(aerogrid['N'].T)
+
+ # Projection on x-axis
+ vector = np.array([1, 0, 0]) # Velocity = (V_inf, 0, 0) at alpha = beta = 0
+ n_alpha_x = vector.dot(n_alpha)
+ n_beta_x = vector.dot(n_beta)
+
+ # Induced downwash (aerogrid['n'], )
+ w_alpha = n_alpha_x
+ w_beta = n_beta_x
+
+ # Complex pressure coefficients (aerogrid['n'], )
+ cp_alpha = q_alpha_jj.dot(w_alpha)
+ cp_beta = q_beta_jj.dot(w_beta)
+
+ # Force on each panel (3, aerogrid['n'])
+ alphaforces = -q_dyn * n_alpha * aerogrid['A'] * cp_alpha
+ betaforces = -q_dyn * n_beta * aerogrid['A'] * cp_beta
+
+ return alphaforces, betaforces
+
+
+def get_mach_list(self, x_hinge: float) -> Tuple[List, List]:
+ """
+ Get list of machs where to compute the derivatives.
+ """
+ db = CeasiompyDb()
+ data = db.get_data(
+ table_name="derivatives_data",
+ columns=["mach"],
+ db_close=True,
+ filters=[
+ f"aircraft = '{self.aircraft_name}'",
+ "method = 'DLM'",
+ f"chord = {self.c}",
+ f"span = {self.s}",
+ f"x_ref = {x_hinge}",
+ "y_ref = 0.0",
+ "z_ref = 0.0"
+ ]
+ )
+
+ mach_set = {row[0] for row in data}
+
+ return list(set(self.mach_list) - mach_set), ", ".join(str(mach) for mach in list(mach_set))
+
+
+def compute_dot_derivatives(self) -> DataFrame:
+ """
+ Computes alpha and beta dot derivatives for SDSA.
+
+ Source:
+ https://www.overleaf.com/read/qjffvmtrzzhb#8fc920
+
+ Returns:
+ (DataFrame): Dot-derivatives per mach.
+
+ """
+
+ log.info("--- Loading Geometry into PanelAero ---")
+ data = []
+
+ # Load Geometry
+ wings_list = load_geometry(self)
+ model = AeroModel(wings_list)
+ model.build_aerogrid()
+ aerogrid = model.aerogrid
+
+ # Plot grid before doing calculations
+ if self.plot:
+ log.info(f"Plotting PanelAero's aerogrid of aircraft {self.aircraft_name}.")
+ plots = DetailedPlots(model)
+ plots.plot_aerogrid()
+
+ # Oscillation Parameters
+
+ # k = 0 for Steady
+ # 0 < k < 0.05 for Quasi-steady
+ # 0.05 < k < 0.2 for Unsteady
+ # 0.2 < k for Highly Unsteady
+ # for reference: https://en.wikipedia.org/wiki/Reduced_frequency.
+
+ k_nastran = 0.1 # = omega * length / (2 * velocity)
+
+ # Amplitudes
+ alpha_0 = beta_0 = 1e-6
+
+ # Time (Value does not matter).
+ # All computations are made in frequency domain.
+ t = 0.0
+
+ # Convert to radians
+ alpha_0 = math.radians(alpha_0)
+ beta_0 = math.radians(beta_0)
+
+ # Use k from equation (2.1) in https://elib.dlr.de/136536/1/DLR-IB-AE-GO-2020-137_V1.05.pdf.
+ k_alpha_model = 2 * k_nastran / self.c # = omega / velocity
+ k_beta_model = 2 * k_nastran / self.b # = omega / velocity
+
+ # Leading edge of airplane
+ x_le, _, _ = get_main_wing_le(model)
+ x_hinge = x_le + (self.c / 4) # Hinge point on x-axis
+ check_x_hinge(aerogrid, x_hinge)
+
+ mach_list, non_mach_str = get_mach_list(self, x_hinge)
+
+ log.info(f"--- Computing AIC Matrices for machs in {mach_list} ---")
+
+ for mach in mach_list:
+ log.info(f"--- Computing AIC Matrix for mach: {mach} ---")
+
+ # AIC Matrix np.identity(model.aerogrid['n'])
+ q_alpha_jj = DLM.calc_Qjj(aerogrid, Ma=mach, k=k_alpha_model)
+ q_beta_jj = DLM.calc_Qjj(aerogrid, Ma=mach, k=k_beta_model)
+
+ # Get angular frequencies
+ omegaalpha, omegabeta, q_dyn = compute_velocity_attributes(
+ self, mach, k_alpha_model, k_beta_model
+ )
+
+ # Get forces on each panels at angle (alpha(t), beta(t))
+ alphaforces, betaforces = compute_panel_forces(
+ aerogrid, q_alpha_jj, q_beta_jj, omegaalpha, omegabeta, q_dyn, t, alpha_0, beta_0
+ )
+
+ # Complex decomposition
+ alphaforces_real, alphaforces_imag = complex_decomposition(alphaforces)
+ betaforces_real, betaforces_imag = complex_decomposition(betaforces)
+
+ # Total force, sum on each panels (3, )
+ falpha = alphaforces.sum(axis=1)
+ fbeta = betaforces.sum(axis=1)
+
+ # Complex decomposition
+ falpha_real, falpha_imag = complex_decomposition(falpha)
+ fbeta_real, fbeta_imag = complex_decomposition(fbeta)
+
+ ############################################################
+ # Get derivatives at (angle, angle_dot) = (0, 0)
+ ############################################################
+
+ fx_alpha, fy_alpha, fz_alpha = access_angle_derivatives(
+ falpha_real, falpha_imag, omegaalpha, t, alpha_0
+ )
+ fx_beta, fy_beta, fz_beta = access_angle_derivatives(
+ fbeta_real, fbeta_imag, omegabeta, t, beta_0
+ )
+
+ m_alpha = compute_moments(
+ aerogrid=aerogrid,
+ forces=access_angle_derivatives_np(
+ alphaforces_real, alphaforces_imag, omegaalpha, t, alpha_0),
+ x_hinge=x_hinge,
+ )
+ m_beta = compute_moments(
+ aerogrid=aerogrid,
+ forces=access_angle_derivatives_np(
+ betaforces_real, betaforces_imag, omegabeta, t, beta_0),
+ x_hinge=x_hinge,
+ )
+
+ ############################################################
+ # Get dot derivatives at (angle, angle_dot) = (0, 0)
+ ############################################################
+
+ fx_alphadot, _, fz_alphadot = access_angle_dot_derivatives(
+ falpha_real, falpha_imag, omegaalpha, t, alpha_0
+ )
+ _, fy_betadot, _ = access_angle_dot_derivatives(
+ fbeta_real, fbeta_imag, omegabeta, t, beta_0
+ )
+
+ m_alphadot = compute_moments(
+ aerogrid=aerogrid,
+ forces=access_angle_dot_derivatives_np(
+ alphaforces_real, alphaforces_imag, omegaalpha, t, alpha_0),
+ x_hinge=x_hinge
+ )
+ m_betadot = compute_moments(
+ aerogrid=aerogrid,
+ forces=access_angle_dot_derivatives_np(
+ betaforces_real, betaforces_imag, omegabeta, t, beta_0),
+ x_hinge=x_hinge,
+ )
+
+ # Scale appropriately to access forces and moment coefficients
+ (
+ cx_alpha, cy_alpha, cz_alpha,
+ cl_alpha, cm_alpha, cn_alpha,
+ cx_beta, cy_beta, cz_beta,
+ cl_beta, cm_beta, cn_beta,
+ cm_alphaprim, cz_alphaprim, cx_alphaprim,
+ cy_betaprim, cl_betaprim, cn_betaprim,
+
+ ) = scale_coefficients(
+ self, q_dyn,
+ fx_alpha, fy_alpha, fz_alpha,
+ m_alpha[0], m_alpha[1], m_alpha[2],
+ fx_beta, fy_beta, fz_beta,
+ m_beta[0], m_beta[1], m_beta[2],
+ m_alphadot[1], fz_alphadot, fx_alphadot,
+ fy_betadot, m_betadot[0], m_betadot[2],
+ )
+
+ # Append data
+ data.append({
+ "mach": mach,
+ "cx_alpha": cx_alpha, "cy_alpha": cy_alpha, "cz_alpha": cz_alpha,
+ "cl_alpha": cl_alpha, "cm_alpha": cm_alpha, "cn_alpha": cn_alpha,
+ "cx_beta": cx_beta, "cy_beta": cy_beta, "cz_beta": cz_beta,
+ "cl_beta": cl_beta, "cm_beta": cm_beta, "cn_beta": cn_beta,
+ "cm_alphaprim": cm_alphaprim,
+ "cz_alphaprim": cz_alphaprim,
+ "cx_alphaprim": cx_alphaprim,
+ "cy_betaprim": cy_betaprim, "cl_betaprim": cl_betaprim, "cn_betaprim": cn_betaprim,
+ })
+
+ log.info(f"Finished computing alpha-dot derivatives for mach: {mach}.")
+
+ log.info("--- Finished computing alpha/beta-dot derivatives ---")
+
+ # Convert the data list to a DataFrame
+ df = DataFrame(data)
+
+ self.model = model
+
+ # Point where we compute moments
+ df["x_ref"], df["y_ref"], df["z_ref"] = x_hinge, 0.0, 0.0
+
+ # Save the DataFrame to a CSV file
+ df.to_csv(self.dynamic_stability_dir / "alpha_beta_dot_derivatives.csv", index=False)
+
+ return add_db_values(self, df, non_mach_str, x_hinge)
+
+
+def add_db_values(self, df: DataFrame, non_mach_str: str, x_hinge: float) -> DataFrame:
+ der_columns = [
+ "mach", "x_ref", "y_ref", "z_ref",
+ "cm_alphaprim", "cz_alphaprim", "cx_alphaprim",
+ "cy_betaprim", "cl_betaprim", "cn_betaprim"
+ ]
+ db = CeasiompyDb()
+ data = db.get_data(
+ table_name="derivatives_data",
+ columns=der_columns,
+ db_close=True,
+ filters=[
+ f"mach IN ({non_mach_str})",
+ f"aircraft = '{self.aircraft_name}'",
+ "method = 'DLM'",
+ f"chord = {self.c}",
+ f"span = {self.s}",
+ f"x_ref = {x_hinge}",
+ "y_ref = 0.0",
+ "z_ref = 0.0"
+ ],
+ )
+
+ return concat([df[der_columns], DataFrame(data, columns=der_columns)], ignore_index=True)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute !")
diff --git a/ceasiompy/DynamicStability/func/panelaeroconfig.py b/ceasiompy/DynamicStability/func/panelaeroconfig.py
new file mode 100644
index 000000000..5d42e29ef
--- /dev/null
+++ b/ceasiompy/DynamicStability/func/panelaeroconfig.py
@@ -0,0 +1,299 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Modified PanelAero methods, see: https://github.com/DLR-AE/PanelAero.
+
+| Author: Leon Deligny
+| Creation: 2025-01-27
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+import os
+import matplotlib
+
+import numpy as np
+import matplotlib.pyplot as plt
+
+from mpl_toolkits.mplot3d.art3d import Poly3DCollection
+
+from ceasiompy import log
+
+# =================================================================================================
+# BACKEND SETTING
+# =================================================================================================
+
+if os.environ.get('DISPLAY', '') == '':
+ matplotlib.use('Agg')
+else:
+ matplotlib.use('TkAgg')
+
+# =================================================================================================
+# CLASSES
+# =================================================================================================
+
+
+class AeroModel():
+ def __init__(self, wings_list):
+ self.aerogrid = None
+ self.caerocards = None
+ self.wings_list = wings_list
+
+ def build_aerogrid(self):
+ caero_grid, caero_panels, caerocards = self.read_CAERO(0)
+ ID = []
+ length = [] # length of panel
+ A = [] # area of one panel
+ N = [] # unit normal vector
+ offset_l = [] # 25% point l
+ offset_k = [] # 50% point k
+ offset_j = [] # 75% downwash control point j
+ offset_P1 = [] # Vortex point at 25% chord, 0% span
+ offset_P3 = [] # Vortex point at 25% chord, 100% span
+ r = [] # vector P1 to P3, span of panel
+
+ for i_panel in range(len(caero_panels['ID'])):
+
+ #
+ # l_2
+ # 4 o---------o 3
+ # | |
+ # u --> b_1 | l k j | b_2
+ # | |
+ # 1 o---------o 2
+ # y l_1
+ # |
+ # z.--- x
+
+ indices = [
+ np.where(caero_panels['cornerpoints'][i_panel][j] == caero_grid['ID'])[0][0]
+ for j in range(4)
+ ]
+
+ l_1 = caero_grid['offset'][indices[1]] - caero_grid['offset'][indices[0]]
+ l_2 = caero_grid['offset'][indices[2]] - caero_grid['offset'][indices[3]]
+ b_1 = caero_grid['offset'][indices[3]] - caero_grid['offset'][indices[0]]
+ b_2 = caero_grid['offset'][indices[2]] - caero_grid['offset'][indices[1]]
+ l_m = (l_1 + l_2) / 2.0
+ b_m = (b_1 + b_2) / 2.0
+
+ ID.append(caero_panels['ID'][i_panel])
+ length.append(l_m[0])
+ # A.append(l_m[0]*b_m[1])
+ A.append(np.linalg.norm(np.cross(l_m, b_m)))
+ normal = np.cross(l_1, b_1) / np.linalg.norm(np.cross(l_1, b_1))
+ if normal[2] < 0.0:
+ normal = -normal
+ N.append(normal)
+ offset_l.append(caero_grid['offset'][indices[0]] + 0.25 * l_m + 0.50 * b_1)
+ offset_k.append(caero_grid['offset'][indices[0]] + 0.50 * l_m + 0.50 * b_1)
+ offset_j.append(caero_grid['offset'][indices[0]] + 0.75 * l_m + 0.50 * b_1)
+ offset_P1.append(caero_grid['offset'][indices[0]] + 0.25 * l_1)
+ offset_P3.append(caero_grid['offset'][indices[3]] + 0.25 * l_2)
+ r.append((caero_grid['offset'][indices[3]] + 0.25 * l_2)
+ - (caero_grid['offset'][indices[0]] + 0.25 * l_1))
+
+ n = len(ID)
+ arange = np.arange(n * 6).reshape((n, 6))
+ set_l, set_k, set_j = arange, arange, arange
+
+ # Assure corner points are correctly generated
+ if not isinstance(caero_grid['ID'], np.ndarray):
+ raise TypeError("'caero_grid['ID']' must be a NumPy array.")
+ if caero_grid['ID'].ndim != 1:
+ raise ValueError("'caero_grid['ID']' must be a 1D array.")
+ if caero_grid['ID'].shape[0] != caero_grid['offset'].shape[0]:
+ raise ValueError("'ID' and 'offset' must have the same number of rows.")
+
+ reshaped_id = np.reshape(caero_grid['ID'], (-1, 1))
+ corner_points = np.hstack(reshaped_id, caero_grid['offset'])
+
+ aerogrid = {
+ 'ID': np.array(ID),
+ 'l': np.array(length),
+ 'A': np.array(A),
+ 'N': np.array(N),
+ 'offset_l': np.array(offset_l),
+ 'offset_k': np.array(offset_k),
+ 'offset_j': np.array(offset_j),
+ 'offset_P1': np.array(offset_P1),
+ 'offset_P3': np.array(offset_P3),
+ 'r': np.array(r),
+ 'set_l': set_l,
+ 'set_k': set_k,
+ 'set_j': set_j,
+ 'CD': caero_panels['CD'],
+ 'CP': caero_panels['CP'],
+ 'n': n,
+ 'coord_desc': 'bodyfixed',
+ 'cornerpoint_panels': caero_panels['cornerpoints'],
+ 'cornerpoint_grids': corner_points
+ }
+
+ self.aerogrid = aerogrid
+ self.caerocards = caerocards
+
+ def read_CAERO(self, i_file):
+ caerocards = []
+
+ # TODO: Improve the quality of aerocards by admitting several CPACS sections
+
+ for wing_i in self.wings_list:
+
+ caerocard = {
+ 'EID': 2 * int(wing_i["EID"]) + 1, # int
+ 'CP': int(wing_i["CP"]), # int
+ 'n_span': int(wing_i["n_span"]), # int
+ 'n_chord': int(wing_i["n_chord"]), # int
+ 'X1': wing_i["X1"], # np.array([float, float, float])
+ 'length12': wing_i["length12"], # float
+ 'X4': wing_i["X4"], # np.array([float, float, float])
+ 'length43': wing_i['length43'], # float
+ 'X2': wing_i["X1"] + np.array([wing_i["length12"], 0.0, 0.0]),
+ 'X3': wing_i["X4"] + np.array([wing_i['length43'], 0.0, 0.0]),
+ }
+
+ X1 = np.array([wing_i["X4"][0], -wing_i["X4"][1], wing_i["X4"][2]])
+ caerocard_sym = {
+ 'EID': 2 * int(wing_i["EID"]), # int
+ 'CP': int(wing_i["CP"]), # int
+ 'n_span': int(wing_i["n_span"]), # int
+ 'n_chord': int(wing_i["n_chord"]), # int
+ # Left to right
+ 'X1': X1,
+ 'length12': wing_i['length43'], # float
+ # Symmetry in x-z plane
+ 'X4': wing_i["X1"], # np.array([float, float, float])
+ 'length43': wing_i["length12"], # float
+ 'X2': X1 + np.array([wing_i['length43'], 0.0, 0.0]),
+ 'X3': wing_i["X1"] + np.array([wing_i["length12"], 0.0, 0.0]),
+ }
+
+ caerocards.append(caerocard_sym)
+ caerocards.append(caerocard)
+
+ # from CAERO cards, construct corner points... '
+ # then, combine four corner points to one panel
+ grid_ID = i_file * 100000 # the file number is used to set a range of grid IDs
+ grids = {'ID': [], 'offset': []}
+ panels = {"ID": [], 'CP': [], 'CD': [], "cornerpoints": []}
+
+ for caerocard in caerocards:
+ # calculate LE, Root and Tip vectors [x,y,z]^T
+ LE = caerocard['X4'] - caerocard['X1']
+ Root = caerocard['X2'] - caerocard['X1']
+ Tip = caerocard['X3'] - caerocard['X4']
+
+ if caerocard['n_chord'] == 0:
+ print('AEFACT cards are not supported by this reader.')
+ else:
+ # assume equidistant spacing
+ d_chord = np.linspace(0.0, 1.0, caerocard['n_chord'] + 1)
+
+ if caerocard['n_span'] == 0:
+ print('AEFACT cards are not supported by this reader.')
+ else:
+ # assume equidistant spacing
+ d_span = np.linspace(0.0, 1.0, caerocard['n_span'] + 1)
+
+ #######################################################################################
+ # Building matrix of corner points
+ #######################################################################################
+
+ # Instantiate the grid of size n_chord x n_span
+ grids_map = np.zeros((caerocard['n_chord'] + 1, caerocard['n_span'] + 1), dtype='int')
+
+ for i_strip in range(caerocard['n_span'] + 1):
+ for i_row in range(caerocard['n_chord'] + 1):
+ # Define offset
+ offset = caerocard['X1'] \
+ + LE * d_span[i_strip] \
+ + (Root * (1.0 - d_span[i_strip]) + Tip * d_span[i_strip]) * d_chord[i_row]
+ grids['offset'].append(offset)
+
+ grids['ID'].append(grid_ID)
+ grids_map[i_row, i_strip] = grid_ID
+
+ grid_ID += 1
+
+ # build panels from cornerpoints
+ # index based on n_boxes
+ panel_ID = caerocard['EID']
+ for i_strip in range(caerocard['n_span']):
+ for i_row in range(caerocard['n_chord']):
+ panels['ID'].append(panel_ID)
+ panels['CP'].append(caerocard['CP']) # applying CP of CAERO card to all grids
+ panels['CD'].append(caerocard['CP'])
+ panels['cornerpoints'].append([
+ grids_map[i_row, i_strip],
+ grids_map[i_row + 1, i_strip],
+ grids_map[i_row + 1, i_strip + 1],
+ grids_map[i_row, i_strip + 1]
+ ])
+
+ panel_ID += 1
+
+ panels['ID'] = np.array(panels['ID'])
+ panels['CP'] = np.array(panels['CP'])
+ panels['CD'] = np.array(panels['CD'])
+ panels['cornerpoints'] = np.array(panels['cornerpoints'])
+ grids['ID'] = np.array(grids['ID'])
+ grids['offset'] = np.array(grids['offset'])
+
+ return grids, panels, caerocards
+
+
+class DetailedPlots():
+
+ def __init__(self, model):
+ self.model = model
+
+ def plot_aerogrid(self, scalars=None, colormap='plasma'):
+ fig = plt.figure()
+ ax = fig.add_subplot(111, projection='3d')
+
+ for shell in self.model.aerogrid['cornerpoint_panels']:
+ vertices = [self.model.aerogrid['cornerpoint_grids'][id, 1:] for id in shell]
+ poly = Poly3DCollection([vertices], alpha=0.5)
+ if scalars is not None:
+ poly.set_array(np.array(scalars))
+ poly.set_cmap(colormap)
+ ax.add_collection3d(poly)
+
+ ax.set_xlabel('X')
+ ax.set_ylabel('Y')
+ ax.set_zlabel('Z')
+
+ # Set axis limits to zoom out and make the plot a cube
+ all_points = self.model.aerogrid['cornerpoint_grids'][:, 1:]
+ max_range = max(
+ all_points[:, 0].max() - all_points[:, 0].min(),
+ all_points[:, 1].max() - all_points[:, 1].min(),
+ all_points[:, 2].max() - all_points[:, 2].min(),
+ ) * 0.5
+
+ mid_x = (all_points[:, 0].max() + all_points[:, 0].min()) * 0.5
+ mid_y = (all_points[:, 1].max() + all_points[:, 1].min()) * 0.5
+ mid_z = (all_points[:, 2].max() + all_points[:, 2].min()) * 0.5
+
+ ax.set_xlim(mid_x - max_range, mid_x + max_range)
+ ax.set_ylim(mid_y - max_range, mid_y + max_range)
+ ax.set_zlim(mid_z - max_range, mid_z + max_range)
+
+ # Add a title to the plot
+ ax.set_title('Aerogrid Visualization')
+
+ plt.show()
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute.")
diff --git a/ceasiompy/DynamicStability/func/steadyderivatives.py b/ceasiompy/DynamicStability/func/steadyderivatives.py
new file mode 100644
index 000000000..999790a97
--- /dev/null
+++ b/ceasiompy/DynamicStability/func/steadyderivatives.py
@@ -0,0 +1,177 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Steady-derivatives computations through PyAVL.
+
+
+| Author: Leon Deligny
+| Creation: 2025-Feb-12
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+from pandas import concat
+
+from typing import Tuple
+from pandas import DataFrame
+from ceasiompy.Database.func.storing import CeasiompyDb
+
+from ceasiompy import log
+from ceasiompy.DynamicStability import ALT
+from ceasiompy.PyAVL import SOFTWARE_NAME as AVL_SOFTWARE
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def format_aero_data(df: DataFrame) -> DataFrame:
+ """
+ Format the data into the specified sequence.
+ """
+ formatted_data = []
+
+ # Group by Mach number
+ mach_groups = df.groupby("mach")
+ for _, mach_group in mach_groups:
+ # Sort by alpha
+ mach_group = mach_group.sort_values(by="alpha")
+
+ # Extract the 9 series
+ formatted_data.append(mach_group[(mach_group["beta"] == 0)
+ & (mach_group["pb_2V"] == 0)
+ & (mach_group["qc_2V"] == 0)
+ & (mach_group["rb_2V"] == 0)])
+
+ # Sort by beta with q=p=r=0
+ formatted_data.append(mach_group[(mach_group["pb_2V"] == 0)
+ & (mach_group["qc_2V"] == 0)
+ & (mach_group["rb_2V"] == 0)].sort_values(by="beta"))
+
+ # Sort by q with beta=p=r=0
+ formatted_data.append(mach_group[(mach_group["beta"] == 0)
+ & (mach_group["pb_2V"] == 0)
+ & (mach_group["rb_2V"] == 0)].sort_values(by="qc_2V"))
+
+ # Sort by p with beta=q=r=0
+ formatted_data.append(mach_group[(mach_group["beta"] == 0)
+ & (mach_group["qc_2V"] == 0)
+ & (mach_group["rb_2V"] == 0)].sort_values(by="pb_2V"))
+
+ # Sort by r with beta=q=p=0
+ formatted_data.append(mach_group[(mach_group["beta"] == 0)
+ & (mach_group["pb_2V"] == 0)
+ & (mach_group["qc_2V"] == 0)].sort_values(by="rb_2V"))
+
+ return concat(formatted_data, ignore_index=True)
+
+
+def format_ctrl_data(df: DataFrame) -> DataFrame:
+ """
+ Format the data into the specified sequence using elevator, rudder, and aileron.
+ """
+ formatted_data = []
+
+ # Group by Mach number
+ mach_groups = df.groupby("mach")
+ for _, mach_group in mach_groups:
+ # Sort by alpha
+ mach_group = mach_group.sort_values(by="alpha")
+
+ # Extract the 9 series
+ formatted_data.append(mach_group[(mach_group["elevator"] == 0)
+ & (mach_group["rudder"] == 0)
+ & (mach_group["aileron"] == 0)])
+
+ # Sort by elevator with rudder=aileron=0
+ formatted_data.append(mach_group[
+ (mach_group["rudder"] == 0)
+ & (mach_group["aileron"] == 0)].sort_values(by="elevator")
+ )
+
+ # Sort by rudder with elevator=aileron=0
+ formatted_data.append(mach_group[(mach_group["elevator"] == 0)
+ & (mach_group["aileron"] == 0)].sort_values(by="rudder"))
+
+ # Sort by aileron with elevator=rudder=0
+ formatted_data.append(mach_group[(mach_group["elevator"] == 0)
+ & (mach_group["rudder"] == 0)].sort_values(by="aileron"))
+
+ return concat(formatted_data, ignore_index=True)
+
+
+def get_tables_values(
+ self
+) -> Tuple[DataFrame, DataFrame]:
+ """
+ Go access steady derivatives in CPACS at xPaths table_xpath and ctrltable_xpath.
+
+ Returns:
+ df (DataFrame): AC in function of alpha, beta, mach, p, q, r.
+ table_xpath (str): xPath to table data.
+ ctrltable_xpath (str): xPath to ctrltable data.
+
+ """
+ log.info("--- Get Tables values ---")
+
+ if self.software_data == AVL_SOFTWARE:
+ table_name = "avl_data"
+ else:
+ log.warning(f"software {self.software_data} not implemented yet.")
+
+ aero_columns = [
+ "alpha", "mach", "beta", "pb_2V", "qc_2V", "rb_2V",
+ "cl", "cd", "cms", "cs", "cmd", "cml"
+ ]
+
+ # Retrieve data from db
+ ceasiompy_db = CeasiompyDb()
+ data = ceasiompy_db.get_data(
+ table_name=table_name,
+ columns=aero_columns,
+ db_close=False,
+ filters=[
+ f"mach IN ({self.mach_str})",
+ f"aircraft = '{self.aircraft_name}'",
+ f"alt = {ALT}"
+ ]
+ )
+
+ # Convert the data list to a DataFrame
+ aero_df = DataFrame(data, columns=aero_columns)
+
+ ctrl_columns = [
+ "alpha", "mach", "elevator", "rudder", "aileron",
+ "cl", "cd", "cms", "cs", "cmd", "cml"
+ ]
+
+ data = ceasiompy_db.get_data(
+ table_name=table_name,
+ columns=ctrl_columns,
+ db_close=True,
+ filters=[
+ f"mach IN ({self.mach_str})",
+ f"aircraft = '{self.aircraft_name}'",
+ f"alt = {ALT}"
+ ]
+ )
+
+ # Convert the data list to a DataFrame
+ ctrl_df = DataFrame(data, columns=ctrl_columns)
+
+ log.info("--- Finished retrieving the Tables values ---")
+
+ return format_aero_data(aero_df), format_ctrl_data(ctrl_df)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute.")
diff --git a/ceasiompy/DynamicStability/func/utils.py b/ceasiompy/DynamicStability/func/utils.py
new file mode 100644
index 000000000..05e06411f
--- /dev/null
+++ b/ceasiompy/DynamicStability/func/utils.py
@@ -0,0 +1,110 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Dimensionalization and non-dimensionalization methods of pitch/roll/yaw rates.
+
+
+| Author: Leon Deligny
+| Creation: 2025-Feb-12
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+import numpy as np
+
+from typing import (
+ List,
+ Tuple,
+)
+
+from numpy import (
+ ndarray,
+ complex128,
+)
+
+from ceasiompy import log
+
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def exp_i(omega: float, t: float) -> complex128:
+ """
+ Computes e^{iwt}.
+ """
+ return np.exp(1j * t * omega)
+
+
+def alpha(omega: float, t: float, alpha_0: float) -> complex:
+ """
+ Defines alpha oscillations for Doublet Lattice method.
+ alpha(t) = alpha_0 e^{iwt}
+ """
+
+ return alpha_0 * exp_i(omega, t)
+
+
+def beta(omega: float, t: float, beta_0: float) -> complex:
+ """
+ Defines beta oscillations for Doublet Lattice method.
+ beta(t) = beta_0 e^{iwt}
+ """
+
+ return beta_0 * exp_i(omega, t)
+
+
+def dalpha_dt(omega: float, t: float, alpha_0: float) -> complex:
+ """
+ Computes the derivative of alpha wrt to time at time t.
+ d alpha / dt (t) = i w alpha_0 e^{iwt}.
+ """
+ return 1j * omega * alpha(omega, t, alpha_0)
+
+
+def dbeta_dt(omega: float, t: float, beta_0: float) -> complex:
+ """
+ Computes the derivative of beta wrt to time at time t.
+ d beta / dt (t) = i w beta_0 e^{iwt}.
+ """
+
+ return 1j * omega * beta(omega, t, beta_0)
+
+
+def complex_decomposition(array: ndarray) -> Tuple[ndarray, ndarray]:
+ """
+ Decomposes a complex numpy array into its real and imaginary parts.
+ """
+
+ real_part = np.real(array)
+ imaginary_part = np.imag(array)
+
+ return real_part, imaginary_part
+
+
+def complex_cross(a: ndarray, b: ndarray) -> ndarray:
+ """
+ Cross product of two complex ndarrays.
+ """
+ real_part = np.cross(a.real, b.real) - np.cross(a.imag, b.imag)
+ imag_part = np.cross(a.real, b.imag) + np.cross(a.imag, b.real)
+
+ return real_part + 1j * imag_part
+
+
+def sdsa_format(list_: List) -> str:
+ return " ".join(map(str, list_))
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute.")
diff --git a/ceasiompy/ExportCSV/README.md b/ceasiompy/ExportCSV/README.md
new file mode 100644
index 000000000..3e598c5ef
--- /dev/null
+++ b/ceasiompy/ExportCSV/README.md
@@ -0,0 +1,35 @@
+
+
+# DynamicStability
+
+**Categories:** ExportCSV, Export data in CSV format
+
+**State**: :heavy_check_mark:
+
+## Inputs
+
+CPACS file with more then 1 aeromap.
+
+## Analyses
+
+Export the aircraft's aeromaps data in a .csv file.
+
+## Outputs
+
+Outputs a CSV file.
+
+## Installation or requirements
+
+No requirement. ExportCSV is a native module of CEASIOMpy.
+
+## Not implemented
+
+NA
+
+## Limitations
+
+NA
+
+## More information
+
+NA
\ No newline at end of file
diff --git a/ceasiompy/ExportCSV/ToolInput/.keep b/ceasiompy/ExportCSV/ToolInput/.keep
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/ceasiompy/ExportCSV/ToolInput/.keep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ceasiompy/ExportCSV/ToolOutput/.keep b/ceasiompy/ExportCSV/ToolOutput/.keep
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/ceasiompy/ExportCSV/ToolOutput/.keep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ceasiompy/ExportCSV/__init__.py b/ceasiompy/ExportCSV/__init__.py
index e69de29bb..43b77b6ef 100644
--- a/ceasiompy/ExportCSV/__init__.py
+++ b/ceasiompy/ExportCSV/__init__.py
@@ -0,0 +1,45 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for ExportCSV module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+from ceasiompy import log
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = True
+
+# ===== Include GUI =====
+include_gui = True
+
+# ===== Add a Results Directory =====
+RES_DIR = True
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/ExportCSV/__specs__.py b/ceasiompy/ExportCSV/__specs__.py
index 195af1ed8..608898b6a 100644
--- a/ceasiompy/ExportCSV/__specs__.py
+++ b/ceasiompy/ExportCSV/__specs__.py
@@ -1,24 +1,35 @@
-from pathlib import Path
-from ceasiompy.utils.moduleinterfaces import CPACSInOut
-from ceasiompy.utils.commonxpath import EXPORT_XPATH
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
-# ===== Module Status =====
-# True if the module is active
-# False if the module is disabled (not working or not ready)
-module_status = True
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-# ===== Results directory path =====
+GUI Interface of ExportCSV.
-RESULTS_DIR = Path("Results", "AeroCoefficients")
+| Author: Leon Deligny
+| Creation: 19-Mar-2025
-# ===== CPACS inputs and outputs =====
+"""
-cpacs_inout = CPACSInOut()
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from ceasiompy.utils.moduleinterfaces import CPACSInOut
-include_gui = False
+from ceasiompy import log
+from ceasiompy.ExportCSV import include_gui
+from ceasiompy.utils.commonxpath import EXPORT_XPATH
+
+# ==============================================================================
+# VARIABLE
+# ==============================================================================
-# ----- Input -----
+cpacs_inout = CPACSInOut()
+
+# ==============================================================================
+# GUI INPUTS
+# ==============================================================================
cpacs_inout.add_input(
var_name="",
@@ -26,9 +37,14 @@
default_value=None,
descr="List of aeroMap to plot",
xpath=EXPORT_XPATH + "/aeroMapToExport",
- gui=True,
+ gui=include_gui,
gui_name="__AEROMAP_CHECKBOX",
gui_group="Aeromap settings",
)
-# ----- Output -----
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/ExportCSV/exportcsv.py b/ceasiompy/ExportCSV/exportcsv.py
index edd0bd999..6131ac785 100644
--- a/ceasiompy/ExportCSV/exportcsv.py
+++ b/ceasiompy/ExportCSV/exportcsv.py
@@ -5,15 +5,11 @@
Module to export Aeromap (or other data?) to CSV
-Python version: >=3.8
| Author: Aidan Jungo
| Creation: 2021-04-07
-
-TODO:
-
- * export other data...
- *
+| Modified: Leon Deligny
+| Date: 03 April 2025
"""
@@ -23,77 +19,41 @@
from pathlib import Path
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.ceasiompyutils import get_aeromap_list_from_xpath, get_results_directory
-from ceasiompy.utils.moduleinterfaces import (
- check_cpacs_input_requirements,
- get_toolinput_file_path,
- get_tooloutput_file_path,
-)
-from ceasiompy.utils.commonxpath import CEASIOMPY_XPATH
-from cpacspy.cpacspy import CPACS
from cpacspy.cpacsfunctions import get_value_or_default
+from ceasiompy.utils.ceasiompyutils import (
+ call_main,
+ get_aeromap_list_from_xpath,
+)
-log = get_logger()
-
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
-
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
+from cpacspy.cpacspy import CPACS
+from cpacspy.aeromap import AeroMap
+from ceasiompy import log
+from ceasiompy.ExportCSV import MODULE_NAME
+from ceasiompy.utils.commonxpath import AEROMAP_TO_EXPORT_XPATH
# =================================================================================================
-# FUNCTIONS
+# MAIN
# =================================================================================================
-def export_aeromaps(cpacs_path, cpacs_out_path):
+def main(cpacs: CPACS, wkdir: Path) -> None:
+ tixi = cpacs.tixi
- cpacs = CPACS(cpacs_path)
-
- aeromap_to_export_xpath = CEASIOMPY_XPATH + "/export/aeroMapToExport"
- aeromap_uid_list = get_aeromap_list_from_xpath(cpacs, aeromap_to_export_xpath)
+ aeromap_uid_list = get_aeromap_list_from_xpath(cpacs, AEROMAP_TO_EXPORT_XPATH)
if not aeromap_uid_list:
aeromap_uid_list = get_value_or_default(
- cpacs.tixi, aeromap_to_export_xpath, "DefaultAeromap"
+ tixi, AEROMAP_TO_EXPORT_XPATH, "DefaultAeromap"
)
- results_dir = get_results_directory("ExportCSV")
-
for aeromap_uid in aeromap_uid_list:
- aeromap = cpacs.get_aeromap_by_uid(aeromap_uid)
-
- csv_path = Path(results_dir, f"{aeromap_uid}.csv")
+ aeromap: AeroMap = cpacs.get_aeromap_by_uid(aeromap_uid)
+ csv_path = Path(wkdir, f"{aeromap_uid}.csv")
aeromap.export_csv(csv_path)
- log.info(f"Aeromap(s) has been saved to {csv_path}")
-
- cpacs.save_cpacs(cpacs_out_path, overwrite=True)
-
-
-# =================================================================================================
-# MAIN
-# =================================================================================================
-
-
-def main(cpacs_path, cpacs_out_path):
-
- log.info("----- Start of " + MODULE_NAME + " -----")
-
- export_aeromaps(cpacs_path, cpacs_out_path)
-
- log.info("----- End of " + MODULE_NAME + " -----")
+ log.info(f"Aeromap(s) have been saved to {csv_path}")
if __name__ == "__main__":
-
- cpacs_path = get_toolinput_file_path(MODULE_NAME)
- cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
-
- main(cpacs_path, cpacs_out_path)
-
- check_cpacs_input_requirements(cpacs_path)
+ call_main(main, MODULE_NAME)
diff --git a/ceasiompy/ExportCSV/tests/test_exportcsv.py b/ceasiompy/ExportCSV/tests/test_exportcsv.py
index 47b9ab6fe..d4eba2dff 100644
--- a/ceasiompy/ExportCSV/tests/test_exportcsv.py
+++ b/ceasiompy/ExportCSV/tests/test_exportcsv.py
@@ -5,62 +5,55 @@
Test functions for 'ceasiompy/ExportCSV/exportcsv.py'
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2021-12-09
+| Modified: Leon Deligny
+| Date: 25 March 2025
+
"""
# =================================================================================================
# IMPORTS
# =================================================================================================
-import shutil
-from pathlib import Path
-
-import pytest
-from ceasiompy.ExportCSV.exportcsv import export_aeromaps
-from ceasiompy.utils.ceasiompyutils import get_results_directory
-
-MODULE_DIR = Path(__file__).parent
-CPACS_IN_PATH = Path(MODULE_DIR, "D150_simple.xml")
+from ceasiompy.utils.decorators import log_test
+from ceasiompy.utils.ceasiompyutils import current_workflow_dir
+from ceasiompy.ExportCSV.exportcsv import main as export_aeromaps
+from pathlib import Path
+from unittest import main
+from ceasiompy.utils.ceasiompytest import CeasiompyTest
# =================================================================================================
-# FUNCTIONS
+# CLASS
# =================================================================================================
-@pytest.fixture(autouse=True)
-def change_test_dir(request, monkeypatch):
-
- monkeypatch.chdir(request.fspath.dirname)
+class TestExportCSV(CeasiompyTest):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ cls.wkdir = current_workflow_dir()
+ cls.csv_path = Path(cls.wkdir, "test_apm.csv")
- global CSV_FILE_PATH
- results_dir = get_results_directory("ExportCSV")
- CSV_FILE_PATH = Path(results_dir, "test_apm.csv")
+ @log_test
+ def test_export_aeromaps(self):
+ """Test function 'exportcsv' function."""
- yield
+ export_aeromaps(self.test_cpacs, self.wkdir)
- # Clean up
- shutil.rmtree(CSV_FILE_PATH.parent.parent)
+ # Read and check csv file
+ with open(self.csv_path, "r") as csv_file:
+ lines = csv_file.readlines()
-
-def test_export_aeromaps():
- """Test function 'exportcsv' function."""
-
- export_aeromaps(CPACS_IN_PATH, CPACS_IN_PATH)
-
- # Read and check csv file
- with open(CSV_FILE_PATH, "r") as csv_file:
- lines = csv_file.readlines()
-
- assert lines[0] == "altitude,machNumber,angleOfSideslip,angleOfAttack,cd,cl,cs,cmd,cml,cms\n"
- assert lines[1] == "0,0.3,0,0,0.01,0.1,0.001,NaN,NaN,NaN\n"
- assert lines[2] == "0,0.3,0,10,0.01,0.1,0.001,NaN,NaN,NaN\n"
- assert lines[3] == "0,0.3,10,0,0.01,0.1,0.001,NaN,NaN,NaN\n"
- assert lines[4] == "0,0.3,10,10,0.01,0.1,0.001,NaN,NaN,NaN\n"
+ features_str = "altitude,machNumber,angleOfSideslip,angleOfAttack"
+ assert lines[0] == f"{features_str},cd,cl,cs,cmd,cml,cms\n"
+ assert lines[1] == "0,0.3,0,0,0.01,0.1,0.001,NaN,NaN,NaN\n"
+ assert lines[2] == "0,0.3,0,10,0.01,0.1,0.001,NaN,NaN,NaN\n"
+ assert lines[3] == "0,0.3,10,0,0.01,0.1,0.001,NaN,NaN,NaN\n"
+ assert lines[4] == "0,0.3,10,10,0.01,0.1,0.001,NaN,NaN,NaN\n"
# =================================================================================================
@@ -68,7 +61,4 @@ def test_export_aeromaps():
# =================================================================================================
if __name__ == "__main__":
-
- print("Running Test ExportCSV")
- print("To run test use the following command:")
- print(">> pytest -v")
+ main(verbosity=0)
diff --git a/ceasiompy/ModuleTemplate/README.md b/ceasiompy/ModuleTemplate/README.md
index f20754ecf..fa5ef1d90 100644
--- a/ceasiompy/ModuleTemplate/README.md
+++ b/ceasiompy/ModuleTemplate/README.md
@@ -37,4 +37,5 @@ ModuleTemplate is limited in every aspect.
## More information
+
*
diff --git a/ceasiompy/ModuleTemplate/ToolInput/.keep b/ceasiompy/ModuleTemplate/ToolInput/.keep
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/ceasiompy/ModuleTemplate/ToolInput/.keep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ceasiompy/ModuleTemplate/ToolInput/ToolInput.xml b/ceasiompy/ModuleTemplate/ToolInput/ToolInput.xml
deleted file mode 100755
index b407294db..000000000
--- a/ceasiompy/ModuleTemplate/ToolInput/ToolInput.xml
+++ /dev/null
@@ -1,576 +0,0 @@
-
-
-
- Cpacs2Test
- Simple Wing for unit testing
- Martin Siggel
- 2012-10-09T15:12:47
- 0.2
- 3.0
-
-
- Converted to cpacs 3.0 using cpacs2to3 - does not include structure update
- cpacs2to3
- 2018-01-15T09:22:57
- 0.2
- 3.0
-
-
- Add this update
- Aidan
- 2018-10-03T09:00:01
- 0.3
- 3.0
-
-
-
-
-
-
- Cpacs2Test
-
- 1
- 1
-
- 0
- 0
- 0
-
-
-
-
- name
- description
-
-
- 1.0
- 0.5
- 0.5
-
-
- 0.0
- 0.0
- 0.0
-
-
- 0.0
- 0.0
- 0.0
-
-
-
-
- D150_Fuselage_1Section1
-
-
- 1.0
- 1.0
- 1.0
-
-
- 0.0
- 0.0
- 0.0
-
-
- 0
- 0
- 0
-
-
-
-
- D150_Fuselage_1Section1
- fuselageCircleProfileuID
-
-
- 1.0
- 1.0
- 1.0
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- D150_Fuselage_1Section2
-
-
- 1.0
- 1.0
- 1.0
-
-
- 0.0
- 0.0
- 0.0
-
-
- 0.5
- 0
- 0
-
-
-
-
- D150_Fuselage_1Section2
- fuselageCircleProfileuID
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- D150_Fuselage_1Section3
-
-
- 1.0
- 1.0
- 1.0
-
-
- 0.0
- 0.0
- 0.0
-
-
- 0
- 0
- 0
-
-
-
-
- D150_Fuselage_1Section3
- fuselageCircleProfileuID
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- D150_Fuselage_1Section4
-
-
- 1.0
- 1.0
- 1.0
-
-
- 0.0
- 0.0
- 0.0
-
-
- 0
- 0
- 0
-
-
-
-
- D150_Fuselage_1Section4
- fuselageCircleProfileuID
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
-
-
-
-
- D150_Fuselage_1Positioning1
- -0.5
- 90
- 0
- D150_Fuselage_1Section1ID
-
-
- D150_Fuselage_1Positioning3
- 2
- 90
- 0
- D150_Fuselage_1Section1ID
- D150_Fuselage_1Section2ID
-
-
- D150_Fuselage_1Positioning3
- 2
- 90
- 0
- D150_Fuselage_1Section2ID
- D150_Fuselage_1Section3ID
-
-
- D150_Fuselage_1Positioning4
- 2
- 90
- 0
- D150_Fuselage_1Section3ID
- D150_Fuselage_1Section4ID
-
-
-
-
- D150_Fuselage_1Segment1
- D150_Fuselage_1Section1IDElement1
- D150_Fuselage_1Section2IDElement1
-
-
- D150_Fuselage_1Segment2
- D150_Fuselage_1Section2IDElement1
- D150_Fuselage_1Section3IDElement1
-
-
- D150_Fuselage_1Segment3
- D150_Fuselage_1Section3IDElement1
- D150_Fuselage_1Section4IDElement1
-
-
-
-
-
-
- Wing
- SimpleFuselage
- This wing has been generated to test CATIA2CPACS.
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
- Cpacs2Test - Wing Section 1
- Cpacs2Test - Wing Section 1
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
- Cpacs2Test - Wing Section 1 Main Element
- Cpacs2Test - Wing Section 1 Main Element
- NACA0012
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Cpacs2Test - Wing Section 2
- Cpacs2Test - Wing Section 2
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
- Cpacs2Test - Wing Section 2 Main Element
- Cpacs2Test - Wing Section 2 Main Element
- NACA0012
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Cpacs2Test - Wing Section 3
- Cpacs2Test - Wing Section 3
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
- Cpacs2Test - Wing Section 3 Main Element
- Cpacs2Test - Wing Section 3 Main Element
- NACA0012
-
-
- 0.5
- 0.5
- 0.5
-
-
- 0
- 0
- 0
-
-
- 0.5
- 0
- 0
-
-
-
-
-
-
-
-
- Cpacs2Test - Wing Section 1 Positioning
- Cpacs2Test - Wing Section 1 Positioning
- 0
- 0
- 0
- Cpacs2Test_Wing_Sec1
-
-
- Cpacs2Test - Wing Section 2 Positioning
- Cpacs2Test - Wing Section 2 Positioning
- 1
- 0
- 0
- Cpacs2Test_Wing_Sec1
- Cpacs2Test_Wing_Sec2
-
-
- Cpacs2Test - Wing Section 3 Positioning
- Cpacs2Test - Wing Section 3 Positioning
- 1
- 0
- 0
- Cpacs2Test_Wing_Sec2
- Cpacs2Test_Wing_Sec3
-
-
-
-
- Fuselage Segment from Cpacs2Test - Wing Section 1 Main Element to Cpacs2Test - Wing Section 2 Main Element
- Fuselage Segment from Cpacs2Test - Wing Section 1 Main Element to Cpacs2Test - Wing Section 2 Main Element
- Cpacs2Test_Wing_Sec1_El1
- Cpacs2Test_Wing_Sec2_El1
-
-
- Fuselage Segment from Cpacs2Test - Wing Section 2 Main Element to Cpacs2Test - Wing Section 3 Main Element
- Fuselage Segment from Cpacs2Test - Wing Section 2 Main Element to Cpacs2Test - Wing Section 3 Main Element
- Cpacs2Test_Wing_Sec2_El1
- Cpacs2Test_Wing_Sec3_El1
-
-
-
-
- Wing_CS1
- Cpacs2Test_Wing_Sec1_El1
- Cpacs2Test_Wing_Sec3_El1
-
-
-
-
- MySkinMat
- 0.0
-
-
-
-
-
-
- MyCellMat
- 0.0
-
-
-
- 0.8
- 0.8
-
-
- 1.0
- 1.0
-
-
- 0.0
- 0.0
-
-
- 0.5
- 0.5
-
- |
-
-
-
-
-
- MySkinMat
-
-
-
-
-
-
-
-
-
-
-
-
-
- NACA0.00.00.12
- NACA 4 Series Profile
-
- 1.0;0.9875;0.975;0.9625;0.95;0.9375;0.925;0.9125;0.9;0.8875;0.875;0.8625;0.85;0.8375;0.825;0.8125;0.8;0.7875;0.775;0.7625;0.75;0.7375;0.725;0.7125;0.7;0.6875;0.675;0.6625;0.65;0.6375;0.625;0.6125;0.6;0.5875;0.575;0.5625;0.55;0.5375;0.525;0.5125;0.5;0.4875;0.475;0.4625;0.45;0.4375;0.425;0.4125;0.4;0.3875;0.375;0.3625;0.35;0.3375;0.325;0.3125;0.3;0.2875;0.275;0.2625;0.25;0.2375;0.225;0.2125;0.2;0.1875;0.175;0.1625;0.15;0.1375;0.125;0.1125;0.1;0.0875;0.075;0.0625;0.05;0.0375;0.025;0.0125;0.0;0.0125;0.025;0.0375;0.05;0.0625;0.075;0.0875;0.1;0.1125;0.125;0.1375;0.15;0.1625;0.175;0.1875;0.2;0.2125;0.225;0.2375;0.25;0.2625;0.275;0.2875;0.3;0.3125;0.325;0.3375;0.35;0.3625;0.375;0.3875;0.4;0.4125;0.425;0.4375;0.45;0.4625;0.475;0.4875;0.5;0.5125;0.525;0.5375;0.55;0.5625;0.575;0.5875;0.6;0.6125;0.625;0.6375;0.65;0.6625;0.675;0.6875;0.7;0.7125;0.725;0.7375;0.75;0.7625;0.775;0.7875;0.8;0.8125;0.825;0.8375;0.85;0.8625;0.875;0.8875;0.9;0.9125;0.925;0.9375;0.95;0.9625;0.975;0.9875;1.0
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- -0.00126;-0.0030004180415;-0.00471438572941;-0.00640256842113;-0.00806559133343;-0.00970403933653;-0.0113184567357;-0.0129093470398;-0.0144771727147;-0.0160223549226;-0.0175452732434;-0.0190462653789;-0.0205256268372;-0.0219836105968;-0.0234204267471;-0.024836242105;-0.0262311798047;-0.0276053188583;-0.0289586936852;-0.0302912936071;-0.0316030623052;-0.0328938972373;-0.0341636490097;-0.0354121207001;-0.0366390671268;-0.0378441940595;-0.0390271573644;-0.0401875620783;-0.0413249614032;-0.042438855614;-0.043528690869;-0.0445938579126;-0.0456336906587;-0.04664746464;-0.0476343953088;-0.0485936361694;-0.0495242767241;-0.0504253402064;-0.0512957810767;-0.0521344822472;-0.0529402520006;-0.0537118205596;-0.0544478362583;-0.0551468612564;-0.0558073667285;-0.0564277274483;-0.0570062156697;-0.0575409941929;-0.0580301084765;-0.0584714776309;-0.0588628840933;-0.059201961739;-0.0594861821311;-0.0597128385384;-0.059879027262;-0.0599816256958;-0.060017266394;-0.059982306219;-0.05987278938;-0.0596844028137;-0.059412421875;-0.059051643633;-0.0585963041308;-0.0580399746271;-0.0573754299024;-0.0565944788455;-0.0556877432118;-0.054644363746;-0.0534516022043;-0.0520942903127;-0.0505540468987;-0.0488081315259;-0.0468277042382;-0.0445750655553;-0.0419990347204;-0.0390266537476;-0.0355468568262;-0.0313738751622;-0.0261471986426;-0.0189390266528;0.0;0.0189390266528;0.0261471986426;0.0313738751622;0.0355468568262;0.0390266537476;0.0419990347204;0.0445750655553;0.0468277042382;0.0488081315259;0.0505540468987;0.0520942903127;0.0534516022043;0.054644363746;0.0556877432118;0.0565944788455;0.0573754299024;0.0580399746271;0.0585963041308;0.059051643633;0.059412421875;0.0596844028137;0.05987278938;0.059982306219;0.060017266394;0.0599816256958;0.059879027262;0.0597128385384;0.0594861821311;0.059201961739;0.0588628840933;0.0584714776309;0.0580301084765;0.0575409941929;0.0570062156697;0.0564277274483;0.0558073667285;0.0551468612564;0.0544478362583;0.0537118205596;0.0529402520006;0.0521344822472;0.0512957810767;0.0504253402064;0.0495242767241;0.0485936361694;0.0476343953088;0.04664746464;0.0456336906587;0.0445938579126;0.043528690869;0.042438855614;0.0413249614032;0.0401875620783;0.0390271573644;0.0378441940595;0.0366390671268;0.0354121207001;0.0341636490097;0.0328938972373;0.0316030623052;0.0302912936071;0.0289586936852;0.0276053188583;0.0262311798047;0.024836242105;0.0234204267471;0.0219836105968;0.0205256268372;0.0190462653789;0.0175452732434;0.0160223549226;0.0144771727147;0.0129093470398;0.0113184567357;0.00970403933653;0.00806559133343;0.00640256842113;0.00471438572941;0.0030004180415;0.00126
-
-
-
-
-
- Circle
- Profile build up from set of Points on Circle where may Dimensions are 1..-1
-
- 0.0;0.0;0.0;0.0;0.0
- 0.0;1.0;0.0;-1.0;0.0
- 1.0;0.0;-1.0;0.0;1.0
-
-
-
-
-
-
-
diff --git a/ceasiompy/ModuleTemplate/ToolOutput/.keep b/ceasiompy/ModuleTemplate/ToolOutput/.keep
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/ceasiompy/ModuleTemplate/ToolOutput/.keep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ceasiompy/ModuleTemplate/__init__.py b/ceasiompy/ModuleTemplate/__init__.py
index e69de29bb..c008db87b 100644
--- a/ceasiompy/ModuleTemplate/__init__.py
+++ b/ceasiompy/ModuleTemplate/__init__.py
@@ -0,0 +1,50 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for CLCalculator module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+from ceasiompy import log
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+# True if the module is active.
+# False if the module is disabled (not working or not ready).
+module_status = False
+
+# ===== Include GUI =====
+# True if you want to add a GUI for this module.
+# False if module is desactivated or no GUI to be displayed.
+include_gui = False
+
+# ===== Add a Results Directory =====
+RES_DIR = False
+
+# ===== Include Module's name =====
+# MODULE_NAME == 'ModuleTemplate'.
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/ModuleTemplate/__specs__.py b/ceasiompy/ModuleTemplate/__specs__.py
index fe73a31de..6027fe1ce 100644
--- a/ceasiompy/ModuleTemplate/__specs__.py
+++ b/ceasiompy/ModuleTemplate/__specs__.py
@@ -1,18 +1,40 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+GUI Interface of ModuleTemplate.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
from ceasiompy.utils.moduleinterfaces import CPACSInOut
-from ceasiompy.utils.commonxpath import CEASIOMPY_XPATH, FUSELAGES_XPATH
-# ===== Module Status =====
-# True if the module is active
-# False if the module is disabled (not working or not ready)
-module_status = False # Because it is just an example not a real module
+from ceasiompy import log
+from ceasiompy.ModuleTemplate import include_gui
-# ===== CPACS inputs and outputs =====
+from ceasiompy.utils.commonxpath import (
+ CEASIOMPY_XPATH,
+ FUSELAGES_XPATH,
+)
-cpacs_inout = CPACSInOut()
+# ==============================================================================
+# VARIABLE
+# ==============================================================================
-include_gui = False
+# ===== CPACS inputs and outputs =====
+cpacs_inout = CPACSInOut()
-# ----- Input -----
+# ==============================================================================
+# GUI INPUTS
+# ==============================================================================
# * In the following example we add three (!) new entries to 'cpacs_inout'
# * Try to use (readable) loops instead of copy-pasting three almost same entries :)
@@ -21,7 +43,7 @@
var_name=direction,
var_type=float,
default_value=None,
- unit="1",
+ unit=None,
descr=f"Fuselage scaling on {direction} axis",
xpath=FUSELAGES_XPATH + f"/fuselage/transformation/scaling/{direction}",
gui=include_gui,
@@ -52,12 +74,21 @@
gui_group="My Selection",
)
-# ----- Output -----
+# ==============================================================================
+# GUI OUTPUTS
+# ==============================================================================
cpacs_inout.add_output(
var_name="output",
default_value=None,
- unit="1",
+ unit=None,
descr="Description of the output",
xpath=CEASIOMPY_XPATH + "/test/myOutput",
)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/ModuleTemplate/func/subfunc.py b/ceasiompy/ModuleTemplate/func/subfunc.py
index 803fb682b..518c4e396 100644
--- a/ceasiompy/ModuleTemplate/func/subfunc.py
+++ b/ceasiompy/ModuleTemplate/func/subfunc.py
@@ -5,39 +5,101 @@
Small description of the script
-Python version: >=3.8
| Author: Name
-| Creation: YEAR-MONTH-DAY
+| Creation: day month year
TODO:
* Things to improve ...
* Things to add ...
-
"""
-
# ==============================================================================
# IMPORTS
# ==============================================================================
-from ceasiompy.utils.ceasiomlogger import get_logger
+from pydantic import validate_call
+from cpacspy.cpacsfunctions import get_value
-log = get_logger()
+from typing import Tuple
+from cpacspy.cpacspy import CPACS
+from ceasiompy.utils.commonxpath import FUSELAGES_XPATH
-# ==============================================================================
+from ceasiompy import (
+ log,
+ ceasiompy_cfg,
+)
+
+# =================================================================================================
# CLASSES
-# ==============================================================================
+# =================================================================================================
-# ==============================================================================
+class MyClass:
+ """
+ Description of the class
+
+ Attributes:
+ var_a (float): Argument a [unit]
+ var_b (float): Argument b [unit]
+
+ .. seealso::
+
+ See some other source
+
+ """
+
+ def __init__(self, a=1.1, b=2.2):
+ self.var_a = a
+ self.var_b = b
+ self.var_c = 0.0
+
+ def add_my_var(self):
+ """This methode will sum up var_a and var_b in var_c"""
+
+ self.var_c = self.var_a + self.var_b
+
+# =================================================================================================
# FUNCTIONS
-# ==============================================================================
+# =================================================================================================
+
+
+@validate_call(config=ceasiompy_cfg)
+def get_fuselage_scaling(cpacs: CPACS) -> Tuple[float, float, float]:
+ """Function to get fuselage scaling along x,y,z axis.
+ Function 'get_fuselage_scaling' return the value of the scaling for the
+ fuselage. (This is an example function just to show usage of CPACS and tixi)
+
+ Source:
+ * Reference paper or book, with author and date
+
+ """
-def my_subfunc(arg_a, arg_b):
+ tixi = cpacs.tixi
+
+ SCALING_XPATH = "/fuselage[1]/transformation/scaling"
+ x_fus_scaling_xpath = FUSELAGES_XPATH + SCALING_XPATH + "/x"
+ y_fus_scaling_xpath = FUSELAGES_XPATH + SCALING_XPATH + "/y"
+ z_fus_scaling_xpath = FUSELAGES_XPATH + SCALING_XPATH + "/z"
+
+ # Get values
+ x = float(get_value(tixi, x_fus_scaling_xpath))
+ y = float(get_value(tixi, y_fus_scaling_xpath))
+ z = float(get_value(tixi, z_fus_scaling_xpath))
+
+ # Log
+ log.info(f"Fuselage x scaling is {x}.")
+ log.info(f"Fuselage y scaling is {y}.")
+ log.info(f"Fuselage z scaling is {z}.")
+
+ return (x, y, z)
+
+
+@validate_call
+def my_subfunc(arg_a: str, arg_b: str) -> str:
"""Function to calculate ...
Function 'my_subfunc' is a subfunction of ModuleTemplate which returns...
@@ -46,7 +108,7 @@ def my_subfunc(arg_a, arg_b):
* Reference paper or book, with author and date
Args:
- arg_a (str): Argument 1
+ arg_a (str): Argument 1
arg_a (str): Argument 2
Returns:
@@ -67,5 +129,4 @@ def my_subfunc(arg_a, arg_b):
# ==============================================================================
if __name__ == "__main__":
-
- print("Nothing to execute!")
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/ModuleTemplate/moduletemplate.py b/ceasiompy/ModuleTemplate/moduletemplate.py
index fe0a536d9..bfe14e75f 100644
--- a/ceasiompy/ModuleTemplate/moduletemplate.py
+++ b/ceasiompy/ModuleTemplate/moduletemplate.py
@@ -5,13 +5,11 @@
Small description of the script
-Python version: >=3.8
| Author: Name
-| Creation: YEAR-MONTH-DAY
+| Creation: D-M-Y
TODO:
-
* Things to improve ...
* Things to add ...
@@ -21,187 +19,51 @@
# IMPORTS
# =================================================================================================
-from pathlib import Path
-
-
-from ambiance import Atmosphere
-from ceasiompy.ModuleTemplate.func.subfunc import my_subfunc
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.moduleinterfaces import (
- check_cpacs_input_requirements,
- get_toolinput_file_path,
- get_tooloutput_file_path,
-)
-from ceasiompy.utils.commonxpath import FUSELAGES_XPATH
-from cpacspy.cpacsfunctions import (
- add_float_vector,
- add_string_vector,
- add_uid,
- copy_branch,
- create_branch,
- get_float_vector,
- get_string_vector,
- get_tigl_configuration,
- get_uid,
- get_value,
- get_value_or_default,
- get_xpath_parent,
- open_tigl,
- open_tixi,
-)
-
-log = get_logger()
-
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
-
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-
-class MyClass:
- """
- Description of the class
-
- Attributes:
- var_a (float): Argument a [unit]
- var_b (float): Argument b [unit]
-
- .. seealso::
-
- See some other source
-
- """
-
- def __init__(self, a=1.1, b=2.2):
- self.var_a = a
- self.var_b = b
- self.var_c = 0.0
-
- def add_my_var(self):
- """This methode will sum up var_a and var_b in var_c"""
-
- self.var_c = self.var_a + self.var_b
-
-
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
-
-
-def sum_funcion(arg1, arg2):
- """Function to calculate ...
-
- Function 'sum_funcion' return the total of arg1 and arg2, after it convert
- arg1 into an float.
+from pydantic import validate_call
+from ceasiompy.utils.ceasiompyutils import call_main
+from ceasiompy.ModuleTemplate.func.subfunc import get_fuselage_scaling
- Source:
- * Reference paper or book, with author and date
+from cpacspy.cpacspy import CPACS
- Args:
- arg1 (interger): Argument 1 [unit]
- arg2 (float): Argument 2 [unit]
+from ceasiompy.ModuleTemplate import MODULE_NAME
- Returns:
- total (float): Output1 [unit]
-
- .. warning::
-
- Example of warning
- """
-
- if not isinstance(arg1, int):
- raise ValueError("arg1 is not an integer")
-
- # Use of a subfunction here
- print(my_subfunc("test1", "test2"))
-
- total = float(arg1) + arg2
-
- return total
-
-
-def get_fuselage_scaling(cpacs_path, cpacs_out_path):
- """Function to get fuselage scaling along x,y,z axis.
-
- Function 'get_fuselage_scaling' return the value of the scaling for the
- fuselage. (This is an example function just to show usage of CPACS and tixi)
-
- Source:
- * Reference paper or book, with author and date
-
- Args:
- cpacs_path (Path): Path to CPACS file
- cpacs_out_path (Path):Path to CPACS output file
-
- Returns:
- Tuple with fuselage scaling
-
- * x (float): Scaling on x [-]
- * y (float): Scaling on y [-]
- * z (float): Scaling on z [-]
- """
-
- # Open TIXI handle
- tixi = open_tixi(cpacs_path)
-
- # Create xpaths
- SCALING_XPATH = "/fuselage/transformation/scaling"
-
- x_fus_scaling_xpath = FUSELAGES_XPATH + SCALING_XPATH + "/x"
- y_fus_scaling_xpath = FUSELAGES_XPATH + SCALING_XPATH + "/y"
- z_fus_scaling_xpath = FUSELAGES_XPATH + SCALING_XPATH + "/z"
-
- # Get values
- x = get_value(tixi, x_fus_scaling_xpath)
- y = get_value(tixi, y_fus_scaling_xpath)
- z = get_value(tixi, z_fus_scaling_xpath)
-
- # Log
- log.info("Fuselage x scaling is : " + str(x))
- log.info("Fuselage y scaling is : " + str(y))
- log.info("Fuselage z scaling is : " + str(z))
-
- # Close TIXI handle and save the CPACS file
- tixi.save(str(cpacs_out_path))
-
- return (x, y, z)
+from ceasiompy import (
+ log,
+ ceasiompy_cfg,
+)
+# Do not add function definitions here.
+# If you want to define classes or functions do it in func directory.
# =================================================================================================
# MAIN
# =================================================================================================
-def main(cpacs_path, cpacs_out_path):
-
- log.info("----- Start of " + MODULE_NAME + " -----")
-
- check_cpacs_input_requirements(cpacs_path)
+# You will need to have a function named main.
+# CEASIOMpy will run one main per module.
- # Define other inputs value
- my_value1 = 6
- my_value2 = 5.5
- # Call 'sum_function'
- my_total = sum_funcion(my_value1, my_value2)
- log.info("My total is equal to: " + str(my_total))
+# If the function takes as input a arbitrary type object,
+# you need to add config=validate_cfg.
+@validate_call(config=ceasiompy_cfg)
+# Try to always use the validate_call decorator if your function is called only once.
+# This way you can be sure that your function takes as inputs
+# and gives as outputs the correct types of object.
+def main(cpacs: CPACS) -> None:
# Call a function which use CPACS inputs
- x, y, z = get_fuselage_scaling(cpacs_path, cpacs_out_path)
- log.info("Value x,y,z as been calculated")
+ x, y, z = get_fuselage_scaling(cpacs)
+ log.info("Value x, y, z have been calculated.")
log.info("x = " + str(x))
log.info("y = " + str(y))
log.info("z = " + str(z))
- log.info("----- End of " + MODULE_NAME + " -----")
-
if __name__ == "__main__":
-
- cpacs_path = get_toolinput_file_path(MODULE_NAME)
- cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
-
- main(cpacs_path, cpacs_out_path)
+ # By default uses D150_simple.xml at CPACS_files.
+ call_main(main, MODULE_NAME)
+ # If you want to run your module with a specific CPACS file,
+ # you can use:
+ # call_main(main, MODULE_NAME, cpacs_path = Path(path/to/cpacs.xml))
+ # The saved CPACS can be found in the ToolOutput directory.
diff --git a/ceasiompy/ModuleTemplate/tests/ToolOutput/.keep b/ceasiompy/ModuleTemplate/tests/ToolOutput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/ModuleTemplate/tests/ToolOutput/.keep
+++ b/ceasiompy/ModuleTemplate/tests/ToolOutput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/ModuleTemplate/tests/test_moduletemplate.py b/ceasiompy/ModuleTemplate/tests/test_moduletemplate.py
index e833cdbd6..5d499506e 100644
--- a/ceasiompy/ModuleTemplate/tests/test_moduletemplate.py
+++ b/ceasiompy/ModuleTemplate/tests/test_moduletemplate.py
@@ -3,12 +3,13 @@
Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-Test functions for 'lib/ModuleTemplate/moduletemplate.py'
+Test functions ModuleTemplate module.
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2019-08-14
+| Modified: Leon Deligny
+| Date: 18-Mar-2025
"""
@@ -16,80 +17,43 @@
# IMPORTS
# =================================================================================================
+from ceasiompy.utils.decorators import log_test
+from ceasiompy.ModuleTemplate.moduletemplate import get_fuselage_scaling
-from pathlib import Path
-
-import pytest
-from ceasiompy.ModuleTemplate.func.subfunc import my_subfunc
-from ceasiompy.ModuleTemplate.moduletemplate import MyClass, get_fuselage_scaling, sum_funcion
-from pytest import approx
-
-MODULE_DIR = Path(__file__).parent
-CPACS_IN_PATH = Path(MODULE_DIR, "ToolInput", "simpletest_cpacs.xml")
-CPACS_OUT_PATH = Path(MODULE_DIR, "ToolOutput", "ToolOutput.xml")
+from unittest import main
+from ceasiompy.utils.ceasiompytest import CeasiompyTest
+from ceasiompy.ModuleTemplate.func.subfunc import MyClass
# =================================================================================================
# CLASSES
# =================================================================================================
+class TestModuleTemplate(CeasiompyTest):
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
-
-
-def test_MyClass():
- """Test Class 'MyClass'"""
-
- TestClass = MyClass()
-
- assert TestClass.var_a == 1.1
- assert TestClass.var_b == 2.2
- assert TestClass.var_c == 0.0
-
- TestClass.add_my_var()
- assert TestClass.var_c == approx(3.3)
-
-
-def test_sum_funcion():
- """Test function 'sum_funcion'"""
+ @log_test
+ def test_module_template_functions(self) -> None:
+ self.assert_equal_function(
+ f=get_fuselage_scaling,
+ input_args=(self.test_cpacs, ),
+ expected=(1.0, 1.0, 1.0, ),
+ )
- # Test Raise ValueError
- with pytest.raises(ValueError):
- sum_funcion(5.5, 4.4)
+ @log_test
+ def test_my_class(self):
+ TestClass = MyClass()
- # Test 'sum_funcion' normal use
- assert sum_funcion(5, 4.4) == approx(9.4)
-
-
-def test_get_fuselage_scaling():
- """Test function 'get_fuselage_scaling'"""
-
- x, y, z = get_fuselage_scaling(CPACS_IN_PATH, CPACS_OUT_PATH)
-
- assert x == approx(1)
- assert y == approx(0.5)
- assert z == approx(0.5)
-
-
-def test_subfunc():
- """Test subfunction 'my_subfunc'"""
-
- a = "a"
- b = "b"
-
- res = my_subfunc(a, b)
-
- assert res == "a and b"
+ self.assertEqual(TestClass.var_a, 1.1)
+ self.assertEqual(TestClass.var_b, 2.2)
+ self.assertEqual(TestClass.var_c, 0.0)
+ TestClass.add_my_var()
+ self.assertAlmostEqual(TestClass.var_c, 3.3)
# =================================================================================================
# MAIN
# =================================================================================================
-if __name__ == "__main__":
- print("Test ModuleTemplate")
- print("To run test use the following command:")
- print(">> pytest -v")
+if __name__ == "__main__":
+ main(verbosity=0)
diff --git a/ceasiompy/Optimisation/ToolInput/.keep b/ceasiompy/Optimisation/ToolInput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/Optimisation/ToolInput/.keep
+++ b/ceasiompy/Optimisation/ToolInput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/Optimisation/ToolOutput/.keep b/ceasiompy/Optimisation/ToolOutput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/Optimisation/ToolOutput/.keep
+++ b/ceasiompy/Optimisation/ToolOutput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/Optimisation/__init__.py b/ceasiompy/Optimisation/__init__.py
index e69de29bb..ad797a43a 100644
--- a/ceasiompy/Optimisation/__init__.py
+++ b/ceasiompy/Optimisation/__init__.py
@@ -0,0 +1,37 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for Optimisation module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+# True if the module is active.
+# False if the module is disabled (not working or not ready).
+module_status = False
+
+# ===== Include GUI =====
+# True if you want to add a GUI for this module.
+# False if module is desactivated or no GUI to be displayed.
+include_gui = False
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+RES_DIR = True
diff --git a/ceasiompy/Optimisation/__specs__.py b/ceasiompy/Optimisation/__specs__.py
index 58bde68a1..d3e2b1d4f 100644
--- a/ceasiompy/Optimisation/__specs__.py
+++ b/ceasiompy/Optimisation/__specs__.py
@@ -1,6 +1,8 @@
from pathlib import Path
from ceasiompy.utils.moduleinterfaces import CPACSInOut
from ceasiompy.utils.commonxpath import OPTIM_XPATH
+import streamlit as st
+from ceasiompy.Optimisation import include_gui
# ===== Module Status =====
# True if the module is active
@@ -16,7 +18,6 @@
cpacs_inout = CPACSInOut()
-include_gui = True
# ----- Input -----
@@ -36,7 +37,7 @@
cpacs_inout.add_input(
var_name="",
var_type=list,
- default_value=None,
+ default_value=st.session_state.cpacs.get_aeromap_uid_list(),
unit=None,
descr="Name of the aero map to evaluate",
xpath=OPTIM_XPATH + "/aeroMapUID",
diff --git a/ceasiompy/Optimisation/files/optim_example.png b/ceasiompy/Optimisation/files/optim_example.png
index 8a16d3b4f..c4601f258 100644
Binary files a/ceasiompy/Optimisation/files/optim_example.png and b/ceasiompy/Optimisation/files/optim_example.png differ
diff --git a/ceasiompy/Optimisation/func/dictionnary.py b/ceasiompy/Optimisation/func/dictionnary.py
index db0d0922b..997b204d0 100755
--- a/ceasiompy/Optimisation/func/dictionnary.py
+++ b/ceasiompy/Optimisation/func/dictionnary.py
@@ -6,7 +6,6 @@
Functions to create the dictionnary of geometric variables needed
for the optimnization routine.
-Python version: >=3.8
| Author : Vivien Riolo
| Creation: 2020-03-24
@@ -24,11 +23,10 @@
# =================================================================================================
import numpy as np
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from cpacspy.cpacsfunctions import get_tigl_configuration, open_tigl
from cpacspy.utils import COEFS, PARAMS_COEFS
-log = get_logger()
# Contains the geometric design variables
geom_var_dict = {}
@@ -401,4 +399,4 @@ def init_geom_var_dict(tixi):
if __name__ == "__main__":
- print("Nothing to execute!")
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/Optimisation/func/optimfunctions.py b/ceasiompy/Optimisation/func/optimfunctions.py
index e166980ef..8cff02a8e 100755
--- a/ceasiompy/Optimisation/func/optimfunctions.py
+++ b/ceasiompy/Optimisation/func/optimfunctions.py
@@ -5,7 +5,6 @@
Function library for the optimisation module.
-Python version: >=3.8
| Author: Vivien Riolo
| Creation: 2020-04-10
@@ -25,6 +24,7 @@
from pathlib import Path
from re import split
+from pandas import DataFrame
import pandas as pd
from ceasiompy.Optimisation.func.dictionnary import init_geom_var_dict
from ceasiompy.Optimisation.func.tools import (
@@ -35,13 +35,12 @@
launch_external_program,
)
from ceasiompy.SMUse.smuse import load_surrogate
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from ceasiompy.utils.moduleinterfaces import get_all_module_specs
from ceasiompy.utils.commonxpath import OPTIM_XPATH, SMUSE_XPATH, SU2_FIXED_CL_XPATH
from cpacspy.cpacsfunctions import get_value_or_default
from cpacspy.utils import COEFS, PARAMS_COEFS
-log = get_logger()
MODULE_DIR = Path(__file__).parent
CSV_PATH = Path(MODULE_DIR, "Variable_library.csv")
@@ -71,7 +70,7 @@ def __init__(self):
"""Define default main parameters."""
# Choice of routine type : DOE or Optimisation
- self.type = "OPTIM"
+ self.type = "Optimisation"
self.date = ""
self.modules = []
@@ -116,36 +115,50 @@ def get_user_inputs(self, tixi):
"""Take user inputs from the GUI."""
# Problem setup
- objectives = get_value_or_default(tixi, OPTIM_XPATH + "/objective", "cl")
+ objectives = get_value_or_default(
+ tixi, OPTIM_XPATH + "/objective", "cl")
self.objective = split(";|,", objectives)
- self.minmax = get_value_or_default(tixi, OPTIM_XPATH + "/minmax", "max")
+ self.minmax = get_value_or_default(
+ tixi, OPTIM_XPATH + "/minmax", "max")
# Global parameters
- self.driver = get_value_or_default(tixi, OPTIM_XPATH + "/parameters/driver", "COBYLA")
- self.max_iter = int(get_value_or_default(tixi, OPTIM_XPATH + "/iterationNB", 200))
- self.tol = float(get_value_or_default(tixi, OPTIM_XPATH + "/tolerance", 1e-3))
- self.save_iter = int(get_value_or_default(tixi, OPTIM_XPATH + "/saving/perIter", 1))
+ self.driver = get_value_or_default(
+ tixi, OPTIM_XPATH + "/parameters/driver", "COBYLA")
+ self.max_iter = int(get_value_or_default(
+ tixi, OPTIM_XPATH + "/iterationNB", 200))
+ self.tol = float(get_value_or_default(
+ tixi, OPTIM_XPATH + "/tolerance", 1e-3))
+ self.save_iter = int(get_value_or_default(
+ tixi, OPTIM_XPATH + "/saving/perIter", 1))
# Specific DoE parameters
self.doedriver = get_value_or_default(
tixi, OPTIM_XPATH + "/parameters/DoE/driver", "Uniform"
)
self.samplesnb = int(
- get_value_or_default(tixi, OPTIM_XPATH + "/parameters/DoE/sampleNB", 3)
+ get_value_or_default(
+ tixi,
+ OPTIM_XPATH + "/parameters/DoE/sampleNB",
+ 3,
+ )
)
# User specified configuration file path
- self.user_config = str(get_value_or_default(tixi, OPTIM_XPATH + "/Config/filepath", "-"))
+ self.user_config = str(get_value_or_default(
+ tixi, OPTIM_XPATH + "/Config/filepath", "-"))
fix_cl = get_value_or_default(tixi, SU2_FIXED_CL_XPATH, "no")
if fix_cl == "YES":
- tixi.updateTextElement(OPTIM_XPATH + "/aeroMapUID", "aeroMap_fixedCL_SU2")
+ tixi.updateTextElement(
+ OPTIM_XPATH + "/aeroMapUID", "aeroMap_fixedCL_SU2")
self.aeromap_uid = "aeroMap_fixedCL_SU2"
else:
- self.aeromap_uid = str(get_value_or_default(tixi, OPTIM_XPATH + "/aeroMapUID", "-"))
+ self.aeromap_uid = str(get_value_or_default(
+ tixi, OPTIM_XPATH + "/aeroMapUID", "-"))
- self.use_aeromap = get_value_or_default(tixi, OPTIM_XPATH + "/Config/useAero", False)
+ self.use_aeromap = get_value_or_default(
+ tixi, OPTIM_XPATH + "/Config/useAero", False)
# =================================================================================================
@@ -438,7 +451,7 @@ def initialize_df():
return df
-def add_geometric_vars(tixi, df):
+def add_geometric_vars(tixi, df: DataFrame):
"""Add geometry parameters as design variables.
Automatically add the geometric variables as they are not included as
@@ -467,7 +480,7 @@ def add_geometric_vars(tixi, df):
"getcmd": getcmd,
"setcmd": setcmd,
}
- df = df.append(new_row, ignore_index=True)
+ df = pd.concat([df, new_row], ignore_index=True)
df.sort_values(
by=["type", "Name"],
@@ -526,7 +539,7 @@ def create_variable_library(Rt, tixi, optim_dir_path):
"""
- global objective, var
+ # global objective, var
CSV_PATH = Path(optim_dir_path, "Variable_library.csv")
@@ -564,4 +577,4 @@ def create_variable_library(Rt, tixi, optim_dir_path):
if __name__ == "__main__":
- print("Nothing to execute!")
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/Optimisation/func/tools.py b/ceasiompy/Optimisation/func/tools.py
index e6526bea7..db9997c62 100755
--- a/ceasiompy/Optimisation/func/tools.py
+++ b/ceasiompy/Optimisation/func/tools.py
@@ -6,7 +6,6 @@
This module contains the tools used for data creation and manipulation of the
Optimisation and PredictiveTool modules.
-Python version: >=3.8
| Author : Vivien Riolo
| Creation: 2020-05-26
@@ -30,10 +29,7 @@
import numpy as np
import openmdao.api as om
import pandas as pd
-import tigl3.configuration # used within eval
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
+from ceasiompy import log
# Not an exhaustive list
@@ -77,7 +73,7 @@ def launch_external_program(path):
os.system("Start excel.exe " + str(path))
elif OS == "darwin":
os.system(
- "/Applications/Microsoft\ Excel.app/Contents/MacOS/Microsoft\ Excel " + str(path)
+ r"/Applications/Microsoft\ Excel.app/Contents/MacOS/Microsoft\ Excel " + str(path)
)
input("Press ENTER to continue...")
@@ -474,4 +470,4 @@ def add_bounds(value, var):
if __name__ == "__main__":
- print("Nothing to execute!")
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/Optimisation/optimisation.py b/ceasiompy/Optimisation/optimisation.py
index d6b1f0b36..1efeeee4b 100755
--- a/ceasiompy/Optimisation/optimisation.py
+++ b/ceasiompy/Optimisation/optimisation.py
@@ -6,7 +6,6 @@
This module sets up and solves an optimisation problem or runs a design of
experiement with specified inputs and outputs that are parsed from a CSV file.
-Python version: >=3.8
| Author: Vivien Riolo
| Creation: 2020-06-15
@@ -18,13 +17,13 @@
"""
+import ast
from pathlib import Path
from re import split
import numpy as np
import openmdao.api as om
-from ceasiompy.CPACSUpdater.cpacsupdater import update_cpacs_file
from ceasiompy.Optimisation.func.dictionnary import (
add_am_to_dict,
create_aeromap_dict,
@@ -38,12 +37,13 @@
)
from ceasiompy.Optimisation.func.tools import change_var_name, is_digit, plot_results, save_results
from ceasiompy.SMUse.smuse import load_surrogate, write_inouts
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from ceasiompy.utils.ceasiompyutils import run_module
from ceasiompy.utils.moduleinterfaces import (
get_specs_for_module,
get_toolinput_file_path,
get_tooloutput_file_path,
+ check_cpacs_input_requirements,
)
from ceasiompy.utils.commonxpath import OPTIM_XPATH
from cpacspy.cpacsfunctions import add_float_vector, get_value, open_tixi
@@ -51,12 +51,9 @@
from cpacspy.utils import COEFS, PARAMS
# Do not remove: Called within eval() function
-from tigl3 import geometry
+# from tigl3.geometry import eval
-log = get_logger()
-
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
+from ceasiompy.Optimisation import MODULE_NAME
Rt = Routine()
@@ -94,7 +91,7 @@ def compute(self, inputs, outputs):
else:
- for m, module in enumerate(Rt.modules):
+ for m, _ in enumerate(Rt.modules):
# Increment name of output CPACS file
Rt.modules[m].cpacs_out = Path(
@@ -166,7 +163,7 @@ def setup(self):
declared.append(entry.var_name)
elif (
"aeromap" in entry.var_name and self.module_name == Rt.last_am_module
- ): # == 'PyTornado': #not skf^is_skf:
+ ): # == 'PyAVL': #not skf^is_skf:
# Condition to avoid any conflict with skinfriction
for name in PARAMS:
if name in Rt.optim_var_dict:
@@ -252,8 +249,10 @@ def setup(self):
elif df.loc[name, "type"] == "des":
self.add_input(name)
- self.xd = df.loc[[name for name in df.index if df.loc[name, "type"] == "des"]]
- self.yd = df.loc[[name for name in df.index if df.loc[name, "type"] == "obj"]]
+ self.xd = df.loc[[
+ name for name in df.index if df.loc[name, "type"] == "des"]]
+ self.yd = df.loc[[
+ name for name in df.index if df.loc[name, "type"] == "obj"]]
def compute(self, inputs, outputs):
"""Make a prediction"""
@@ -320,6 +319,70 @@ def compute(self, inputs, outputs):
# =================================================================================================
+def update_cpacs_file(cpacs_path, cpacs_out_path, optim_var_dict):
+ """Function to update a CPACS file with value from the optimiser
+
+ This function sets the new values of the design variables given by
+ the routine driver to the CPACS file, using the Tigl and Tixi handler.
+
+ Source:
+ * See CPACSCreator api function,
+
+ Args:
+ cpacs_path (Path): Path to CPACS file to update
+ cpacs_out_path (Path):Path to the updated CPACS file
+ optim_var_dict (dict): Dictionary containing all the variable
+ value/min/max and command to modify a CPACS file
+
+ """
+
+ log.info("----- Start of CPACSUpdater -----")
+ log.info(f"{cpacs_path} will be updated.")
+
+ tixi = open_tixi(cpacs_path)
+ tigl = tixi
+ # open_tigl(tixi)
+
+ # Object seems to be unused, but are use in "eval" function
+ aircraft = tigl
+ # get_tigl_configuration(tigl)
+ # wings = aircraft.get_wings()
+ # fuselage = aircraft.get_fuselages().get_fuselage(1)
+
+ # Perform update of all the variable contained in 'optim_var_dict'
+ for name, variables in optim_var_dict.items():
+
+ # Unpack the variables
+ val_type, listval, _, _, getcommand, setcommand = variables
+
+ if val_type == "des" and listval[0] not in ["-", "True", "False"]:
+
+ if setcommand not in ["-", ""]:
+
+ # Define variable (var1,var2,..)
+ locals()[str(name)] = listval[-1]
+
+ # Update value by using tigl configuration
+ if ";" in setcommand: # if more than one command on the line
+ command_split = setcommand.split(";")
+ for setcommand in command_split:
+ ast.literal_eval(setcommand)
+ else:
+ ast.literal_eval(setcommand)
+ else:
+
+ # Update value directly in the CPACS file
+ xpath = getcommand
+ tixi.updateTextElement(xpath, str(listval[-1]))
+
+ aircraft.write_cpacs(aircraft.get_uid())
+ tigl.close()
+ tixi.save(str(cpacs_out_path))
+
+ log.info(f"{cpacs_out_path} has been saved.")
+ log.info("----- Start of CPACSUpdater -----")
+
+
def driver_setup(prob):
"""Change settings of the driver
@@ -334,7 +397,7 @@ def driver_setup(prob):
"""
- if Rt.type == "OPTIM":
+ if Rt.type == "Optimisation":
# TBD : Genetic algorithm
# if len(Rt.objective) > 1 and False:
# log.info("""More than 1 objective function, the driver will
@@ -367,8 +430,10 @@ def driver_setup(prob):
# Attaching a recorder and a diagramm visualizer ##
prob.driver.recording_options["record_inputs"] = True
- prob.driver.add_recorder(om.SqliteRecorder(str(Rt.optim_dir) + "/circuit.sqlite"))
- prob.driver.add_recorder(om.SqliteRecorder(str(Rt.optim_dir) + "/Driver_recorder.sql"))
+ prob.driver.add_recorder(om.SqliteRecorder(
+ str(Rt.optim_dir) + "/circuit.sqlite"))
+ prob.driver.add_recorder(om.SqliteRecorder(
+ str(Rt.optim_dir) + "/Driver_recorder.sql"))
def add_subsystems(prob, ivc):
@@ -396,15 +461,17 @@ def add_subsystems(prob, ivc):
# Loop throuth Modules
for module in Rt.modules:
- if module.name in ["SU2Run", "PyTornado", "SkinFriction"]:
+ if module.name in ["SU2Run", "PyAVL", "SkinFriction"]:
Rt.last_am_module = module.name
if module.name == "SMUse":
- prob.model.add_subsystem(module.name, SmComp(module), promotes=["*"])
+ prob.model.add_subsystem(
+ module.name, SmComp(module), promotes=["*"])
else:
spec = get_specs_for_module(module.name)
if spec.cpacs_inout.inputs or spec.cpacs_inout.outputs:
- prob.model.add_subsystem(module.name, ModuleComp(module), promotes=["*"])
+ prob.model.add_subsystem(
+ module.name, ModuleComp(module), promotes=["*"])
# Objectives
prob.model.add_subsystem("objective", obj, promotes=["*"])
@@ -429,12 +496,13 @@ def add_parameters(prob, ivc):
listval,
minval,
maxval,
- getcommand,
- setcommand,
+ _,
+ _,
) in Rt.optim_var_dict.items():
if val_type == "des" and listval[-1] not in ["True", "False", "-"]:
if is_digit(minval) and is_digit(maxval):
- prob.model.add_design_var(name, lower=float(minval), upper=float(maxval))
+ prob.model.add_design_var(
+ name, lower=float(minval), upper=float(maxval))
elif is_digit(minval):
prob.model.add_design_var(name, lower=float(minval))
elif is_digit(maxval):
@@ -444,7 +512,8 @@ def add_parameters(prob, ivc):
ivc.add_output(name, val=listval[-1])
elif val_type == "const":
if is_digit(minval) and is_digit(maxval):
- prob.model.add_constraint(name, lower=float(minval), upper=float(maxval))
+ prob.model.add_constraint(
+ name, lower=float(minval), upper=float(maxval))
elif is_digit(minval):
prob.model.add_constraint(name, lower=float(minval))
elif is_digit(maxval):
@@ -557,9 +626,10 @@ def routine_launcher(optim_method, module_optim, wkflow_dir):
# =====================================================================================================================
-def main(cpacs_path, cpacs_out_path):
+def main(cpacs_path: Path, cpacs_out_path: Path) -> None:
- log.info("----- Start of " + MODULE_NAME + " -----")
+ module_name = MODULE_NAME
+ log.info("----- Start of " + module_name + " -----")
tixi = open_tixi(cpacs_path)
@@ -574,12 +644,13 @@ def main(cpacs_path, cpacs_out_path):
tixi.save(cpacs_out_path)
- log.info("----- End of " + MODULE_NAME + " -----")
+ log.info("----- End of " + module_name + " -----")
if __name__ == "__main__":
cpacs_path = get_toolinput_file_path("Optimisation")
cpacs_out_path = get_tooloutput_file_path("Optimisation")
+ check_cpacs_input_requirements(cpacs_path)
main(cpacs_path, cpacs_out_path)
diff --git a/ceasiompy/PyAVL/README.md b/ceasiompy/PyAVL/README.md
index 99493d12d..b75297300 100644
--- a/ceasiompy/PyAVL/README.md
+++ b/ceasiompy/PyAVL/README.md
@@ -9,7 +9,7 @@
-`PyAVL` module is a launcher for the [Athena Vortex Lattice (AVL)](https://web.mit.edu/drela/Public/web/avl/) solver, developed by M. Drela and H. Youngren at MIT. It is a vortex lattice method (VLM) solver for low-fidelity aerodynamic computations.
+`PyAVL` module is a launcher for the [Athena Vortex Lattice (AVL)](https://web.mit.edu/drela/Public/web/avl/) solver, developed by M. Drela and H. Youngren at MIT. It is a vortex lattice method (VLM) solver for low-fidelity aerodynamic computations.
## Inputs
@@ -29,7 +29,7 @@ Example of AVL geometry model.
## Outputs
`PyAVL` outputs a CPACS file with the aerodynamic coefficients added in the aeromap. The settings of the simulation (number of chordwise/spanwise vortices, vortex distribution) are saved. The following force files are saved:
-- `ft.txt`: Total Forces.
+- `ft.txt`: Total Forces.
- `fn.txt`: Surface Forces.
- `fs.txt`: Strip Forces.
- `fe.txt`: Element Forces (vortex strength).
@@ -48,7 +48,7 @@ Example of aerodynamic loads computed by AVL.
## Installation or requirements
-Following the automatic installation procedure on the [CEASIOMpy installation page](../../installation/INSTALLATION.md) should install `PyAVL` automatically with the other tools.
+Following the automatic installation procedure on the [CEASIOMpy installation page](../../installation/INSTALLATION.md) should install `PyAVL` automatically along with the other tools.
## Limitations
diff --git a/ceasiompy/PyAVL/__init__.py b/ceasiompy/PyAVL/__init__.py
index e69de29bb..5264423b5 100644
--- a/ceasiompy/PyAVL/__init__.py
+++ b/ceasiompy/PyAVL/__init__.py
@@ -0,0 +1,81 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for PyAVL module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+from ceasiompy import log
+from ceasiompy.utils.commonxpath import CEASIOMPY_XPATH
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = True
+
+# ===== Include GUI =====
+include_gui = True
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+# ===== Add a Results Directory =====
+RES_DIR = True
+
+# ===== Name of Software used =====
+SOFTWARE_NAME = "avl"
+
+# =================================================================================================
+# xPaths
+# =================================================================================================
+
+# Main branch
+AVL_XPATH = CEASIOMPY_XPATH + "/avl"
+
+# AeroMap settings
+AVL_AEROMAP_UID_XPATH = AVL_XPATH + "/aeroMapUID"
+AVL_ROTRATES_XPATH = AVL_XPATH + "/RotationRates"
+AVL_FREESTREAM_MACH_XPATH = AVL_XPATH + "/FreestreamMach"
+
+# Software setting
+AVL_NB_CPU_XPATH = AVL_XPATH + "NbCPU"
+
+# Geometry settings
+AVL_CTRLSURF_ANGLES_XPATH = AVL_XPATH + "/ControlSurfaceAngles"
+AVL_FUSELAGE_XPATH = AVL_XPATH + "/IntegrateFuselage"
+
+# Plot settings
+AVL_PLOT_XPATH = AVL_XPATH + "/SavePlots"
+AVL_PLOTLIFT_XPATH = AVL_XPATH + "/PlotLift"
+
+# Dynamic Stability
+AVL_TABLE_XPATH = AVL_XPATH + "/Table"
+AVL_CTRLTABLE_XPATH = AVL_XPATH + "/CtrlTable"
+
+# Vortex distribution
+AVL_VORTEX_DISTR_XPATH = AVL_XPATH + "/VortexDistribution"
+AVL_NCHORDWISE_XPATH = AVL_VORTEX_DISTR_XPATH + "/Nchordwise"
+AVL_NSPANWISE_XPATH = AVL_VORTEX_DISTR_XPATH + "/Nspanwise"
+AVL_DISTR_XPATH = AVL_VORTEX_DISTR_XPATH + "/Distribution"
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/PyAVL/__specs__.py b/ceasiompy/PyAVL/__specs__.py
index 3dc0f8093..b1fdf3b25 100644
--- a/ceasiompy/PyAVL/__specs__.py
+++ b/ceasiompy/PyAVL/__specs__.py
@@ -1,43 +1,89 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+GUI Interface of PyAVL.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+import streamlit as st
+
+from ceasiompy.utils.ceasiompyutils import get_reasonable_nb_cpu
+
from ceasiompy.utils.moduleinterfaces import CPACSInOut
-from ceasiompy.utils.commonxpath import (
- CEASIOMPY_XPATH,
+
+from ceasiompy import log
+from ceasiompy.PyAVL import include_gui
+
+from ceasiompy.PyAVL import (
AVL_PLOT_XPATH,
- AVL_VORTEX_DISTR_XPATH,
+ AVL_DISTR_XPATH,
+ AVL_NB_CPU_XPATH,
+ AVL_ROTRATES_XPATH,
+ AVL_PLOTLIFT_XPATH,
AVL_FUSELAGE_XPATH,
+ AVL_NSPANWISE_XPATH,
+ AVL_NCHORDWISE_XPATH,
AVL_AEROMAP_UID_XPATH,
- AEROPERFORMANCE_XPATH,
+ AVL_FREESTREAM_MACH_XPATH,
+ AVL_CTRLSURF_ANGLES_XPATH,
)
-from pathlib import Path
-
-# ===== Module Status =====
-# True if the module is active
-# False if the module is disabled (not working or not ready)
-module_status = True # Because it is just an example not a real module
-
-# ===== Results directory path =====
-RESULTS_DIR = Path("Results", "PyAVL")
-
-# ===== CPACS inputs and outputs =====
+# ==============================================================================
+# VARIABLE
+# ==============================================================================
cpacs_inout = CPACSInOut()
-# ----- Input -----
+# ==============================================================================
+# GUI INPUTS
+# ==============================================================================
-# * In the following example we add three (!) new entries to 'cpacs_inout'
-# * Try to use (readable) loops instead of copy-pasting three almost same entries :)
cpacs_inout.add_input(
var_name="aeromap_uid",
var_type=list,
- default_value=None,
+ default_value=st.session_state.cpacs.get_aeromap_uid_list(),
unit=None,
descr="Name of the aero map to calculate",
xpath=AVL_AEROMAP_UID_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="__AEROMAP_SELECTION",
gui_group="Aeromap settings",
)
+cpacs_inout.add_input(
+ var_name="rates",
+ var_type="multiselect",
+ default_value=[0.0],
+ unit="[deg/s]",
+ descr="List of p, q, r rates",
+ xpath=AVL_ROTRATES_XPATH,
+ gui=include_gui,
+ gui_name="Rotation Rates",
+ gui_group="Rate settings",
+)
+
+cpacs_inout.add_input(
+ var_name="ctrl_surf_angles",
+ var_type="multiselect",
+ default_value=[0.0],
+ unit="[deg]",
+ descr="List of Aileron, Elevator, Rudder angles",
+ xpath=AVL_CTRLSURF_ANGLES_XPATH,
+ gui=include_gui,
+ gui_name="Aileron/Elevator/Rudder Angles",
+ gui_group="Control surface settings",
+)
+
cpacs_inout.add_input(
var_name="integrate_fuselage",
var_type=bool,
@@ -45,7 +91,7 @@
unit=None,
descr="Select to integrate the fuselage in the AVL model",
xpath=AVL_FUSELAGE_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Integrate fuselage",
gui_group="Fuselage",
)
@@ -56,8 +102,8 @@
default_value=["cosine", "sine", "equal"],
unit=None,
descr=("Select the type of distribution"),
- xpath=AVL_VORTEX_DISTR_XPATH + "/Distribution",
- gui=True,
+ xpath=AVL_DISTR_XPATH,
+ gui=include_gui,
gui_name="Choice of distribution",
gui_group="Vortex Lattice Spacing Distributions",
)
@@ -68,8 +114,8 @@
default_value=20,
unit=None,
descr="Select the number of chordwise vortices",
- xpath=AVL_VORTEX_DISTR_XPATH + "/Nchordwise",
- gui=True,
+ xpath=AVL_NCHORDWISE_XPATH,
+ gui=include_gui,
gui_name="Number of chordwise vortices",
gui_group="Vortex Lattice Spacing Distributions",
)
@@ -80,12 +126,48 @@
default_value=50,
unit=None,
descr="Select the number of spanwise vortices",
- xpath=AVL_VORTEX_DISTR_XPATH + "/Nspanwise",
- gui=True,
+ xpath=AVL_NSPANWISE_XPATH,
+ gui=include_gui,
gui_name="Number of spanwise vortices",
gui_group="Vortex Lattice Spacing Distributions",
)
+cpacs_inout.add_input(
+ var_name="nb_proc",
+ var_type=int,
+ default_value=get_reasonable_nb_cpu(),
+ unit=None,
+ descr="Number of proc to use to run SU2",
+ xpath=AVL_NB_CPU_XPATH,
+ gui=include_gui,
+ gui_name="Nb of processor",
+ gui_group="CPU",
+)
+
+cpacs_inout.add_input(
+ var_name="default_freestream_mach",
+ var_type=float,
+ default_value=0.6,
+ unit="[Mach]",
+ descr="Usually 0.2 < default value < 0.8",
+ xpath=AVL_FREESTREAM_MACH_XPATH,
+ gui=include_gui,
+ gui_name="Default freestream Mach for Prandtl-Glauert corrections",
+ gui_group="Default freestream Mach",
+)
+
+cpacs_inout.add_input(
+ var_name="plot_lift",
+ var_type=bool,
+ default_value=False,
+ unit=None,
+ descr="Select to plot lift along wing",
+ xpath=AVL_PLOTLIFT_XPATH,
+ gui=include_gui,
+ gui_name="Plot Lift",
+ gui_group="Plots Settings",
+)
+
cpacs_inout.add_input(
var_name="save_plots",
var_type=bool,
@@ -93,26 +175,14 @@
unit=None,
descr="Select to save geometry and results plots",
xpath=AVL_PLOT_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Save plots",
- gui_group="Plots",
+ gui_group="Plots Settings",
)
-# ----- Output -----
-
-cpacs_inout.add_output(
- var_name="output",
- default_value=None,
- unit="1",
- descr="Description of the output",
- xpath=CEASIOMPY_XPATH + "/test/myOutput",
-)
+# =================================================================================================
+# MAIN
+# =================================================================================================
-cpacs_inout.add_output(
- var_name="aeromap_avl", # name to change...
- # var_type=CPACS_aeroMap, # no type pour output, would it be useful?
- default_value=None,
- unit="-",
- descr="aeroMap with aero coefficients calculated by AVL",
- xpath=AEROPERFORMANCE_XPATH + "/aeroMap/aeroPerformanceMap",
-)
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/PyAVL/avlrun.py b/ceasiompy/PyAVL/avlrun.py
deleted file mode 100644
index 8b642e8c4..000000000
--- a/ceasiompy/PyAVL/avlrun.py
+++ /dev/null
@@ -1,136 +0,0 @@
-"""
-CEASIOMpy: Conceptual Aircraft Design Software
-
-Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-
-Script to run AVL calculations in CEASIOMpy.
-AVL allows to perform aerodynamic analyses using
-the vortex-lattice method (VLM)
-
-Python version: >=3.8
-
-| Author: Romain Gauthier
-| Creation: 2024-03-14
-
-TODO:
-
- * Things to improve ...
-
-"""
-
-# ==============================================================================
-# IMPORTS
-# ==============================================================================
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.moduleinterfaces import get_toolinput_file_path, get_tooloutput_file_path
-from ceasiompy.PyAVL.func.cpacs2avl import convert_cpacs_to_avl
-from ceasiompy.PyAVL.func.avlconfig import (
- write_command_file,
- get_aeromap_conditions,
- get_option_settings,
-)
-from ceasiompy.PyAVL.func.avlresults import (
- get_avl_results,
- convert_ps_to_pdf
-)
-from ceasiompy.utils.ceasiompyutils import get_results_directory
-
-import subprocess
-from pathlib import Path
-from ambiance import Atmosphere
-
-
-log = get_logger()
-
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
-def run_avl(cpacs_path, wkdir):
- """Function to run AVL.
-
- Function 'run_avl' runs AVL calculations using a CPACS file
- as input.
-
- Args:
- cpacs_path (Path) : path to the CPACS input file
- wkdir (Path) : path to the working directory
- """
-
- alt_list, mach_list, aoa_list, aos_list = get_aeromap_conditions(cpacs_path)
- save_fig, _, _, _, _ = get_option_settings(cpacs_path)
-
- for i_case in range(len(alt_list)):
- alt = alt_list[i_case]
- mach = mach_list[i_case]
- aoa = aoa_list[i_case]
- aos = aos_list[i_case]
- Atm = Atmosphere(alt)
-
- density = Atm.density[0]
- velocity = Atm.speed_of_sound[0] * mach
- g = Atm.grav_accel[0]
-
- case_dir_name = (
- f"Case{str(i_case).zfill(2)}_alt{alt}_mach{round(mach, 2)}"
- f"_aoa{round(aoa, 1)}_aos{round(aos, 1)}"
- )
-
- Path(wkdir, case_dir_name).mkdir(exist_ok=True)
- case_dir_path = Path(wkdir, case_dir_name)
-
- avl_path = convert_cpacs_to_avl(cpacs_path, machnumber=mach, wkdir=case_dir_path)
-
- command_path = write_command_file(
- avl_path,
- case_dir_path,
- save_plots=save_fig,
- alpha=aoa,
- beta=aos,
- mach_number=mach,
- ref_velocity=velocity,
- ref_density=density,
- g_acceleration=g,
- )
- log.info("Running AVL ...")
- subprocess.run(
- ["xvfb-run", "avl"],
- stdin=open(str(command_path), "r"),
- cwd=case_dir_path,
- stdout=subprocess.DEVNULL
- )
- log.info("AVL Done!")
-
- Path(avl_path).rename(case_dir_path / Path(avl_path).name)
-
- if save_fig:
- convert_ps_to_pdf(wkdir=case_dir_path)
-
-
-# =================================================================================================
-# MAIN
-# =================================================================================================
-
-
-def main(cpacs_path, cpacs_out_path):
- log.info("----- Start of " + MODULE_NAME + " -----")
-
- results_dir = get_results_directory("PyAVL")
- run_avl(cpacs_path, results_dir)
- get_avl_results(cpacs_path, cpacs_out_path, results_dir)
-
- log.info("----- End of " + MODULE_NAME + " -----")
-
-
-if __name__ == "__main__":
- cpacs_path = get_toolinput_file_path(MODULE_NAME)
- cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
-
- main(cpacs_path, cpacs_out_path)
diff --git a/ceasiompy/PyAVL/files/avl_example.png b/ceasiompy/PyAVL/files/avl_example.png
index ee2134d02..a76d2a258 100644
Binary files a/ceasiompy/PyAVL/files/avl_example.png and b/ceasiompy/PyAVL/files/avl_example.png differ
diff --git a/ceasiompy/PyAVL/files/avl_example_loads.png b/ceasiompy/PyAVL/files/avl_example_loads.png
index 6fdc182b9..24955125e 100644
Binary files a/ceasiompy/PyAVL/files/avl_example_loads.png and b/ceasiompy/PyAVL/files/avl_example_loads.png differ
diff --git a/ceasiompy/PyAVL/files/avl_logo.png b/ceasiompy/PyAVL/files/avl_logo.png
index 0c1551049..013cb6962 100644
Binary files a/ceasiompy/PyAVL/files/avl_logo.png and b/ceasiompy/PyAVL/files/avl_logo.png differ
diff --git a/ceasiompy/PyAVL/files/template.mass b/ceasiompy/PyAVL/files/template.mass
index 9faeeb38e..fdab08e16 100644
--- a/ceasiompy/PyAVL/files/template.mass
+++ b/ceasiompy/PyAVL/files/template.mass
@@ -12,7 +12,7 @@ Lunit = 1.0 m
Munit = 1.0 kg
Tunit = 1.0 s
-#-------------------------
+#-------------------------
# Gravity and density to be used as default values in trim setup.
# Must be in the units given above.
g = 9.81
diff --git a/ceasiompy/PyAVL/func/__init__.py b/ceasiompy/PyAVL/func/__init__.py
new file mode 100644
index 000000000..3634752d5
--- /dev/null
+++ b/ceasiompy/PyAVL/func/__init__.py
@@ -0,0 +1,47 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for PyAVL.func functions.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+from ceasiompy import log
+
+# =================================================================================================
+# CONSTANTS
+# =================================================================================================
+
+# config.py
+FORCE_FILES = ["ft", "fn", "fs", "fe", "st", "sb"]
+
+# results.py
+AVL_COEFS = {
+ "CLtot": (1, "cl"),
+ "CDtot": (1, "cd"),
+ "CYtot": (1, "cs"),
+ "Cltot": (2, "cmd"),
+ "Cmtot": (2, "cms"),
+ "Cntot": (2, "cml"),
+ "Cma": (1, "cms_a"),
+ "Clb": (2, "cmd_b"),
+ "Cnb": (2, "cml_b"),
+}
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute.")
diff --git a/ceasiompy/PyAVL/func/avlconfig.py b/ceasiompy/PyAVL/func/avlconfig.py
deleted file mode 100644
index 7c7d64c3f..000000000
--- a/ceasiompy/PyAVL/func/avlconfig.py
+++ /dev/null
@@ -1,231 +0,0 @@
-"""
-CEASIOMpy: Conceptual Aircraft Design Software
-
-Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-
-Script to get the flight conditions (alt, aoa, mach...) from
-the input CPACS file, and write the command file for AVL.
-
-Python version: >=3.8
-
-| Author: Romain Gauthier
-| Creation: 2024-03-14
-
-TODO:
-
- * Things to improve...
-
-"""
-
-# ==============================================================================
-# IMPORTS
-# ==============================================================================
-
-from pathlib import Path
-
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.commonxpath import (
- RANGE_XPATH,
- AVL_AEROMAP_UID_XPATH,
- AVL_PLOT_XPATH,
- AVL_VORTEX_DISTR_XPATH,
- AVL_FUSELAGE_XPATH,
-)
-from cpacspy.cpacsfunctions import get_value_or_default
-from ceasiompy.utils.moduleinterfaces import get_module_path
-from cpacspy.cpacspy import CPACS
-
-log = get_logger()
-
-
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
-def write_command_file(
- avl_path,
- case_dir_path,
- alpha,
- beta,
- mach_number,
- ref_velocity,
- ref_density,
- g_acceleration,
- save_plots,
-):
- """Function to write the command file for AVL.
-
- Function 'write_command_file' writes the command file to
- execute for AVL calculations.
-
- Args:
- avl_path (Path) : path to the AVL input file
- case_dir_path (Path) : path to the run case directory
- alpha (float) : angle of attack [deg]
- beta (float) : angle of attack [deg]
- mach_number (float) : Mach number
- ref_velocity (float) : reference upstream velocity [m/s]
- ref_density (float) : reference upstream density [kg/m^3]
- g_acceleration (float) : gravitational acceleration [m/s^2]
-
- Returns:
- avl_commands.txt : write the command AVL file.
- command_path (Path) : path to the command file.
- """
-
- command_path = str(case_dir_path) + "/avl_commands.txt"
- pyavl_dir = get_module_path("PyAVL")
- mass_path = Path(pyavl_dir, "files", "template.mass")
-
- if save_plots:
- with open(command_path, "w") as command_file:
- command_file.writelines(
- [
- "load " + str(avl_path) + "\n",
- "mass " + str(mass_path) + "\n",
- "oper\n",
- "g\n",
- "h\n\n",
- "a a " + str(alpha) + "\n",
- "b b " + str(beta) + "\n",
- "m\n",
- "mn " + str(mach_number) + "\n",
- "g " + str(g_acceleration) + "\n",
- "d " + str(ref_density) + "\n",
- "v " + str(ref_velocity) + "\n\n",
- ]
- )
- command_file.write("x\n")
- command_file.writelines(["t\n", "h\n\n"])
- command_file.writelines(["g\n", "lo\n", "h\n\n"])
- command_file.write("x\n")
- for force_file in ["ft", "fn", "fs", "fe", "st"]:
- command_file.write(force_file + "\n")
- command_file.write(force_file + ".txt\n")
- command_file.write("\n\n\n")
- command_file.write("quit")
-
- else: # same without lines saving figures
- with open(command_path, "w") as command_file:
- command_file.writelines(
- [
- "load " + str(avl_path) + "\n",
- "mass " + str(mass_path) + "\n",
- "oper\n",
- "a a " + str(alpha) + "\n",
- "b b " + str(beta) + "\n",
- "m\n",
- "mn " + str(mach_number) + "\n",
- "g " + str(g_acceleration) + "\n",
- "d " + str(ref_density) + "\n",
- "v " + str(ref_velocity) + "\n\n",
- ]
- )
- command_file.write("x\n")
- for force_file in ["ft", "fn", "fs", "fe", "st", "sb"]:
- command_file.write(force_file + "\n")
- command_file.write(force_file + ".txt\n")
- command_file.write("\n\n\n")
- command_file.write("quit")
-
- # Print the command file for debugging
- # with open(command_path, "r") as command_file:
- # for line in command_file:
- # print(line)
-
- return Path(command_path)
-
-
-def get_aeromap_conditions(cpacs_path):
- """Function read the flight conditions from the aeromap.
-
- Function 'get_aeromap_conditions' reads the flight conditions
- (angle of attack, mach number...) from the aeromap of CEASIOMpy.
-
- Args:
- cpacs_path (Path) : path to the cpacs input file
-
- Returns:
- alt_list (list) : altitude of the cases.
- mach_list (list) : mach number of the cases.
- aoa_list (list) : angle of attack of the cases.
- aos_list (list) : angle of sweep of the cases.
- """
- cpacs = CPACS(cpacs_path)
-
- # Get the first aeroMap as default one or create automatically one
- aeromap_list = cpacs.get_aeromap_uid_list()
-
- if aeromap_list:
- aeromap_default = aeromap_list[0]
- log.info(f"The aeromap is {aeromap_default}")
-
- aeromap_uid = get_value_or_default(cpacs.tixi, AVL_AEROMAP_UID_XPATH, aeromap_default)
-
- activate_aeromap = cpacs.get_aeromap_by_uid(aeromap_uid)
- alt_list = activate_aeromap.get("altitude").tolist()
- mach_list = activate_aeromap.get("machNumber").tolist()
- aoa_list = activate_aeromap.get("angleOfAttack").tolist()
- aos_list = activate_aeromap.get("angleOfSideslip").tolist()
-
- else:
- default_aeromap = cpacs.create_aeromap("DefaultAeromap")
- default_aeromap.description = "AeroMap created automatically"
-
- mach = get_value_or_default(cpacs.tixi, RANGE_XPATH + "/cruiseMach", 0.3)
- alt = get_value_or_default(cpacs.tixi, RANGE_XPATH + "/cruiseAltitude", 10000)
-
- default_aeromap.add_row(alt=alt, mach=mach, aos=0.0, aoa=0.0)
- default_aeromap.save()
-
- alt_list = [alt]
- mach_list = [mach]
- aoa_list = [0.0]
- aos_list = [0.0]
-
- aeromap_uid = get_value_or_default(cpacs.tixi, AVL_AEROMAP_UID_XPATH, "DefaultAeromap")
- log.info(f"{aeromap_uid} has been created")
-
- cpacs.save_cpacs(cpacs_path, overwrite=True)
- return alt_list, mach_list, aoa_list, aos_list
-
-
-def get_option_settings(cpacs_path):
- """Function read the setting of the graphical user interface.
-
- Function 'get_option_settings' reads the setting to use in AVL
- from the graphical user interface of CEASIOMpy.
-
- Args:
- cpacs_path (Path) : path to the cpacs input file
-
- Returns:
- save_plots (bool) : to save the geometry and results figures.
- vortex_distribution (float) : distribution of the vortices.
- Nchordwise (int) : number of chordwise vortices.
- Nspanwise (int) : number of spanwise vortices.
- """
- cpacs = CPACS(cpacs_path)
-
- save_plots = get_value_or_default(cpacs.tixi, AVL_PLOT_XPATH, False)
- vortex_distribution_gui = get_value_or_default(
- cpacs.tixi, AVL_VORTEX_DISTR_XPATH + "/Distribution", "equal"
- )
- if vortex_distribution_gui == "cosine":
- vortex_distribution = 1.0
- elif vortex_distribution_gui == "sine":
- vortex_distribution = 2.0
- else:
- vortex_distribution = 3.0
- Nchordwise = get_value_or_default(cpacs.tixi, AVL_VORTEX_DISTR_XPATH + "/Nchordwise", 5)
- Nspanwise = get_value_or_default(cpacs.tixi, AVL_VORTEX_DISTR_XPATH + "/Nspanwise", 20)
- integrate_fuselage = get_value_or_default(cpacs.tixi, AVL_FUSELAGE_XPATH, False)
-
- return save_plots, vortex_distribution, Nchordwise, Nspanwise, integrate_fuselage
-
-
-# =================================================================================================
-# MAIN
-# =================================================================================================
-
-if __name__ == "__main__":
- log.info("Nothing to execute!")
diff --git a/ceasiompy/PyAVL/func/avlresults.py b/ceasiompy/PyAVL/func/avlresults.py
deleted file mode 100644
index daba425f2..000000000
--- a/ceasiompy/PyAVL/func/avlresults.py
+++ /dev/null
@@ -1,225 +0,0 @@
-"""
-CEASIOMpy: Conceptual Aircraft Design Software
-
-Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-
-Extract results from AVL calculations and save them in a CPACS file.
-Plot the lift distribution from AVL strip forces file 'fs.txt'.
-Convert AVL 'plot.ps' to 'plot.pdf'.
-
-Python version: >=3.8
-
-| Author: Romain Gauthier
-| Creation: 2024-03-18
-
-TODO:
-
- *
-
-"""
-
-# =================================================================================================
-# IMPORTS
-# =================================================================================================
-
-from pathlib import Path
-import pandas as pd
-import matplotlib.pyplot as plt
-import subprocess
-
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.commonxpath import AVL_AEROMAP_UID_XPATH
-
-from cpacspy.cpacsfunctions import get_value
-from cpacspy.cpacspy import CPACS
-
-log = get_logger()
-
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
-
-
-def plot_lift_distribution(force_file_fs, aoa, aos, mach, alt, wkdir):
- """Plot the lift distribution from AVL strip forces file (fs.txt)
-
- Args:
- force_file_fs (Path): Path to the AVL strip forces file
- aoa (float): angle of attack [deg]
- aos (float): angle of sideslip [deg]
- mach (float): mach number
- alt (float): flight altitude [m]
- wkir (path): path to save the plot
-
- Returns:
- """
- y_list = []
- chord_list = []
- cl_list = []
- clnorm_list = []
-
- with open(force_file_fs, "r") as fs:
- for line in fs:
- if "Cref =" in line:
- cref = float(line.split()[5])
-
- elif "# Spanwise =" in line:
- # number_strips = int(line.split()[7])
- number_strips = int(line.split("=")[2].split("F")[0])
-
- elif "Xle" in line:
- number_data = 0
- for line in fs:
- data = line.split()
- y_list.append(float(data[2]))
- chord_list.append(float(data[4]))
- cl_list.append(float(data[9]))
- clnorm_list.append(float(data[8]))
- number_data += 1
-
- # break when every line has been extracted
- if number_data == number_strips:
- break
-
- data_df = pd.DataFrame(
- {"y": y_list, "chord": chord_list, "cl": cl_list, "cl_norm": clnorm_list}
- )
-
- data_df.sort_values(by="y", inplace=True)
- data_df.reset_index(drop=True, inplace=True)
- data_df["cl_cref"] = data_df["cl"] * data_df["chord"] / cref
-
- _, ax = plt.subplots(figsize=(10, 5))
- data_df.plot("y", "cl_norm", ax=ax, label="$c_{l\perp}$", linestyle="dashed", color="r")
- data_df.plot("y", "cl", label="$c_l$", ax=ax, linestyle="dashed", color="#FFA500")
- data_df.plot(
- "y", "cl_cref", ax=ax, label="$c_l \cdot C/C_{ref}$", linestyle="solid", color="#41EE33"
- )
-
- plt.title(
- (
- "Lift distribution along the wing span "
- "($\\alpha=%.1f^{\circ}$, $\\beta=%.1f^{\circ}$, "
- "$M=%.1f$, alt = %d m)"
- )
- % (aoa, aos, mach, alt),
- fontsize=14,
- )
-
- plt.ylabel("$C_l$", rotation=0, fontsize=12)
- plt.legend(fontsize=12)
- plt.grid()
- plt.savefig(Path(wkdir, "lift_distribution.png"))
-
-
-def get_avl_aerocoefs(force_file_ft):
- """Get aerodynamic coefficients and velocity from AVL total forces file (ft.txt)
-
- Args:
- force_file_ft (Path): Path to the AVL total forces file
-
- Returns:
- cl, cd, cs, cmd, cms, cml: Aerodynamic coefficients
- """
-
- if not force_file_ft.is_file():
- raise FileNotFoundError(f"The AVL forces file '{force_file_ft}' has not been found!")
-
- cl, cd = None, None
-
- with open(force_file_ft) as f:
- for line in f.readlines():
- if "CLtot" in line:
- cl = float(line.split("=")[1].strip())
- if "CDtot" in line:
- cd = float(line.split("=")[1].strip())
- if "Cmtot" in line:
- cm = float(line.split("=")[2].strip())
-
- return cl, cd, cm
-
-
-def get_avl_results(cpacs_path, cpacs_out_path, wkdir):
- """Function to write AVL results in a CPACS file.
-
- Function 'get_avl_results' gets available results from the latest AVL calculation and put them
- at the correct place in the CPACS file.
-
- '/cpacs/vehicles/aircraft/model/analyses/aeroPerformance/aeroMap[n]/aeroPerformanceMap'
-
- Args:
- cpacs_path (Path): Path to input CPACS file
- cpacs_out_path (Path): Path to output CPACS file
- wkdir (Path): Path to the working directory
-
- """
-
- cpacs = CPACS(cpacs_path)
- if not wkdir.exists():
- raise OSError(f"The working directory : {wkdir} does not exit!")
- aeromap_uid = get_value(cpacs.tixi, AVL_AEROMAP_UID_XPATH)
-
- log.info(f"The aeromap uid is: {aeromap_uid}")
- aeromap = cpacs.get_aeromap_by_uid(aeromap_uid)
-
- alt_list = aeromap.get("altitude").tolist()
- mach_list = aeromap.get("machNumber").tolist()
- aoa_list = aeromap.get("angleOfAttack").tolist()
- aos_list = aeromap.get("angleOfSideslip").tolist()
-
- case_dir_list = [case_dir for case_dir in wkdir.iterdir() if "Case" in case_dir.name]
-
- for config_dir in sorted(case_dir_list):
- if not config_dir.is_dir():
- continue
-
- ft_file_path = Path(config_dir, "ft.txt")
-
- if not ft_file_path.exists():
- raise FileNotFoundError("No result total forces 'ft.txt' file have been found!")
-
- fs_file_path = Path(config_dir, "fs.txt")
- if not fs_file_path.exists():
- raise FileNotFoundError("No result strip forces 'fs.txt' file have been found!")
-
- case_nb = int(config_dir.name.split("_")[0].split("Case")[1])
-
- aoa = aoa_list[case_nb]
- aos = aos_list[case_nb]
- mach = mach_list[case_nb]
- alt = alt_list[case_nb]
-
- cl, cd, cm = get_avl_aerocoefs(ft_file_path)
- plot_lift_distribution(fs_file_path, aoa, aos, mach, alt, wkdir=config_dir)
-
- aeromap.add_coefficients(alt=alt, mach=mach, aos=aos, aoa=aoa, cd=cd, cl=cl, cms=cm)
- aeromap.save()
- cpacs.save_cpacs(cpacs_out_path, overwrite=True)
-
-
-def convert_ps_to_pdf(wkdir):
- """Function to convert AVL 'plot.ps' to 'plot.pdf'.
-
- Args:
- wkdir (Path): Path to the working directory.
-
- """
- if not Path(wkdir, "plot.ps").exists():
- raise FileNotFoundError("File 'plot.ps' does not exist.")
-
- subprocess.run(["ps2pdf", "plot.ps", "plot.pdf"], cwd=wkdir)
- subprocess.run(["rm", "plot.ps"], cwd=wkdir)
-
-
-# =================================================================================================
-# MAIN
-# =================================================================================================
-
-if __name__ == "__main__":
- log.info("Nothing to execute!")
diff --git a/ceasiompy/PyAVL/func/config.py b/ceasiompy/PyAVL/func/config.py
new file mode 100644
index 000000000..9c1342df0
--- /dev/null
+++ b/ceasiompy/PyAVL/func/config.py
@@ -0,0 +1,200 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Script to get the flight conditions (alt, aoa, mach...) from
+the input CPACS file, and write the command file for AVL.
+More details at: https://web.mit.edu/drela/Public/web/avl/AVL_User_Primer.pdf.
+
+
+| Author: Romain Gauthier
+| Creation: 2024-03-14
+| Modified: Leon Deligny
+| Date: 11-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pydantic import validate_call
+from cpacspy.cpacsfunctions import get_value
+from ceasiompy.PyAVL.func.utils import get_atmospheric_cond
+from ceasiompy.utils.mathsfunctions import non_dimensionalize_rate
+from ceasiompy.utils.ceasiompyutils import (
+ bool_,
+ get_aeromap_conditions,
+)
+
+from pathlib import Path
+from cpacspy.cpacspy import CPACS
+from tixi3.tixi3wrapper import Tixi3
+from ceasiompy.PyAVL.func.cpacs2avl import Avl
+from typing import (
+ List,
+ Tuple,
+)
+
+from ceasiompy import log, ceasiompy_cfg
+from ceasiompy.PyAVL import MODULE_DIR
+from ceasiompy.PyAVL.func import FORCE_FILES
+from ceasiompy.utils.commonxpath import (
+ AREA_XPATH,
+ LENGTH_XPATH,
+)
+from ceasiompy.PyAVL import (
+ AVL_PLOT_XPATH,
+ AVL_NB_CPU_XPATH,
+ AVL_ROTRATES_XPATH,
+ AVL_AEROMAP_UID_XPATH,
+ AVL_CTRLSURF_ANGLES_XPATH,
+)
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+@validate_call(config=ceasiompy_cfg)
+def retrieve_gui_values(cpacs: CPACS, results_dir: Path) -> Tuple[
+ List, List, List, List,
+ List, List,
+ Path, bool,
+ int,
+]:
+ tixi = cpacs.tixi
+ alt_list, mach_list, aoa_list, aos_list = get_aeromap_conditions(cpacs, AVL_AEROMAP_UID_XPATH)
+
+ save_fig = bool_(get_value(tixi, AVL_PLOT_XPATH))
+ rotation_rates_float = get_value(tixi, AVL_ROTRATES_XPATH)
+ control_surface_float = get_value(tixi, AVL_CTRLSURF_ANGLES_XPATH)
+
+ # Convert to lists
+ rotation_rate_list = [float(x) for x in str(rotation_rates_float).split(';')]
+ control_surface_list = [float(x) for x in str(control_surface_float).split(';')]
+
+ avl_file = Avl(tixi, results_dir)
+ avl_path = avl_file.convert_cpacs_to_avl()
+
+ nb_cpu = int(get_value(tixi, AVL_NB_CPU_XPATH))
+
+ return (
+ alt_list, mach_list, aoa_list, aos_list,
+ rotation_rate_list, control_surface_list,
+ avl_path,
+ save_fig,
+ nb_cpu,
+ )
+
+
+def write_command_file(
+ tixi: Tixi3,
+ avl_path: Path,
+ case_dir_path: Path,
+ save_plots: bool,
+ alt: float,
+ mach_number: float,
+ alpha: float,
+ beta: float = 0.0,
+ pitch_rate: float = 0.0,
+ roll_rate: float = 0.0,
+ yaw_rate: float = 0.0,
+ aileron: float = 0.0,
+ elevator: float = 0.0,
+ rudder: float = 0.0,
+) -> Path:
+ """
+ Writes the command file for AVL.
+
+ Args:
+ tixi (handles): TIXI Handle of the CPACS file.
+ avl_path (Path): Path to the AVL input file.
+ case_dir_path (Path): path to the run case directory
+ alpha (float): angle of attack [deg]
+ beta (float): angle of attack [deg]
+ pitch_rate (float): pitch rate [deg/s]
+ roll_rate (float): roll rate [deg/s]
+ yaw_rate (float): yaw rate [deg/s]
+ mach_number (float): Mach number
+ alt (float): Altitude.
+ aileron (float): Aileron angle [deg].
+ elevator (float): Elevator angle [deg].
+ rudder (float): Rudder angle [deg].
+ save_plots (bool): Saving plots condition.
+
+ Returns:
+ (Path): Path to the command file.
+
+ """
+
+ ref_density, g_acceleration, ref_velocity = get_atmospheric_cond(alt, mach_number)
+
+ command_path = str(case_dir_path) + "/avl_commands.txt"
+
+ # Retrieve template file for mass
+ mass_path = Path(MODULE_DIR, "files", "template.mass")
+
+ # Get the reference dimensions
+ s = tixi.getDoubleElement(AREA_XPATH)
+ c = tixi.getDoubleElement(LENGTH_XPATH)
+ b = s / c
+
+ # See https://web.mit.edu/drela/Public/web/avl/AVL_User_Primer.pdf
+ # for how he non-dimensionalize the rates
+ roll_rate_star, pitch_rate_star, yaw_rate_star = non_dimensionalize_rate(
+ p=roll_rate,
+ q=pitch_rate,
+ r=yaw_rate,
+ v=ref_velocity,
+ b=b,
+ c=c,
+ )
+
+ command = [
+ "load " + str(avl_path) + "\n",
+ "mass " + str(mass_path) + "\n",
+ "oper\n",
+ "a a " + str(alpha) + "\n",
+ "b b " + str(beta) + "\n",
+ "r r " + str(roll_rate_star) + "\n",
+ "p p " + str(pitch_rate_star) + "\n",
+ "y y " + str(yaw_rate_star) + "\n",
+ "d2 d2 " + str(aileron) + "\n",
+ "d3 d3 " + str(elevator) + "\n",
+ "d4 d4 " + str(rudder) + "\n",
+ "m\n",
+ "mn " + str(mach_number) + "\n",
+ "g " + str(g_acceleration) + "\n",
+ "d " + str(ref_density) + "\n",
+ "v " + str(ref_velocity) + "\n\n",
+ ]
+
+ with open(command_path, "w") as command_file:
+ command_file.writelines(command)
+ command_file.write("x\n")
+
+ if save_plots:
+ command_file.writelines(["t\n", "h\n\n"])
+ command_file.writelines(["g\n", "lo\n", "h\n\n"])
+
+ command_file.write("x\n")
+
+ for force_file in FORCE_FILES:
+ command_file.write(force_file + "\n")
+ command_file.write(force_file + ".txt\n")
+
+ command_file.write("\n\n\n")
+ command_file.write("quit")
+
+ return Path(command_path)
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/PyAVL/func/cpacs2avl.py b/ceasiompy/PyAVL/func/cpacs2avl.py
index 69a8b935c..6df04c392 100644
--- a/ceasiompy/PyAVL/func/cpacs2avl.py
+++ b/ceasiompy/PyAVL/func/cpacs2avl.py
@@ -5,15 +5,12 @@
Script to convert CPACS file geometry into AVL geometry
-Python version: >=3.8
| Author: Romain Gauthier
| Adapted from : Aidan Jungo script cpacs2sumo.py
| Creation: 2024-03-14
-
-TODO:
-
- * Improve link between wings and fuselages parts
+| Modified: Leon Deligny
+| Date: 11-Mar-2025
"""
@@ -21,556 +18,471 @@
# IMPORTS
# ==============================================================================
-from ceasiompy.utils.ceasiompyutils import get_results_directory
import math
import numpy as np
+
+from cpacspy.cpacsfunctions import (
+ get_uid,
+ get_value,
+)
+from ceasiompy.utils.mathsfunctions import (
+ euler2fix,
+ rotate_points,
+)
+from ceasiompy.PyAVL.func.utils import (
+ write_control,
+ to_cpacs_format,
+ get_points_ref,
+ convert_dist_to_avl_format,
+)
+from ceasiompy.utils.geometryfunctions import (
+ sum_points,
+ prod_points,
+ get_profile_coord,
+ check_if_rotated,
+ get_chord_span,
+ elements_number,
+ get_positionings,
+ convert_fuselage_profiles,
+ corrects_airfoil_profile,
+)
+
from pathlib import Path
-from scipy import interpolate
+from numpy import ndarray
+from tixi3.tixi3wrapper import Tixi3
+from scipy.interpolate import interp1d
+from typing import (
+ List,
+ Tuple,
+)
+from ceasiompy.utils.generalclasses import (
+ Point,
+ Transformation,
+)
+
+from ceasiompy import log
+from ceasiompy.PyAVL import (
+ AVL_DISTR_XPATH,
+ AVL_FUSELAGE_XPATH,
+ AVL_NSPANWISE_XPATH,
+ AVL_NCHORDWISE_XPATH,
+ AVL_FREESTREAM_MACH_XPATH,
+)
+from ceasiompy.utils.commonxpath import (
+ AREA_XPATH,
+ WINGS_XPATH,
+ LENGTH_XPATH,
+ FUSELAGES_XPATH,
+ AIRCRAFT_NAME_XPATH,
+)
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def compute_fuselage_coords(
+ i_sec: int,
+ elem_transf: Transformation,
+ sec_transf: Transformation,
+ fus_transf: Transformation,
+ pos_x_list: List, pos_z_list: List,
+ prof_size_y, prof_size_z,
+ fus_radius_vec,
+ x_fuselage,
+ y_fuselage_top, y_fuselage_bottom,
+):
+ # Compute coordinates of the center of section
+ body_frm_center_x = (
+ elem_transf.translation.x
+ + sec_transf.translation.x + pos_x_list[i_sec]
+ ) * fus_transf.scaling.x
+
+ body_frm_center_z = (
+ elem_transf.translation.z * sec_transf.scaling.z
+ + sec_transf.translation.z
+ + pos_z_list[i_sec]
+ ) * fus_transf.scaling.z
+
+ # Compute height and width of the section
+ _, y, z = prod_points(elem_transf.scaling, sec_transf.scaling, fus_transf.scaling)
+ body_frm_width = 2 * prof_size_y * y
+ body_frm_height = 2 * prof_size_z * z
+
+ # Compute diameter of the section as the mean between height and width
+ # AVL assumes only circular cross section for fuselage
+ fus_radius = np.mean([body_frm_height, body_frm_width]) / 2
+ fus_radius_vec[i_sec] = (fus_radius)
+
+ # Save the coordinates of the fuselage
+ x_fuselage[i_sec] = body_frm_center_x
+ y_fuselage_top[i_sec] = body_frm_center_z + fus_radius
+ y_fuselage_bottom[i_sec] = body_frm_center_z - fus_radius
+
+ return body_frm_width, body_frm_height
+
+
+def leadingedge_coordinates(
+ tixi: Tixi3,
+ avl_path,
+ i_wing,
+ i_sec,
+ x_LE_rot,
+ y_LE_rot,
+ z_LE_rot,
+ wg_sec_chord,
+ wg_sec_rot,
+ wing_xpath,
+ c_ref,
+ s_ref,
+ foil_dat_path,
+) -> None:
+
+ # Write the leading edge coordinates and the airfoil file
+ with open(avl_path, 'a') as avl_file:
+ avl_file.write("#---------------\nSECTION\n")
+ avl_file.write("#Xle Yle Zle Chord Ainc\n")
+ avl_file.write(
+ f"{x_LE_rot:.3f} {y_LE_rot:.3f} {z_LE_rot:.3f} "
+ f"{(wg_sec_chord):.3f} {wg_sec_rot.y}\n"
+ )
+
+ edge_xpath = "/componentSegments/componentSegment"
+ bis_xpath = "/controlSurfaces/trailingEdgeDevices"
+ control_xpath_base = wing_xpath + edge_xpath + bis_xpath
+ num_devices = tixi.getNumberOfChilds(control_xpath_base)
+
+ for i in range(1, num_devices + 1):
+ control_xpath = f"{control_xpath_base}/trailingEdgeDevice[{i}]"
+ control_uid = tixi.getTextAttribute(control_xpath, "uID")
+ innerhingeXsi_xpath = control_xpath + "/path/innerHingePoint/hingeXsi"
+ outerhingeXsi_xpath = control_xpath + "/path/outerHingePoint/hingeXsi"
+ innerhingeXsi = float(get_value(tixi, innerhingeXsi_xpath))
+ outerhingeXsi = float(get_value(tixi, outerhingeXsi_xpath))
+
+ innerEta_xpath = control_xpath + "/outerShape/innerBorder/etaTE/eta"
+ outerEta_xpath = control_xpath + "/outerShape/outerBorder/etaTE/eta"
+
+ innerEta = float(get_value(tixi, innerEta_xpath))
+ outerEta = float(get_value(tixi, outerEta_xpath))
+
+ x_axis = (outerhingeXsi - innerhingeXsi) * c_ref
+ y_axis = (outerEta - innerEta) * s_ref
+ z_axis = 0.0
+
+ CONTROL_DICT = {
+ "InnerFlap": {
+ "i_sec": [0, 1],
+ "type": "flap",
+ "axis": f"{x_axis} {y_axis} {z_axis}",
+ "bool": [1.0, 1.0],
+ },
+ "OuterFlap": {
+ "i_sec": [1, 2],
+ "type": "flap",
+ "axis": f"{x_axis} {y_axis} {z_axis}",
+ "bool": [1.0, 1.0],
+ },
+ "Aileron": {
+ "i_sec": [2, 3],
+ "type": "aileron",
+ "axis": f"{x_axis} {y_axis} {z_axis}",
+ "bool": [-1.0, -1.0],
+ },
+ "Elevator": {
+ "i_sec": [0, 1],
+ "type": "elevator",
+ "axis": f"{x_axis} {y_axis} {z_axis}",
+ "bool": [1.0, 1.0],
+ },
+ "Rudder": {
+ "i_sec": [0, 1],
+ "type": "rudder",
+ "axis": f"{x_axis} {z_axis} {y_axis}",
+ "bool": [-1.0, -1.0],
+ },
+ }
+ control_type = CONTROL_DICT[control_uid]["type"]
+
+ if i_sec == CONTROL_DICT[control_uid]["i_sec"][0]:
+ write_control(
+ avl_file,
+ control_type,
+ innerhingeXsi,
+ CONTROL_DICT[control_uid]["axis"],
+ CONTROL_DICT[control_uid]["bool"][0],
+ )
+ elif i_sec == CONTROL_DICT[control_uid]["i_sec"][1]:
+ write_control(
+ avl_file,
+ control_type,
+ outerhingeXsi,
+ CONTROL_DICT[control_uid]["axis"],
+ CONTROL_DICT[control_uid]["bool"][1],
+ )
+ else:
+ log.warning(
+ f"Issue with {control_uid} control surface "
+ f"at section {i_sec} of wing number {i_wing}."
+ )
-from ceasiompy.CPACS2SUMO.func.getprofile import get_profile_coord
+ avl_file.write("AFILE\n")
+ avl_file.write(foil_dat_path + "\n\n")
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.commonxpath import WINGS_XPATH, FUSELAGES_XPATH, REF_XPATH
-from ceasiompy.utils.generalclasses import SimpleNamespace, Transformation
-from ceasiompy.utils.mathfunctions import euler2fix
-from cpacspy.cpacsfunctions import open_tixi
+def write_airfoil_coords(
+ foil_dat_path: Path,
+ prof_uid: str,
+ prof_vect_x: ndarray,
+ prof_vect_z: ndarray,
+) -> None:
+ """
+ No need to add in Avl class.
+ """
+ with open(foil_dat_path, 'w') as dat_file:
+ dat_file.write(prof_uid + "\n")
-from ceasiompy.PyAVL.func.rotation3D import rotate_3D_points
-from ceasiompy.PyAVL.func.avlconfig import get_option_settings
+ prof_x_len = len(prof_vect_x)
-log = get_logger()
+ # Limit the number of points to 100 (otherwise AVL error)
+ if prof_x_len >= 100:
+ log.warning("Limiting the number of points.")
+ step = round(prof_x_len / 100)
+ prof_vect_x = prof_vect_x[0:prof_x_len:step]
+ prof_vect_z = prof_vect_z[0:prof_x_len:step]
+
+ for coord_x, coord_z in zip(prof_vect_x, prof_vect_z):
+ dat_file.write(f"{coord_x}\t{coord_z}\n")
# =================================================================================================
-# FUNCTIONS
+# CLASS
# =================================================================================================
-def convert_cpacs_to_avl(cpacs_path, machnumber, wkdir):
- """Function to convert a CPACS file geometry into an AVL file geometry.
+class Avl:
+ def __init__(
+ self: "Avl",
+ tixi: Tixi3,
+ results_dir: Path,
+ ) -> None:
- Function 'convert_cpacs_to_avl' opens an input CPACS file with TIXI handle
- and converts every element (as much as possible) in the AVL (.avl) format.
+ # Store input variables
+ self.tixi = tixi
+ self.results_dir = results_dir
- Source:
- * https://github.com/cfsengineering/CEASIOMpy/blob/main/ceasiompy/CPACS2SUMO/cpacs2sumo.py
+ # Retrieve GUI values
+ self.vortex_dist: int = convert_dist_to_avl_format(get_value(tixi, AVL_DISTR_XPATH))
+ self.nchordwise: int = get_value(tixi, AVL_NCHORDWISE_XPATH)
+ self.nspanwise: int = get_value(tixi, AVL_NSPANWISE_XPATH)
+ self.add_fuselage: bool = get_value(tixi, AVL_FUSELAGE_XPATH)
- Args:
- cpacs_path (Path) : path to the CPACS input file
+ self.area_ref: float = tixi.getDoubleElement(AREA_XPATH)
+ self.chord_ref: float = tixi.getDoubleElement(LENGTH_XPATH)
+ self.span_ref: float = self.area_ref / self.chord_ref
+ self.points_ref: ndarray = get_points_ref(tixi)
+
+ # .avl file type
+ self.name_aircraft = self.tixi.getTextElement(AIRCRAFT_NAME_XPATH)
+ self.avl_path = str(self.results_dir) + "/" + self.name_aircraft + ".avl"
+
+ def initialize_avl_command_file(self) -> Path:
+ with open(self.avl_path, 'w') as avl_file:
+ avl_file.write(self.name_aircraft + "\n\n")
+
+ def convert_cpacs_to_avl(self) -> Path:
+ """
+ Convert a CPACS file geometry into an AVL file geometry.
+
+ Source:
+ * https://github.com/cfsengineering/CEASIOMpy/blob/main/ceasiompy/CPACS2SUMO/cpacs2sumo.py
+
+ Workflow:
+ 1. Initialize command file .avl
+ 2. Convert fuselage (if included)
+ 3. Convert wings
+
+ Returns:
+ (Path): Path to the AVL input file.
+
+ """
+
+ # 1. Initialize command file .avl
+ self.initialize_avl_command_file()
+
+ mach = get_value(self.tixi, AVL_FREESTREAM_MACH_XPATH)
+ with open(self.avl_path, 'a') as avl_file:
+ # Default freestream mach number
+ avl_file.write('#Mach\n')
+ avl_file.write(f"{mach}\n\n")
+
+ # No Symmetry assumed
+ avl_file.write("#IYsym IZsym Zsym\n")
+ avl_file.write("0\t0\t0\n\n")
+
+ self.write_ref_values()
+
+ # 2. Convert fuselage (if included)
+ fus_z_profile, fus_radius_profile, body_transf = None, None, None
+ if self.add_fuselage:
+ fus_z_profile, fus_radius_profile, body_transf = self.convert_fuselage()
+
+ # 3. Convert wings
+ self.convert_wings(
+ fus_radius_profile,
+ fus_z_profile,
+ body_transf,
+ )
+
+ return Path(self.avl_path)
+
+ def convert_fuselage(
+ self: "Avl",
+ ) -> Tuple[interp1d, interp1d, Transformation]:
+ """
+ Convert fuselages from CPACS to avl format.
+
+ Workflow:
+ For each fuselage
+ 1. Write fuselage settings
+ For each sections
+ 2. Write the fuselage coordinates
+
+ """
+
+ fus_cnt = elements_number(self.tixi, FUSELAGES_XPATH, "fuselage")
- Returns:
- aircraft.avl : write the input AVL file
- avl_path (Path) : path to the AVL input file
- """
- tixi = open_tixi(cpacs_path)
-
- # Get the aircraft name
- name_aircraft = tixi.getTextElement("/cpacs/header/name")
-
- results_dir = get_results_directory("PyAVL")
- results_path = str(results_dir)
- avl_path = str(results_dir) + "/" + name_aircraft + ".avl"
-
- with open(avl_path, 'w') as avl_file:
- # Aircraft name
- avl_file.write(name_aircraft + "\n\n")
- # Mach number
- avl_file.write('#Mach\n')
- avl_file.write(str(machnumber) + "\n\n")
- # Symmetry
- avl_file.write("#IYsym IZsym Zsym\n")
- avl_file.write("0\t0\t0\n\n")
-
- _, vortex_distribution, Nchordwise, Nspanwise, integrate_fuselage = get_option_settings(
- cpacs_path)
-
- # Get the reference dimensions
- area_ref = tixi.getDoubleElement(REF_XPATH + '/area')
- chord_ref = tixi.getDoubleElement(REF_XPATH + '/length')
- span_ref = area_ref / chord_ref
- points_ref = np.array([tixi.getDoubleElement(REF_XPATH + '/point/x'),
- tixi.getDoubleElement(REF_XPATH + '/point/y'),
- tixi.getDoubleElement(REF_XPATH + '/point/z')])
- with open(avl_path, 'a') as avl_file:
- # Reference dimensions
- avl_file.write("#Sref Cref Bref\n")
- avl_file.write(f"{area_ref:.3f}\t{chord_ref:.3f}\t{span_ref:.3f}\n\n")
- # Reference location for moments/rotations
- avl_file.write("#Xref Yref Zref\n")
- for i_points in range(3):
- avl_file.write(f"{points_ref[i_points]:.3f}\t")
- avl_file.write("\n\n")
-
- # Fuselage(s) ---------------------------------------------------------------
-
- if tixi.checkElement(FUSELAGES_XPATH):
- fus_cnt = tixi.getNamedChildrenCount(FUSELAGES_XPATH, "fuselage")
- log.info(str(fus_cnt) + " fuselage has been found.")
- else:
- fus_cnt = 0
- log.warning("No fuselage has been found in this CPACS file!")
-
- if integrate_fuselage:
for i_fus in reversed(range(fus_cnt)):
fus_xpath = FUSELAGES_XPATH + "/fuselage[" + str(i_fus + 1) + "]"
- fus_uid = tixi.getTextAttribute(fus_xpath, "uID")
+ fus_uid = get_uid(self.tixi, fus_xpath)
+ fus_dat_path = str(self.results_dir) + "/" + fus_uid + ".dat"
+
fus_transf = Transformation()
- fus_transf.get_cpacs_transf(tixi, fus_xpath)
+ fus_transf.get_cpacs_transf(self.tixi, fus_xpath)
body_transf = Transformation()
body_transf.translation = fus_transf.translation
-
- # Convert angles
body_transf.rotation = euler2fix(fus_transf.rotation)
- # Add body origin
- body_ori_str = (
- str(body_transf.translation.x)
- + "\t"
- + str(body_transf.translation.y)
- + "\t"
- + str(body_transf.translation.z)
- )
+ # 1. Write fuselage settings
+ self.write_fuselage_settings(fus_transf.scaling, body_transf.translation)
- # Write fuselage settings
- with open(avl_path, 'a') as avl_file:
- avl_file.write("#--------------------------------------------------\n")
- avl_file.write("BODY\n")
- avl_file.write("Fuselage\n\n")
- avl_file.write("!Nbody Bspace\n")
- avl_file.write("100\t1.0\n\n")
-
- # Scaling
- avl_file.write("SCALE\n")
- avl_file.write(str(fus_transf.scaling.x)
- + "\t"
- + str(fus_transf.scaling.y)
- + "\t"
- + str(fus_transf.scaling.z)
- + "\n\n")
-
- # Translation
- avl_file.write("TRANSLATE\n")
- avl_file.write(body_ori_str + "\n\n")
-
- avl_file.write("NOWAKE\n\n")
-
- # Positionings
- if tixi.checkElement(fus_xpath + "/positionings"):
- pos_cnt = tixi.getNamedChildrenCount(fus_xpath + "/positionings", "positioning")
- log.info(str(fus_cnt) + ' "Positioning" has been found : ')
-
- pos_x_list = []
- pos_y_list = []
- pos_z_list = []
- from_sec_list = []
- to_sec_list = []
-
- for i_pos in range(pos_cnt):
- pos_xpath = fus_xpath + "/positionings/positioning[" + str(i_pos + 1) + "]"
-
- length = tixi.getDoubleElement(pos_xpath + "/length")
- sweep_deg = tixi.getDoubleElement(pos_xpath + "/sweepAngle")
- sweep = math.radians(sweep_deg)
- dihedral_deg = tixi.getDoubleElement(pos_xpath + "/dihedralAngle")
- dihedral = math.radians(dihedral_deg)
-
- # Get the corresponding translation of each positioning
- pos_x_list.append(length * math.sin(sweep))
- pos_y_list.append(length * math.cos(dihedral) * math.cos(sweep))
- pos_z_list.append(length * math.sin(dihedral) * math.cos(sweep))
-
- # Get which section are connected by the positioning
- if tixi.checkElement(pos_xpath + "/fromSectionUID"):
- from_sec = tixi.getTextElement(pos_xpath + "/fromSectionUID")
- else:
- from_sec = ""
- from_sec_list.append(from_sec)
-
- if tixi.checkElement(pos_xpath + "/toSectionUID"):
- to_sec = tixi.getTextElement(pos_xpath + "/toSectionUID")
- else:
- to_sec = ""
- to_sec_list.append(to_sec)
-
- # Re-loop though the positioning to re-order them
- for j_pos in range(pos_cnt):
- if from_sec_list[j_pos] == "":
- prev_pos_x = 0
- prev_pos_y = 0
- prev_pos_z = 0
-
- elif from_sec_list[j_pos] == to_sec_list[j_pos - 1]:
- prev_pos_x = pos_x_list[j_pos - 1]
- prev_pos_y = pos_y_list[j_pos - 1]
- prev_pos_z = pos_z_list[j_pos - 1]
-
- else:
- index_prev = to_sec_list.index(from_sec_list[j_pos])
- prev_pos_x = pos_x_list[index_prev]
- prev_pos_y = pos_y_list[index_prev]
- prev_pos_z = pos_z_list[index_prev]
-
- pos_x_list[j_pos] += prev_pos_x
- pos_y_list[j_pos] += prev_pos_y
- pos_z_list[j_pos] += prev_pos_z
+ sec_cnt, pos_x_list, pos_y_list, pos_z_list = get_positionings(
+ self.tixi, fus_xpath, "fuselage")
- else:
- log.warning('No "positionings" have been found!')
- pos_cnt = 0
-
- # Sections
- sec_cnt = tixi.getNamedChildrenCount(fus_xpath + "/sections", "section")
- log.info(" -" + str(sec_cnt) + " fuselage sections have been found")
- x_fuselage = np.zeros(sec_cnt)
- y_fuselage_top = np.zeros(sec_cnt)
- y_fuselage_bottom = np.zeros(sec_cnt)
- fus_radius_vec = np.zeros(sec_cnt)
- body_width_vec = np.zeros(sec_cnt)
- body_height_vec = np.zeros(sec_cnt)
-
- if pos_cnt == 0:
- pos_x_list = [0.0] * sec_cnt
- pos_y_list = [0.0] * sec_cnt
- pos_z_list = [0.0] * sec_cnt
+ # Initialize to null array of size [sec_cnt]
+ (
+ x_fuselage, y_fuselage_top, y_fuselage_bottom,
+ fus_radius_vec, body_width_vec, body_height_vec
+
+ ) = (np.zeros(sec_cnt) for _ in range(6))
for i_sec in range(sec_cnt):
sec_xpath = fus_xpath + "/sections/section[" + str(i_sec + 1) + "]"
- sec_uid = tixi.getTextAttribute(sec_xpath, "uID")
-
+ sec_uid = self.tixi.getTextAttribute(sec_xpath, "uID")
sec_transf = Transformation()
- sec_transf.get_cpacs_transf(tixi, sec_xpath)
+ sec_transf.get_cpacs_transf(self.tixi, sec_xpath)
+ check_if_rotated(sec_transf.rotation, sec_uid)
- if sec_transf.rotation.x or sec_transf.rotation.y or sec_transf.rotation.z:
- log.warning(
- f"Sections '{sec_uid}' is rotated, it is"
- "not possible to take that into account in SUMO !"
- )
-
- # Elements
- elem_cnt = tixi.getNamedChildrenCount(sec_xpath + "/elements", "element")
+ elem_cnt = self.tixi.getNamedChildrenCount(sec_xpath + "/elements", "element")
- if elem_cnt > 1:
- log.warning(
- "Sections "
- + sec_uid
- + " contains multiple \
- element, it could be an issue for the conversion \
- to SUMO!"
+ for i_elem in range(elem_cnt):
+ elem_transf, prof_size_y, prof_size_z, _, _ = convert_fuselage_profiles(
+ self.tixi, sec_xpath, i_sec, i_elem, pos_y_list, pos_z_list
)
- for i_elem in range(elem_cnt):
- elem_xpath = sec_xpath + "/elements/element[" + str(i_elem + 1) + "]"
- elem_uid = tixi.getTextAttribute(elem_xpath, "uID")
-
- elem_transf = Transformation()
- elem_transf.get_cpacs_transf(tixi, elem_xpath)
-
- if elem_transf.rotation.x or elem_transf.rotation.y or elem_transf.rotation.z:
- log.warning(
- f"Element '{elem_uid}' is rotated, it is"
- "not possible to take that into account in SUMO !"
- )
-
- # Fuselage profiles
- prof_uid = tixi.getTextElement(elem_xpath + "/profileUID")
- prof_vect_x, prof_vect_y, prof_vect_z = get_profile_coord(tixi, prof_uid)
-
- prof_size_y = (max(prof_vect_y) - min(prof_vect_y)) / 2
- prof_size_z = (max(prof_vect_z) - min(prof_vect_z)) / 2
-
- prof_vect_y[:] = [y / prof_size_y for y in prof_vect_y]
- prof_vect_z[:] = [z / prof_size_z for z in prof_vect_z]
-
- prof_min_y = min(prof_vect_y)
- prof_min_z = min(prof_vect_z)
-
- prof_vect_y[:] = [y - 1 - prof_min_y for y in prof_vect_y]
- prof_vect_z[:] = [z - 1 - prof_min_z for z in prof_vect_z]
-
- # Could be a problem if they are less positionings than sections
- # TODO: solve that!
- pos_y_list[i_sec] += ((1 + prof_min_y) * prof_size_y) * elem_transf.scaling.y
- pos_z_list[i_sec] += ((1 + prof_min_z) * prof_size_z) * elem_transf.scaling.z
-
- # Compute coordinates of the center of section
- body_frm_center_x = (
- elem_transf.translation.x + sec_transf.translation.x + pos_x_list[i_sec]
- ) * fus_transf.scaling.x
-
- body_frm_center_z = (
- elem_transf.translation.z * sec_transf.scaling.z
- + sec_transf.translation.z
- + pos_z_list[i_sec]
- ) * fus_transf.scaling.z
-
- # Compute height and width of the section
- body_frm_height = (
- prof_size_z
- * 2
- * elem_transf.scaling.z
- * sec_transf.scaling.z
- * fus_transf.scaling.z
+ body_frm_width, body_frm_height = compute_fuselage_coords(
+ i_sec,
+ elem_transf,
+ sec_transf,
+ fus_transf,
+ pos_x_list, pos_z_list,
+ prof_size_y, prof_size_z,
+ fus_radius_vec,
+ x_fuselage,
+ y_fuselage_top, y_fuselage_bottom,
)
- body_frm_width = (
- prof_size_y
- * 2
- * elem_transf.scaling.y
- * sec_transf.scaling.y
- * fus_transf.scaling.y
+
+ body_width_vec[i_sec] = body_frm_width
+ body_height_vec[i_sec] = body_frm_height
+
+ body_transf_x = x_fuselage + body_transf.translation.x
+ fus_z_profile = interp1d(body_transf_x, y_fuselage_top - fus_radius_vec)
+ fus_radius_profile = interp1d(body_transf_x, fus_radius_vec)
+
+ self.write_fuselage_coords(
+ fus_dat_path, i_fus,
+ x_fuselage, y_fuselage_bottom, y_fuselage_top,
)
- # Compute diameter of the section as the mean between height and width
- # AVL assumes only circular cross section for fuselage
- fus_radius = np.mean([body_frm_height, body_frm_width]) / 2
- fus_radius_vec[i_sec] = (fus_radius)
+ return fus_z_profile, fus_radius_profile, body_transf
- # Save the coordinates of the fuselage
- x_fuselage[i_sec] = body_frm_center_x
- y_fuselage_top[i_sec] = body_frm_center_z + fus_radius
- y_fuselage_bottom[i_sec] = body_frm_center_z - fus_radius
+ def convert_wings(
+ self: "Avl",
+ fus_radius_profile: interp1d,
+ fus_z_profile: interp1d,
+ body_transf: Transformation,
+ ) -> None:
+ wing_cnt = elements_number(self.tixi, WINGS_XPATH, "wing")
- body_width_vec[i_sec] = body_frm_width
- body_height_vec[i_sec] = body_frm_height
+ for i_wing in range(wing_cnt):
+ root_defined = False
+ wing_xpath = WINGS_XPATH + "/wing[" + str(i_wing + 1) + "]"
- fus_z_profile = interpolate.interp1d(
- x_fuselage + body_transf.translation.x, y_fuselage_top - fus_radius_vec)
- fus_radius_profile = interpolate.interp1d(
- x_fuselage + body_transf.translation.x, fus_radius_vec)
-
- fuselage_dir = Path(results_path) / "Fuselage_files"
- fuselage_dir.mkdir(exist_ok=True)
- fuselage_dat_path = fuselage_dir / f"{fus_uid}.dat"
-
- with open(fuselage_dat_path, 'w') as fus_file:
- fus_file.write("fuselage" + str(i_fus + 1) + "\n")
-
- # Write coordinates of the top surface
- for x_fus, y_fus in reversed(list(zip(x_fuselage[1:], y_fuselage_top[1:]))):
- fus_file.write(f"{x_fus:.3f}\t{y_fus:.3f}\n")
-
- # Write coordinates of the nose of the fuselage
- y_nose = np.mean([y_fuselage_top[0], y_fuselage_bottom[0]])
- fus_file.write(f"{x_fuselage[0]:.3f}\t{y_nose:.3f}\n")
-
- # Write coordinates of the bottom surface
- for x_fus, y_fus in zip(x_fuselage[1:], y_fuselage_bottom[1:]):
- fus_file.write(f"{x_fus:.3f}\t{y_fus:.3f}\n")
-
- with open(avl_path, 'a') as avl_file:
- avl_file.write("BFILE\n")
- avl_file.write(str(fuselage_dat_path) + "\n\n")
-
- # Wing(s) ------------------------------------------------------------------
- if tixi.checkElement(WINGS_XPATH):
- wing_cnt = tixi.getNamedChildrenCount(WINGS_XPATH, "wing")
- log.info(str(wing_cnt) + " wings has been found.")
- else:
- wing_cnt = 0
- log.warning("No wings has been found in this CPACS file!")
-
- if vortex_distribution > 3 or vortex_distribution < -3:
- log.warning(
- "The vortex distribution is not in the range [-3 ; 3]. "
- + "Default value of 1 will be used.")
- vortex_distribution = 1
-
- for i_wing in range(wing_cnt):
- root_defined = False
- wing_xpath = WINGS_XPATH + "/wing[" + str(i_wing + 1) + "]"
- wing_transf = Transformation()
- wing_transf.get_cpacs_transf(tixi, wing_xpath)
-
- # Create a class for the transformation of the WingSkeleton
- wg_sk_transf = Transformation()
-
- # Convert WingSkeleton rotation
- wg_sk_transf.rotation = euler2fix(wing_transf.rotation)
-
- # Add WingSkeleton origin
- wg_sk_transf.translation = wing_transf.translation
- wg_sk_ori_str = (
- str(round(wg_sk_transf.translation.x, 3))
- + "\t"
- + str(round(wg_sk_transf.translation.y, 3))
- + "\t"
- + str(round(wg_sk_transf.translation.z, 3))
- )
+ # Retrieve chord and span length of specific wing
+ c_ref, s_ref = get_chord_span(self.tixi, wing_xpath)
- # Write wing settings
- with open(avl_path, 'a') as avl_file:
- avl_file.write("#--------------------------------------------------\n")
- avl_file.write("SURFACE\n")
- avl_file.write("Wing\n\n")
- avl_file.write("!Nchordwise Cspace Nspanwise Sspace\n")
- avl_file.write(
- f"{Nchordwise} {vortex_distribution} {Nspanwise} {vortex_distribution}\n\n")
- avl_file.write('COMPONENT\n')
- avl_file.write("1\n\n")
+ wing_transf = Transformation()
+ wing_transf.get_cpacs_transf(self.tixi, wing_xpath)
- # Symmetry
- if tixi.checkAttribute(wing_xpath, "symmetry"):
- if tixi.getTextAttribute(wing_xpath, "symmetry") == "x-z-plane":
- avl_file.write('YDUPLICATE\n')
- avl_file.write("0\n\n")
-
- # Angle
- avl_file.write('ANGLE\n')
- avl_file.write("0\n\n")
-
- # Scaling
- avl_file.write("SCALE\n")
- avl_file.write(str(wing_transf.scaling.x)
- + "\t"
- + str(wing_transf.scaling.y)
- + "\t"
- + str(wing_transf.scaling.z)
- + "\n\n")
-
- # Translation
- avl_file.write("TRANSLATE\n")
- avl_file.write(wg_sk_ori_str + "\n\n")
-
- # Positionings
- if tixi.checkElement(wing_xpath + "/positionings"):
- pos_cnt = tixi.getNamedChildrenCount(wing_xpath + "/positionings", "positioning")
- log.info(str(pos_cnt) + ' "positioning" has been found : ')
-
- pos_x_list = []
- pos_y_list = []
- pos_z_list = []
- from_sec_list = []
- to_sec_list = []
-
- for i_pos in range(pos_cnt):
- pos_xpath = wing_xpath + "/positionings/positioning[" + str(i_pos + 1) + "]"
-
- length = tixi.getDoubleElement(pos_xpath + "/length")
- sweep_deg = tixi.getDoubleElement(pos_xpath + "/sweepAngle")
- sweep = math.radians(sweep_deg)
- dihedral_deg = tixi.getDoubleElement(pos_xpath + "/dihedralAngle")
- dihedral = math.radians(dihedral_deg)
-
- # Get the corresponding translation of each positioning
- pos_x_list.append(length * math.sin(sweep))
- pos_y_list.append(length * math.cos(dihedral) * math.cos(sweep))
- pos_z_list.append(length * math.sin(dihedral) * math.cos(sweep))
-
- # Get which section are connected by the positioning
- if tixi.checkElement(pos_xpath + "/fromSectionUID"):
- from_sec = tixi.getTextElement(pos_xpath + "/fromSectionUID")
- else:
- from_sec = ""
- from_sec_list.append(from_sec)
+ # Create a class for the transformation of the WingSkeleton
+ wg_sk_transf = Transformation()
+ wg_sk_transf.rotation = euler2fix(wing_transf.rotation)
+ wg_sk_transf.translation = wing_transf.translation
- if tixi.checkElement(pos_xpath + "/toSectionUID"):
- to_sec = tixi.getTextElement(pos_xpath + "/toSectionUID")
- else:
- to_sec = ""
- to_sec_list.append(to_sec)
-
- # Re-loop though the positioning to re-order them
- for j_pos in range(pos_cnt):
- if from_sec_list[j_pos] == "":
- prev_pos_x = 0
- prev_pos_y = 0
- prev_pos_z = 0
- elif from_sec_list[j_pos] == to_sec_list[j_pos - 1]:
- prev_pos_x = pos_x_list[j_pos - 1]
- prev_pos_y = pos_y_list[j_pos - 1]
- prev_pos_z = pos_z_list[j_pos - 1]
- else:
- index_prev = to_sec_list.index(from_sec_list[j_pos])
- prev_pos_x = pos_x_list[index_prev]
- prev_pos_y = pos_y_list[index_prev]
- prev_pos_z = pos_z_list[index_prev]
-
- pos_x_list[j_pos] += prev_pos_x
- pos_y_list[j_pos] += prev_pos_y
- pos_z_list[j_pos] += prev_pos_z
-
- else:
- log.warning('No "positionings" have been found!')
- pos_cnt = 0
-
- # Sections
- sec_cnt = tixi.getNamedChildrenCount(wing_xpath + "/sections", "section")
- log.info(" -" + str(sec_cnt) + " wing sections have been found")
-
- if pos_cnt == 0:
- pos_x_list = [0.0] * sec_cnt
- pos_y_list = [0.0] * sec_cnt
- pos_z_list = [0.0] * sec_cnt
-
- for i_sec in range(sec_cnt):
- sec_xpath = wing_xpath + "/sections/section[" + str(i_sec + 1) + "]"
- sec_uid = tixi.getTextAttribute(sec_xpath, "uID")
- sec_transf = Transformation()
- sec_transf.get_cpacs_transf(tixi, sec_xpath)
-
- # Elements
- elem_cnt = tixi.getNamedChildrenCount(sec_xpath + "/elements", "element")
-
- if elem_cnt > 1:
- log.warning(
- f"Sections {sec_uid} contains multiple element,"
- " it could be an issue for the conversion to SUMO!"
- )
+ self.write_wing_settings(
+ wing_xpath,
+ wing_transf.scaling,
+ wg_sk_transf.translation,
+ )
- for i_elem in range(elem_cnt):
- elem_xpath = sec_xpath + "/elements/element[" + str(i_elem + 1) + "]"
- elem_transf = Transformation()
- elem_transf.get_cpacs_transf(tixi, elem_xpath)
+ sec_cnt, pos_x_list, pos_y_list, pos_z_list = get_positionings(
+ self.tixi, wing_xpath, "wing")
- # Get wing profile (airfoil)
- prof_uid = tixi.getTextElement(elem_xpath + "/airfoilUID")
- prof_vect_x, prof_vect_y, prof_vect_z = get_profile_coord(tixi, prof_uid)
- airfoil_dir = Path(results_path) / "Airfoil_files"
- airfoil_dir.mkdir(exist_ok=True)
- airfoil_dat_path = airfoil_dir / f"{prof_uid}.dat"
-
- with open(airfoil_dat_path, 'w') as dat_file:
- dat_file.write(prof_uid + "\n")
- # Limit the number of points to 100 (otherwise AVL error)
- if len(prof_vect_x) < 100:
- for coord_x, coord_z in zip(prof_vect_x, prof_vect_z):
- dat_file.write(str(coord_x) + '\t' + str(coord_z) + "\n")
- else:
- step = round(len(prof_vect_x) / 100)
- for coord_x, coord_z in zip(prof_vect_x[0:len(prof_vect_x):step],
- prof_vect_z[0:len(prof_vect_x):step]):
- dat_file.write(str(coord_x) + '\t' + str(coord_z) + "\n")
+ for i_sec in range(sec_cnt):
+ sec_xpath = wing_xpath + "/sections/section[" + str(i_sec + 1) + "]"
+ sec_transf = Transformation()
+ sec_transf.get_cpacs_transf(self.tixi, sec_xpath)
- # Apply scaling
- for i, item in enumerate(prof_vect_x):
- # wing_transf.scaling removed
- prof_vect_x[i] = (item * elem_transf.scaling.x * sec_transf.scaling.x)
+ elem_xpath = sec_xpath + "/elements/element[1]"
+ elem_transf = Transformation()
+ elem_transf.get_cpacs_transf(self.tixi, elem_xpath)
- for i, item in enumerate(prof_vect_y):
- prof_vect_y[i] = (item * elem_transf.scaling.y * sec_transf.scaling.y)
+ # Get wing profile (airfoil)
+ prof_uid, prof_vect_x, prof_vect_y, prof_vect_z = get_profile_coord(
+ self.tixi, elem_xpath + "/airfoilUID")
- for i, item in enumerate(prof_vect_z):
- prof_vect_z[i] = (item * elem_transf.scaling.z * sec_transf.scaling.z)
+ airfoil_dir = Path(self.results_dir) / "Airfoil_files"
+ airfoil_dir.mkdir(exist_ok=True)
+ foil_dat_path = str(airfoil_dir / f"{prof_uid}.dat")
- prof_size_x = max(prof_vect_x) - min(prof_vect_x)
- prof_size_y = max(prof_vect_y) - min(prof_vect_y)
+ write_airfoil_coords(foil_dat_path, prof_uid, prof_vect_x, prof_vect_z)
- if prof_size_y == 0:
- prof_vect_x[:] = [x / prof_size_x for x in prof_vect_x]
- prof_vect_z[:] = [z / prof_size_x for z in prof_vect_z]
- # Is it correct to divide by prof_size_x ????
+ # Apply scaling
+ x, y, z = prod_points(elem_transf.scaling, sec_transf.scaling)
+ prof_vect_x *= x
+ prof_vect_y *= y
+ prof_vect_z *= z
- wg_sec_chord = prof_size_x
- else:
- log.error("An airfoil profile is not define correctly")
+ wg_sec_chord = corrects_airfoil_profile(prof_vect_x, prof_vect_y, prof_vect_z)
# Add rotation from element and sections
# Adding the two angles: Maybe not work in every case!!!
- add_rotation = SimpleNamespace()
- add_rotation.x = sec_transf.rotation.x + wg_sk_transf.rotation.x
- add_rotation.y = elem_transf.rotation.y + \
- sec_transf.rotation.y + wg_sk_transf.rotation.y
- add_rotation.z = sec_transf.rotation.z + wg_sk_transf.rotation.z
+ x, y, z = sum_points(
+ elem_transf.rotation,
+ sec_transf.rotation,
+ wg_sk_transf.rotation,
+ )
+ add_rotation = Point(x=x, y=y, z=z)
# Get Section rotation
wg_sec_rot = euler2fix(add_rotation)
@@ -578,59 +490,133 @@ def convert_cpacs_to_avl(cpacs_path, machnumber, wkdir):
wg_sec_twist = math.radians(wg_sec_rot.y)
wg_sec_yaw = math.radians(wg_sec_rot.z)
- # Define the leading edge position from translation/scaling
- x_LE = elem_transf.translation.x + \
- sec_transf.translation.x * sec_transf.scaling.x + pos_x_list[i_sec]
-
- y_LE = elem_transf.translation.y * elem_transf.scaling.y + \
- sec_transf.translation.y * sec_transf.scaling.y + pos_y_list[i_sec]
-
- z_LE = elem_transf.translation.z * elem_transf.scaling.z + \
- sec_transf.translation.z * sec_transf.scaling.z + pos_z_list[i_sec]
-
- x_LE_rot, y_LE_rot, z_LE_rot = rotate_3D_points(
- x_LE, y_LE, z_LE, wg_sec_dihed, wg_sec_twist, wg_sec_yaw)
+ if all(abs(value) < 1e-6 for value in pos_y_list):
+ # Define the leading edge position from translations
+ x_LE, y_LE, z_LE = sum_points(sec_transf.translation, elem_transf.translation)
+ x_LE_rot, y_LE_rot, z_LE_rot = rotate_points(
+ x_LE, y_LE, z_LE, wg_sec_dihed, wg_sec_twist, wg_sec_yaw)
+ else:
+ x_LE_rot, y_LE_rot, z_LE_rot = rotate_points(
+ pos_x_list[i_sec], pos_y_list[i_sec], pos_z_list[i_sec],
+ wg_sec_dihed, wg_sec_twist, wg_sec_yaw)
# Compute the absolute location of the leading edge
x_LE_abs = x_LE_rot + wg_sk_transf.translation.x
y_LE_abs = y_LE_rot + wg_sk_transf.translation.y
z_LE_abs = z_LE_rot + wg_sk_transf.translation.z
- if integrate_fuselage:
+ if self.add_fuselage:
# Compute the radius of the fuselage and the height difference ...
# between fuselage center and leading edge
radius_fus = fus_radius_profile(x_LE_abs + wg_sec_chord / 2)
fus_z_center = fus_z_profile(x_LE_abs + wg_sec_chord / 2)
- delta_z = np.abs(fus_z_center + body_transf.translation.z - z_LE_abs)
-
- # If the root wing section is inside the fuselage, translate it to...
- # the fuselage border
- # To make sure there is no wing part inside the fuselage
- if integrate_fuselage and np.sqrt((y_LE_abs)**2 + (delta_z)**2) < radius_fus and \
- wg_sec_dihed < math.pi / 2 and root_defined is False:
-
- y_LE_abs += np.sqrt(radius_fus**2 - delta_z**2) - y_LE_abs
- y_LE_rot = y_LE_abs - wg_sk_transf.translation.y
- root_defined = True
-
- with open(avl_path, 'a') as avl_file:
- avl_file.writelines([
- "#---------------\n",
- "SECTION\n",
- "#Xle Yle Zle Chord Ainc\n"
- ])
- avl_file.write(f"{x_LE_rot:.3f} {y_LE_rot:.3f} {z_LE_rot:.3f} "
- f"{(wg_sec_chord):.3f} {wg_sec_rot.y}\n\n")
-
- avl_file.writelines(["AFILE\n", str(airfoil_dat_path) + "\n\n"])
+ delta_z = np.abs(
+ fus_z_center + body_transf.translation.z - z_LE_abs)
+
+ # If the root wing section is inside the fuselage, translate it to...
+ # the fuselage border
+ # To make sure there is no wing part inside the fuselage
+ if np.sqrt((y_LE_abs)**2 + (delta_z)**2) < radius_fus and \
+ wg_sec_dihed < math.pi / 2 and root_defined is False:
+
+ y_LE_abs += np.sqrt(radius_fus**2 - delta_z**2) - y_LE_abs
+ y_LE_rot = y_LE_abs - wg_sk_transf.translation.y
+ root_defined = True
+
+ leadingedge_coordinates(
+ self.tixi,
+ self.avl_path,
+ i_wing,
+ i_sec,
+ x_LE_rot,
+ y_LE_rot,
+ z_LE_rot,
+ wg_sec_chord,
+ wg_sec_rot,
+ wing_xpath,
+ c_ref,
+ s_ref,
+ foil_dat_path,
+ )
- return Path(avl_path)
+ def write_wing_settings(
+ self,
+ wing_xpath,
+ scaling,
+ translation,
+ ) -> None:
+ # Write wing settings
+ with open(self.avl_path, 'a') as avl_file:
+ avl_file.write("#--------------------------------------------------\n")
+ avl_file.write("SURFACE\nWing\n\n")
+ avl_file.write("!Nchordwise Cspace Nspanwise Sspace\n")
+ avl_file.write(
+ f"{self.nchordwise} {self.vortex_dist} {self.nspanwise} {self.vortex_dist}\n\n"
+ )
+ avl_file.write("COMPONENT\n1\n\n")
+ # Symmetry
+ if (
+ self.tixi.checkAttribute(wing_xpath, "symmetry")
+ and self.tixi.getTextAttribute(wing_xpath, "symmetry") == "x-z-plane"
+ ):
+ avl_file.write("YDUPLICATE\n0\n\n")
+
+ # Angle, scaling and translation
+ avl_file.write("ANGLE\n0\n\n")
+ avl_file.write(f"SCALE\n{to_cpacs_format(scaling)}\n\n")
+ avl_file.write(f"TRANSLATE\n{to_cpacs_format(translation)}\n\n")
+
+ def write_ref_values(self) -> None:
+ with open(self.avl_path, 'a') as avl_file:
+ # Reference dimensions
+ avl_file.write("#Sref Cref Bref\n")
+ avl_file.write(f"{self.area_ref:.3f}\t{self.chord_ref:.3f}\t{self.span_ref:.3f}\n\n")
+
+ # Reference location for moments/rotations
+ avl_file.write("#Xref Yref Zref\n")
+ for i_points in range(3):
+ avl_file.write(f"{self.points_ref[i_points]:.3f}\t")
+ avl_file.write("\n\n")
+
+ def write_fuselage_coords(
+ self: "Avl",
+ fus_dat_path: Path,
+ i_fus: int,
+ x_fuselage, y_fuselage_bottom, y_fuselage_top
+ ) -> None:
+ with open(fus_dat_path, 'w') as fus_file:
+ fus_file.write("fuselage" + str(i_fus + 1) + "\n")
+
+ # Write coordinates of the top surface
+ for x_fus, y_fus in reversed(
+ list(zip(x_fuselage[1:], y_fuselage_top[1:]))):
+ fus_file.write(f"{x_fus:.3f}\t{y_fus:.3f}\n")
+
+ # Write coordinates of the nose of the fuselage
+ y_nose = np.mean([y_fuselage_top[0], y_fuselage_bottom[0]])
+ fus_file.write(f"{x_fuselage[0]:.3f}\t{y_nose:.3f}\n")
+
+ # Write coordinates of the bottom surface
+ for x_fus, y_fus in zip(x_fuselage[1:], y_fuselage_bottom[1:]):
+ fus_file.write(f"{x_fus:.3f}\t{y_fus:.3f}\n")
+
+ with open(self.avl_path, 'a') as avl_file:
+ avl_file.write("BFILE\n")
+ avl_file.write(fus_dat_path + "\n\n")
+
+ def write_fuselage_settings(self, scaling: Point, translation: Point) -> None:
+ with open(self.avl_path, 'a') as avl_file:
+ avl_file.write("#--------------------------------------------------\n")
+ avl_file.write("BODY\nFuselage\n\n")
+ avl_file.write("!Nbody Bspace\n100\t1.0\n\n")
+ avl_file.write(f"SCALE\n{to_cpacs_format(scaling)}\n\n")
+ avl_file.write(f"TRANSLATE\n{to_cpacs_format(translation)}\n\n")
# =================================================================================================
# MAIN
-# =================================================================================================
+# ================================================================================================
-if __name__ == "__main__":
+if __name__ == "__main__":
log.info("Nothing to execute!")
diff --git a/ceasiompy/PyAVL/func/plot.py b/ceasiompy/PyAVL/func/plot.py
new file mode 100644
index 000000000..29470ccaa
--- /dev/null
+++ b/ceasiompy/PyAVL/func/plot.py
@@ -0,0 +1,134 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Extract results from AVL calculations and save them in a CPACS file.
+
+
+| Author: Leon Deligny
+| Creation: 2025-Feb-14
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+import subprocess
+
+import pandas as pd
+import matplotlib.pyplot as plt
+
+from pathlib import Path
+
+from ceasiompy import log
+
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def convert_ps_to_pdf(wkdir: Path) -> None:
+ """
+ Function to convert AVL 'plot.ps' to 'plot.pdf'.
+
+ Args:
+ wkdir (Path): Path to the working directory.
+
+ """
+ if not Path(wkdir, "plot.ps").exists():
+ log.warning("File 'plot.ps' does not exist. Nothing to convert.")
+ else:
+ subprocess.run(["ps2pdf", "plot.ps", "plot.pdf"], cwd=wkdir)
+ subprocess.run(["rm", "plot.ps"], cwd=wkdir)
+
+
+def plot_lift_distribution(
+ force_file_fs: Path,
+ aoa: float,
+ aos: float,
+ mach: float,
+ alt: float,
+ wkdir: Path
+) -> None:
+ """
+ Plot the lift distribution from AVL strip forces file (fs.txt)
+
+ Args:
+ force_file_fs (Path): Path to the AVL strip forces file.
+ aoa (float): Angle of attack [deg].
+ aos (float): Angle of sideslip [deg].
+ mach (float): Mach number.
+ alt (float): Flight altitude [m].
+ wkir (Path): Path to the directory on where to save the plot.
+
+ """
+ y_list = []
+ chord_list = []
+ cl_list = []
+ clnorm_list = []
+
+ with open(force_file_fs, "r") as fs:
+ for line in fs:
+ if "Cref =" in line:
+ cref = float(line.split()[5])
+
+ elif "# Spanwise =" in line:
+ number_strips = int(line.split()[7])
+
+ elif "Xle" in line:
+ number_data = 0
+ for line in fs:
+ data = line.split()
+ y_list.append(float(data[2]))
+ chord_list.append(float(data[4]))
+ cl_list.append(float(data[9]))
+ clnorm_list.append(float(data[8]))
+ number_data += 1
+
+ # break when every line has been extracted
+ if number_data == number_strips:
+ break
+
+ data_df = pd.DataFrame({
+ "y": y_list,
+ "chord": chord_list,
+ "cl": cl_list,
+ "cl_norm": clnorm_list
+ })
+
+ data_df.sort_values(by="y", inplace=True)
+ data_df.reset_index(drop=True, inplace=True)
+ data_df["cl_cref"] = data_df["cl"] * data_df["chord"] / cref
+
+ _, ax = plt.subplots(figsize=(10, 5))
+ data_df.plot("y", "cl_norm", ax=ax, label=r"$c_{l\perp}$", linestyle="dashed", color="r")
+ data_df.plot("y", "cl", label=r"$c_l$", ax=ax, linestyle="dashed", color="#FFA500")
+ data_df.plot(
+ "y", "cl_cref", ax=ax, label=r"$c_l \cdot C/C_{ref}$", linestyle="solid", color="#41EE33"
+ )
+
+ plt.title(
+ (
+ "Lift distribution along the wing span "
+ r"($\alpha=%.1f^{\circ}$, $\beta=%.1f^{\circ}$, "
+ r"$M=%.1f$, alt = %d m)"
+ )
+ % (aoa, aos, mach, alt),
+ fontsize=14,
+ )
+
+ plt.ylabel(r"$C_l$", rotation=0, fontsize=12)
+ plt.legend(fontsize=12)
+ plt.grid()
+ plt.savefig(Path(wkdir, "lift_distribution.png"))
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute.")
diff --git a/ceasiompy/PyAVL/func/results.py b/ceasiompy/PyAVL/func/results.py
new file mode 100644
index 000000000..fb48d41cf
--- /dev/null
+++ b/ceasiompy/PyAVL/func/results.py
@@ -0,0 +1,356 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Extract results from AVL calculations and save them in a CPACS file.
+
+
+| Author: Leon Deligny
+| Creation: 2025-Feb-14
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+from cpacspy.aeromap import get_filter
+from cpacspy.cpacsfunctions import get_value
+from ceasiompy.PyAVL.func.plot import plot_lift_distribution
+from ceasiompy.PyAVL.func.utils import (
+ split_dir,
+ split_line,
+)
+from ceasiompy.utils.ceasiompyutils import (
+ bool_,
+ ensure_and_append_text_element,
+)
+
+from typing import Tuple
+from pathlib import Path
+from cpacspy.cpacspy import CPACS, AeroMap
+from tixi3.tixi3wrapper import Tixi3
+
+from ceasiompy import log
+from ceasiompy.PyAVL.func import AVL_COEFS
+from ceasiompy.PyAVL import (
+ AVL_XPATH,
+ AVL_TABLE_XPATH,
+ AVL_PLOTLIFT_XPATH,
+ AVL_CTRLTABLE_XPATH,
+ AVL_AEROMAP_UID_XPATH,
+)
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def get_avl_aerocoefs(force_file: Path) -> Tuple[
+ float, float, float,
+ float, float, float,
+ float, float, float,
+]:
+ """
+ Get aerodynamic coefficients and velocity from AVL total forces file (sb.txt).
+
+ Args:
+ force_file_ft (Path): Path to the AVL total forces file.
+
+ Returns:
+ cl (float): Lift coefficient.
+ cd (float): Drag coefficient.
+ cs (float): Side force coefficient.
+ cmd (float): Rolling moment.
+ cml (float): Yawing moment.
+ cms (float): Pitching moment.
+ cms_a (float): Derivative of Pitching moment with respect to the angle of attack.
+ cml_b (float): Derivative of Yawing moment with respect to the sidesplip angle.
+ cmd_b (float): Derivative of Rolling moment with respect to the sidesplip angle.
+
+ """
+
+ results = {key: None for key in AVL_COEFS.values()}
+
+ with open(force_file) as f:
+ for line in f.readlines():
+ for key, (index, var_name) in AVL_COEFS.items():
+ if key in line:
+ # Exception as they appear twice in .txt file
+ if key in ["Clb", "Cnb"]:
+ parts = line.split('=')
+ if len(parts) > 2:
+ results[var_name] = split_line(line, index)
+ else:
+ results[var_name] = split_line(line, index)
+
+ return (
+ results["cd"], results["cs"], results["cl"],
+ results["cmd"], results["cms"], results["cml"],
+ results["cmd_b"], results["cms_a"], results["cml_b"],
+ )
+
+
+def add_coefficients_in_aeromap(
+ cpacs: CPACS,
+ alt: float,
+ mach: float,
+ aos: float,
+ aoa: float,
+ fs_file_path: Path,
+ st_file_path: Path,
+ config_dir: Path,
+) -> None:
+ """
+ Add aerodynamic coefficients from PyAVL in chosen aeromap.
+
+ Args:
+ cpacs (CPACS): CPACS file.
+ alt (float): Altitude.
+ mach (float): Mach Number.
+ aos (float): SideSlip angle.
+ aoa (float): Angle of attack.
+ fs_file_path (Path): Path to force coefficients for plot.
+ st_file_path (Path): Path to moment coefficients.
+ config_dir (Path): Configuration Directory for plot.
+
+ """
+
+ tixi = cpacs.tixi
+
+ cl, cd, cs, cmd, cml, cms, cms_a, cml_b, cmd_b = get_avl_aerocoefs(st_file_path)
+
+ plot = bool_(get_value(tixi, AVL_PLOTLIFT_XPATH))
+
+ if plot:
+ plot_lift_distribution(fs_file_path, aoa, aos, mach, alt, wkdir=config_dir)
+
+ aeromap_uid = get_value(tixi, AVL_AEROMAP_UID_XPATH)
+ aeromap: AeroMap = cpacs.get_aeromap_by_uid(aeromap_uid)
+
+ filt = get_filter(aeromap.df, [alt], [mach], [aos], [aoa])
+ if aeromap.df.loc[filt].empty:
+ aeromap.add_row(
+ alt=alt,
+ mach=mach,
+ aos=aos,
+ aoa=aoa,
+ )
+
+ # Add coefficients to the aeromap
+ aeromap.add_coefficients(
+ alt=alt,
+ mach=mach,
+ aos=aos,
+ aoa=aoa,
+ cd=cd,
+ cl=cl,
+ cs=cs,
+ cmd=cmd,
+ cml=cml,
+ cms=cms
+ )
+
+ increment_maps_xpath = f"{aeromap.xpath}/incrementMaps"
+ increment_map_xpath = f"{increment_maps_xpath}/incrementMap"
+
+ # Ensure the incrementMaps and incrementMap elements exist
+ if not tixi.checkElement(increment_maps_xpath):
+ tixi.createElement(aeromap.xpath, "incrementMaps")
+ if not tixi.checkElement(increment_map_xpath):
+ tixi.createElement(increment_maps_xpath, "incrementMap")
+
+ # Add text elements for the coefficients
+ ensure_and_append_text_element(tixi, increment_map_xpath, "dcmd", str(cmd_b))
+ ensure_and_append_text_element(tixi, increment_map_xpath, "dcms", str(cms_a))
+ ensure_and_append_text_element(tixi, increment_map_xpath, "dcml", str(cml_b))
+
+ aeromap.save()
+
+
+def add_coefficients(
+ tixi: Tixi3,
+ xpath: str,
+ table_name: str,
+ coefficients: dict,
+) -> None:
+ """
+ Adds aerodynamic coefficients to a specified table.
+ """
+
+ if not tixi.checkElement(xpath):
+ tixi.createElement(AVL_XPATH, table_name)
+
+ # Add text elements for the coefficients
+ for name, value in coefficients.items():
+ ensure_and_append_text_element(tixi, xpath, name, str(value))
+
+
+def add_coefficients_in_table(
+ tixi: Tixi3,
+ mach: float,
+ aos: float,
+ aoa: float,
+ p: float,
+ q: float,
+ r: float,
+ st_file_path: Path,
+) -> None:
+ """
+ Add aerodynamic coefficients from PyAVL in specific table.
+ xPath of Table: AVL_TABLE_XPATH.
+
+ Args:
+ cpacs (CPACS): CPACS file.
+ mach (float): Mach Number.
+ aos (float): SideSlip angle.
+ aoa (float): Angle of attack.
+ p (float): Roll rate.
+ q (float): Pitch rate.
+ r (float): Yaw rate.
+ st_file_path (Path): Path to moment coefficients.
+
+ """
+
+ cd, cs, cl, cmd, cms, cml, _, _, _ = get_avl_aerocoefs(st_file_path)
+
+ coefficients = {
+ "mach": mach,
+ "aoa": aoa, "aos": aos,
+ "p": p, "q": q, "r": r,
+ "cd": cd, "cs": cs, "cl": cl,
+ "cmd": cmd, "cms": cms, "cml": cml,
+ }
+
+ add_coefficients(tixi, AVL_TABLE_XPATH, "Table", coefficients)
+
+
+def add_coefficients_in_ctrltable(
+ tixi: Tixi3,
+ mach: float,
+ aoa: float,
+ aileron: float,
+ elevator: float,
+ rudder: float,
+ st_file_path: Path,
+) -> None:
+ """
+ Add aerodynamic coefficients from PyAVL in specific ctrltable.
+ xPath of Table: AVL_CTRLTABLE_XPATH.
+
+ Args:
+ cpacs (CPACS): CPACS file.
+ mach (float): Mach Number.
+ aos (float): SideSlip angle.
+ aoa (float): Angle of attack.
+ aileron (float): Aileron rate.
+ elevator (float): Elevator rate.
+ rudder (float): Rudder rate.
+ st_file_path (Path): Path to moment coefficients.
+
+ """
+
+ cd, cs, cl, cmd, cms, cml, _, _, _ = get_avl_aerocoefs(st_file_path)
+
+ coefficients = {
+ "mach": mach,
+ "aoa": aoa,
+ "aileron": aileron, "elevator": elevator, "rudder": rudder,
+ "cd": cd, "cs": cs, "cl": cl,
+ "cmd": cmd, "cms": cms, "cml": cml,
+ }
+
+ add_coefficients(tixi, AVL_CTRLTABLE_XPATH, "CtrlTable", coefficients)
+
+
+def get_force_files(config_dir: Path) -> Tuple[Path, Path]:
+ st_file_path = Path(config_dir, "st.txt")
+ if not st_file_path.exists():
+ raise FileNotFoundError(
+ f"No result total forces 'st.txt' file have been found at {st_file_path}. "
+ )
+
+ fs_file_path = Path(config_dir, "fs.txt")
+ if not fs_file_path.exists():
+ raise FileNotFoundError(
+ f"No result strip forces 'fs.txt' file have been found {fs_file_path}"
+ )
+
+ return st_file_path, fs_file_path
+
+
+def get_avl_results(cpacs: CPACS, results_dir: Path) -> None:
+ """
+ Write AVL results in a CPACS file at xPath:
+ '/cpacs/vehicles/aircraft/model/analyses/aeroPerformance/aeroMap[n]/aeroPerformanceMap'
+ """
+
+ tixi = cpacs.tixi
+ case_dir_list = [
+ case_dir
+ for case_dir in results_dir.iterdir()
+ if ("Case" in case_dir.name) and (case_dir.is_dir())
+ ]
+
+ for config_dir in sorted(case_dir_list):
+ dir_name = config_dir.name
+ st_file_path, fs_file_path = get_force_files(config_dir)
+
+ # Extract common parameters
+ alt = split_dir(dir_name, 1, "alt")
+ mach = split_dir(dir_name, 2, "mach")
+ aoa = split_dir(dir_name, 3, "aoa" if "p" in dir_name else "alt")
+
+ if "p" in dir_name: # Standard aeromap or dynamic stability
+ aos = split_dir(dir_name, 4, "aos")
+ q = split_dir(dir_name, 5, "q")
+ p = split_dir(dir_name, 6, "p")
+ r = split_dir(dir_name, 7, "r")
+
+ if (p == 0.0) and (q == 0.0) and (r == 0.0):
+ add_coefficients_in_aeromap(
+ cpacs,
+ alt,
+ mach,
+ aos,
+ aoa,
+ fs_file_path,
+ st_file_path,
+ config_dir,
+ )
+
+ # Add coefficients for dynamic stability
+ add_coefficients_in_table(
+ tixi,
+ mach,
+ aos,
+ aoa,
+ p,
+ q,
+ r,
+ st_file_path,
+ )
+ else: # Control surface deflections for dynamic stability
+ aileron = split_dir(dir_name, 4, "aileron")
+ elevator = split_dir(dir_name, 5, "elevator")
+ rudder = split_dir(dir_name, 6, "rudder")
+
+ # Add coefficients for control surface deflections
+ add_coefficients_in_ctrltable(
+ tixi,
+ mach,
+ aoa,
+ aileron,
+ elevator,
+ rudder,
+ st_file_path,
+ )
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/PyAVL/func/rotation3D.py b/ceasiompy/PyAVL/func/rotation3D.py
deleted file mode 100644
index 9b4c3deb0..000000000
--- a/ceasiompy/PyAVL/func/rotation3D.py
+++ /dev/null
@@ -1,88 +0,0 @@
-"""
-CEASIOMpy: Conceptual Aircraft Design Software
-
-Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-
-Function to apply a 3D rotation to the coordinates of a point.
-
-Python version: >=3.8
-
-| Author: Romain Gauthier
-| Creation: 2024-03-13
-
-"""
-
-
-# ==============================================================================
-# IMPORTS
-# ==============================================================================
-
-from ceasiompy.utils.ceasiomlogger import get_logger
-import numpy as np
-import math
-
-log = get_logger()
-
-
-# ==============================================================================
-# FUNCTIONS
-# ==============================================================================
-
-
-def rotate_3D_points(x, y, z, angle_x, angle_y, angle_z):
- """Function to apply a 3D rotation to the coordinates of a point
-
- Function 'rotate_3D_points' returns the rotated points after applying
- a 3D rotation matrix.
-
- Source:
- * https://en.wikipedia.org/wiki/Rotation_matrix
-
- Args:
- x (float): x coordinate of the initial point
- y (float): y coordinate of the initial point
- z (float): z coordinate of the initial point
- angle_x (float): rotation angle around x-axis [rad]
- angle_y (float): rotation angle around y-axis [rad]
- angle_z (float): rotation angle around z-axis [rad]
-
- Returns:
- x_rot (float): x coordinate of the rotated point
- y_rot (float): y coordinate of the rotated point
- z_rot (float): z coordinate of the rotated point
- """
- R11 = math.cos(angle_z) * math.cos(angle_y)
- R12 = math.cos(angle_z) * math.sin(angle_y) * math.sin(angle_x) - \
- math.sin(angle_z) * math.cos(angle_x)
- R13 = math.cos(angle_z) * math.sin(angle_y) * math.cos(angle_x) + \
- math.sin(angle_z) * math.sin(angle_x)
- R21 = math.sin(angle_z) * math.cos(angle_y)
- R22 = math.sin(angle_z) * math.sin(angle_y) * math.sin(angle_x) + \
- math.cos(angle_z) * math.cos(angle_x)
- R23 = math.sin(angle_z) * math.sin(angle_y) * math.cos(angle_x) - \
- math.cos(angle_z) * math.sin(angle_x)
- R31 = -math.sin(angle_y)
- R32 = math.cos(angle_y) * math.sin(angle_x)
- R33 = math.cos(angle_y) * math.cos(angle_x)
-
- rotation_matrix = np.array([[R11, R12, R13],
- [R21, R22, R23],
- [R31, R32, R33]])
-
- x_rot = x * rotation_matrix[0, 0] + y * \
- rotation_matrix[0, 1] + z * rotation_matrix[0, 2]
- y_rot = x * rotation_matrix[1, 0] + y * \
- rotation_matrix[1, 1] + z * rotation_matrix[1, 2]
- z_rot = x * rotation_matrix[2, 0] + y * \
- rotation_matrix[2, 1] + z * rotation_matrix[2, 2]
-
- return x_rot, y_rot, z_rot
-
-
-# ==============================================================================
-# MAIN
-# ==============================================================================
-
-if __name__ == "__main__":
-
- print("Nothing to execute!")
diff --git a/ceasiompy/PyAVL/func/utils.py b/ceasiompy/PyAVL/func/utils.py
new file mode 100644
index 000000000..8314b5857
--- /dev/null
+++ b/ceasiompy/PyAVL/func/utils.py
@@ -0,0 +1,167 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Utils for PyAVL module.
+
+
+| Author: Leon Deligny
+| Creation: 2025-Feb-28
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+import numpy as np
+
+from pydantic import validate_call
+
+from pathlib import Path
+from numpy import ndarray
+from itertools import product
+from ambiance import Atmosphere
+from tixi3.tixi3wrapper import Tixi3
+from ceasiompy.utils.generalclasses import Point
+from typing import (
+ List,
+ Tuple,
+ TextIO,
+)
+
+from ceasiompy.utils.commonxpath import REF_XPATH
+from ceasiompy import (
+ log,
+ ceasiompy_cfg,
+)
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def get_points_ref(tixi: Tixi3) -> ndarray:
+ return np.array([
+ tixi.getDoubleElement(REF_XPATH + "/point/x"),
+ tixi.getDoubleElement(REF_XPATH + "/point/y"),
+ tixi.getDoubleElement(REF_XPATH + "/point/z"),
+ ])
+
+
+def write_control(
+ avl_file: TextIO,
+ control_type: str,
+ hinge_xsi: float,
+ axis: str,
+ control_bool: float
+) -> None:
+ """Helper function to write CONTROL section."""
+ avl_file.write("CONTROL\n")
+ avl_file.write(f"{control_type} {0.0} {hinge_xsi} {axis} {control_bool}\n\n")
+
+
+def split_dir(dir_name: str, index: int, param: str) -> float:
+ return float(dir_name.split("_")[index].split(param)[1])
+
+
+def split_line(line: str, index: int):
+ return float(line.split("=")[index].strip().split()[0])
+
+
+def get_atmospheric_cond(alt: float, mach: float) -> Tuple[float, float, float]:
+ Atm = Atmosphere(alt)
+ density = Atm.density[0]
+ g = Atm.grav_accel[0]
+ velocity = Atm.speed_of_sound[0] * mach
+
+ return density, g, velocity
+
+
+def create_case_dir(results_dir: Path, i_case: int, alt: float, **params: float) -> Path:
+ # Log the parameters
+ param_log = ", ".join(f"{key}: {value}" for key, value in params.items())
+ log.info(f"--- alt: {alt}, {param_log} ---")
+
+ # Create the case directory name dynamically
+ case_dir_name = f"Case{str(i_case).zfill(2)}_alt{alt}" + "".join(
+ f"_{key}{round(value, 2) if isinstance(value, float) else value}"
+ for key, value in params.items()
+ )
+
+ # Create the directory
+ case_dir_path = Path(results_dir, case_dir_name)
+ case_dir_path.mkdir(exist_ok=True)
+
+ return case_dir_path
+
+
+def convert_dist_to_avl_format(vortex_dist: str) -> int:
+ if vortex_dist == "cosine":
+ return 1
+ elif vortex_dist == "sine":
+ return 2
+ else:
+ return 3
+
+
+def to_cpacs_format(point: Point) -> str:
+ return str(point.x) + "\t" + str(point.y) + "\t" + str(point.z)
+
+
+@validate_call(config=ceasiompy_cfg)
+def duplicate_elements(*lists: List) -> Tuple[List, ...]:
+ """
+ Duplicates lists such that there is a unique combination of them
+ and the last three lists are zero-independent.
+
+ zero-independent: For a unique combination of elements, you will have,
+ (elem1, ..., elemn) (value, 0.0, 0.0)
+ (elem1, ..., elemn) (0.0, value, 0.0)
+ (elem1, ..., elemn) (0.0, 0.0, value)
+
+ Objective:
+ SDSA requires data of the specified type.
+
+ Returns:
+ (Tuple[List, ...]): Number of *lists + 2, where the 3 last lists are zero-independent.
+
+ """
+ if len(lists) < 2:
+ return tuple(lists)
+
+ *initial_lists, last_list = lists
+ combinations = list(product(*initial_lists))
+
+ n = len(initial_lists)
+ new_lists = [[] for _ in initial_lists] + [[] for _ in range(3)]
+
+ def append_combination(combination: List, values: List) -> None:
+ """Appends a combination with specific values."""
+ for i, entry in enumerate(combination):
+ new_lists[i].append(entry)
+ for j, value in enumerate(values):
+ new_lists[n + j].append(value)
+
+ for value in last_list:
+ if value == 0.0:
+ for combination in combinations:
+ append_combination(combination, [0.0, 0.0, 0.0])
+ else:
+ for combination in combinations:
+ append_combination(combination, [value, 0.0, 0.0])
+ for combination in combinations:
+ append_combination(combination, [0.0, value, 0.0])
+ for combination in combinations:
+ append_combination(combination, [0.0, 0.0, value])
+
+ return tuple(new_lists)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute.")
diff --git a/ceasiompy/PyAVL/pyavl.py b/ceasiompy/PyAVL/pyavl.py
new file mode 100644
index 000000000..c25b4d9a3
--- /dev/null
+++ b/ceasiompy/PyAVL/pyavl.py
@@ -0,0 +1,203 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Script to run AVL calculations in CEASIOMpy.
+AVL allows to perform aerodynamic analyses using
+the vortex-lattice method (VLM)
+
+
+| Author: Romain Gauthier
+| Creation: 2024-03-14
+| Modified: Leon Deligny
+| Date: 11-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from ceasiompy.PyAVL.func.plot import convert_ps_to_pdf
+from ceasiompy.PyAVL.func.results import get_avl_results
+from ceasiompy.PyAVL.func.utils import (
+ create_case_dir,
+ duplicate_elements,
+)
+from ceasiompy.PyAVL.func.config import (
+ write_command_file,
+ retrieve_gui_values,
+)
+from ceasiompy.utils.ceasiompyutils import (
+ call_main,
+ run_software,
+)
+
+from pathlib import Path
+from cpacspy.cpacspy import CPACS
+
+from ceasiompy.PyAVL import (
+ MODULE_NAME,
+ SOFTWARE_NAME,
+)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+def main(cpacs: CPACS, results_dir: Path) -> None:
+ """
+ Run AVL calculations on specified CPACS file.
+ 1. Load the necessary data.
+ 2. Run avl with p, q, r rate deflections.
+ 3. Run avl with control surfaces deflections.
+ """
+
+ # 1. Load the necessary data
+ tixi = cpacs.tixi
+
+ (
+ alt_list, mach_list, aoa_list, aos_list,
+ rotation_rate_list, control_surface_list,
+ avl_path,
+ save_fig,
+ nb_cpu,
+
+ ) = retrieve_gui_values(cpacs, results_dir)
+
+ #
+ # 2. p, q, r
+ (
+ new_alt_list,
+ new_mach_list,
+ new_aoa_list,
+ new_aos_list,
+ new_pitch_rate_list,
+ new_roll_rate_list,
+ new_yaw_rate_list
+ ) = duplicate_elements(
+ list(set(alt_list)),
+ list(set(mach_list)),
+ list(set(aoa_list)),
+ list(set(aos_list)),
+ list(set(rotation_rate_list)),
+ )
+ first_cases = len(new_alt_list)
+
+ for i_case, alt in enumerate(new_alt_list):
+ mach = new_mach_list[i_case]
+ aoa = new_aoa_list[i_case]
+ aos = new_aos_list[i_case]
+
+ q = new_pitch_rate_list[i_case]
+ p = new_roll_rate_list[i_case]
+ r = new_yaw_rate_list[i_case]
+
+ case_dir_path = create_case_dir(
+ results_dir,
+ i_case,
+ alt,
+ mach=mach,
+ aoa=aoa,
+ aos=aos,
+ q=q,
+ p=p,
+ r=r,
+ )
+
+ command_path = write_command_file(
+ tixi=tixi,
+ avl_path=avl_path,
+ case_dir_path=case_dir_path,
+ save_plots=save_fig,
+ alpha=aoa,
+ beta=aos,
+ pitch_rate=q,
+ roll_rate=p,
+ yaw_rate=r,
+ mach_number=mach,
+ alt=alt,
+ )
+
+ run_software(
+ software_name=SOFTWARE_NAME,
+ arguments=[""],
+ wkdir=case_dir_path,
+ with_mpi=False,
+ nb_cpu=nb_cpu,
+ stdin=open(str(command_path), "r"),
+ )
+
+ if save_fig:
+ convert_ps_to_pdf(case_dir_path)
+
+ if control_surface_list != [0.0]:
+
+ #
+ # 3. aileron, elevator, rudder
+ (
+ new_alt_list,
+ new_mach_list,
+ new_aoa_list,
+ new_aileron_list,
+ new_elevator_list,
+ new_rudder_list
+ ) = duplicate_elements(
+ list(set(alt_list)),
+ list(set(mach_list)),
+ list(set(aoa_list)),
+ list(set(control_surface_list)),
+ )
+
+ # Iterate through each case
+ for i_case, alt in enumerate(new_alt_list):
+ mach = new_mach_list[i_case]
+ aoa = new_aoa_list[i_case]
+
+ aileron = new_aileron_list[i_case]
+ elevator = new_elevator_list[i_case]
+ rudder = new_rudder_list[i_case]
+
+ case_dir_path = create_case_dir(
+ results_dir,
+ i_case + first_cases,
+ alt,
+ mach=mach,
+ aoa=aoa,
+ aileron=aileron,
+ elevator=elevator,
+ rudder=rudder,
+ )
+
+ command_path = write_command_file(
+ tixi=tixi,
+ avl_path=avl_path,
+ case_dir_path=case_dir_path,
+ save_plots=save_fig,
+ alpha=aoa,
+ mach_number=mach,
+ alt=alt,
+ aileron=aileron,
+ rudder=rudder,
+ elevator=elevator,
+ )
+
+ run_software(
+ software_name=SOFTWARE_NAME,
+ arguments=[""],
+ wkdir=case_dir_path,
+ with_mpi=False,
+ nb_cpu=nb_cpu,
+ stdin=open(str(command_path), "r"),
+ )
+
+ if save_fig:
+ convert_ps_to_pdf(case_dir_path)
+
+ get_avl_results(cpacs, results_dir)
+
+
+if __name__ == "__main__":
+ call_main(main, MODULE_NAME)
diff --git a/ceasiompy/PyAVL/tests/aircraft.avl b/ceasiompy/PyAVL/tests/aircraft.avl
new file mode 100644
index 000000000..c0471ec6c
--- /dev/null
+++ b/ceasiompy/PyAVL/tests/aircraft.avl
@@ -0,0 +1,162 @@
+D150
+
+#Mach
+0.3
+
+#IYsym IZsym Zsym
+0 0 0
+
+#Sref Cref Bref
+122.400 4.193 29.192
+
+#Xref Yref Zref
+0.000 0.000 0.000
+
+#--------------------------------------------------
+SURFACE
+Wing
+
+!Nchordwise Cspace Nspanwise Sspace
+20.0 1.0 50.0 1.0
+
+COMPONENT
+1
+
+YDUPLICATE
+0
+
+ANGLE
+0
+
+SCALE
+1.0 1.0 1.0
+
+TRANSLATE
+12.746 0.0 -1.136
+
+#---------------
+SECTION
+#Xle Yle Zle Chord Ainc
+0.000 0.000 0.000 6.076 2.0
+CONTROL
+flap 0.0 0.812922 -0.56760591384 0.0 0.0 1.0
+
+AFILE
+/users/disk12/cfse2/Leon/CEASIOMpy_Leon/CEASIOMpy/WKDIR/Workflow_105/Results/PyAVL/D150_VAMP_W_SupCritProf1.dat
+
+#---------------
+SECTION
+#Xle Yle Zle Chord Ainc
+0.000 1.856 0.000 6.076 2.0
+CONTROL
+flap 0.0 0.7195 -0.56760591384 0.0 0.0 1.0
+
+CONTROL
+flap 0.0 0.7195 -0.08731417212000013 0.0 0.0 1.0
+
+AFILE
+/users/disk12/cfse2/Leon/CEASIOMpy_Leon/CEASIOMpy/WKDIR/Workflow_105/Results/PyAVL/D150_VAMP_W_SupCritProf1.dat
+
+#---------------
+SECTION
+#Xle Yle Zle Chord Ainc
+2.335 6.333 0.310 3.758 2.0
+CONTROL
+flap 0.0 0.705129 -0.05401254124000008 0.0 0.0 1.0
+
+CONTROL
+aileron 0.0 0.702209 0.03679888603999998 0.0 0.0 -1.0
+
+AFILE
+/users/disk12/cfse2/Leon/CEASIOMpy_Leon/CEASIOMpy/WKDIR/Workflow_105/Results/PyAVL/D150_VAMP_W_SupCritProf1.dat
+
+#---------------
+SECTION
+#Xle Yle Zle Chord Ainc
+7.875 16.956 1.047 1.496 2.0
+CONTROL
+aileron 0.0 0.712 0.014645769439999992 0.0 0.0 -1.0
+
+AFILE
+/users/disk12/cfse2/Leon/CEASIOMpy_Leon/CEASIOMpy/WKDIR/Workflow_105/Results/PyAVL/D150_VAMP_W_SupCritProf1.dat
+
+#--------------------------------------------------
+SURFACE
+Wing
+
+!Nchordwise Cspace Nspanwise Sspace
+20.0 1.0 50.0 1.0
+
+COMPONENT
+1
+
+YDUPLICATE
+0
+
+ANGLE
+0
+
+SCALE
+1.0 1.0 1.0
+
+TRANSLATE
+31.466 0.0 0.697
+
+#---------------
+SECTION
+#Xle Yle Zle Chord Ainc
+0.000 0.000 0.000 3.744 -0.0
+CONTROL
+elevator 0.0 0.577506 0.2339981590200002 0.0 0.0 1.0
+
+AFILE
+/users/disk12/cfse2/Leon/CEASIOMpy_Leon/CEASIOMpy/WKDIR/Workflow_105/Results/PyAVL/D150_VAMP_HL_NACA0000Prof1.dat
+
+#---------------
+SECTION
+#Xle Yle Zle Chord Ainc
+3.937 6.225 0.545 1.236 -0.0
+CONTROL
+elevator 0.0 0.64 0.07721946122000006 0.0 0.0 1.0
+
+AFILE
+/users/disk12/cfse2/Leon/CEASIOMpy_Leon/CEASIOMpy/WKDIR/Workflow_105/Results/PyAVL/D150_VAMP_HL_NACA0000Prof1.dat
+
+#--------------------------------------------------
+SURFACE
+Wing
+
+!Nchordwise Cspace Nspanwise Sspace
+20.0 1.0 50.0 1.0
+
+COMPONENT
+1
+
+ANGLE
+0
+
+SCALE
+1.0 1.0 1.0
+
+TRANSLATE
+29.838 -0.002 1.891
+
+#---------------
+SECTION
+#Xle Yle Zle Chord Ainc
+0.000 0.000 0.000 5.435 0.0
+CONTROL
+rudder 0.0 0.6875 0.14402007999999983 0.0 0.0 -1.0
+
+AFILE
+/users/disk12/cfse2/Leon/CEASIOMpy_Leon/CEASIOMpy/WKDIR/Workflow_105/Results/PyAVL/D150_VAMP_SL_NACA0000Prof1.dat
+
+#---------------
+SECTION
+#Xle Yle Zle Chord Ainc
+4.988 0.192 5.865 1.897 0.0
+CONTROL
+rudder 0.0 0.714 0.05026307999999994 0.0 0.0 -1.0
+
+AFILE
+/users/disk12/cfse2/Leon/CEASIOMpy_Leon/CEASIOMpy/WKDIR/Workflow_105/Results/PyAVL/D150_VAMP_SL_NACA0000Prof1.dat
diff --git a/ceasiompy/PyAVL/tests/fs_template.txt b/ceasiompy/PyAVL/tests/fs_template.txt
index 25bc3c4ec..04e4713b0 100644
--- a/ceasiompy/PyAVL/tests/fs_template.txt
+++ b/ceasiompy/PyAVL/tests/fs_template.txt
@@ -1,21 +1,21 @@
---------------------------------------------------------------
Surface and Strip Forces by surface
- Sref = 0.27000E-01 Cref = 0.70000E-01 Bref = 0.39200
- Xref = 0.19200 Yref = 0.0000 Zref = 0.0000
+ Sref = 0.27000E-01 Cref = 0.70000E-01 Bref = 0.39200
+ Xref = 0.19200 Yref = 0.0000 Zref = 0.0000
- Surface # 1 Wing
+ Surface # 1 Wing
# Chordwise = 5 # Spanwise = 20 First strip = 1
Surface area Ssurf = 0.013703 Ave. chord Cave = 0.067500
Forces referred to Sref, Cref, Bref about Xref, Yref, Zref
- Standard axis orientation, X fwd, Z down
+ Standard axis orientation, X fwd, Z down
CLsurf = 0.17532 Clsurf = -0.04113
CYsurf = 0.00714 Cmsurf = -0.00681
CDsurf = 0.00312 Cnsurf = -0.00444
CDisurf = 0.00312 CDvsurf = 0.00000
- Forces referred to Ssurf, Cave
+ Forces referred to Ssurf, Cave
CLsurf = 0.34545 CDsurf = 0.00614
Strip Forces referred to Strip Area, Chord
@@ -41,18 +41,18 @@
19 0.2546 0.1878 0.0000 0.0484 0.0005 0.0150 0.0659 0.4877 0.3113 -0.0013 0.0000 0.0162 -0.0475 0.198
20 0.2629 0.1979 0.0000 0.0461 0.0005 0.0106 0.0727 0.3610 0.2304 -0.0027 0.0000 0.0203 -0.0288 0.162
- Surface # 2 Wing (YDUP)
+ Surface # 2 Wing (YDUP)
# Chordwise = 5 # Spanwise = 20 First strip = 21
Surface area Ssurf = 0.013703 Ave. chord Cave = 0.067500
Forces referred to Sref, Cref, Bref about Xref, Yref, Zref
- Standard axis orientation, X fwd, Z down
+ Standard axis orientation, X fwd, Z down
CLsurf = 0.17532 Clsurf = 0.04113
CYsurf = -0.00714 Cmsurf = -0.00681
CDsurf = 0.00312 Cnsurf = 0.00444
CDisurf = 0.00312 CDvsurf = 0.00000
- Forces referred to Ssurf, Cave
+ Forces referred to Ssurf, Cave
CLsurf = 0.34545 CDsurf = 0.00614
Strip Forces referred to Strip Area, Chord
diff --git a/ceasiompy/PyAVL/tests/ft_template.txt b/ceasiompy/PyAVL/tests/ft_template.txt
index 1b2939b90..6d063dc0e 100644
--- a/ceasiompy/PyAVL/tests/ft_template.txt
+++ b/ceasiompy/PyAVL/tests/ft_template.txt
@@ -1,17 +1,17 @@
---------------------------------------------------------------
Vortex Lattice Output -- Total Forces
- Configuration: labARscaled
+ Configuration: labARscaled
# Surfaces = 2
# Strips = 40
# Vortices = 200
- Sref = 0.27000E-01 Cref = 0.70000E-01 Bref = 0.39200
- Xref = 0.19200 Yref = 0.0000 Zref = 0.0000
+ Sref = 0.27000E-01 Cref = 0.70000E-01 Bref = 0.39200
+ Xref = 0.19200 Yref = 0.0000 Zref = 0.0000
- Standard axis orientation, X fwd, Z down
+ Standard axis orientation, X fwd, Z down
- Run case: -unnamed-
+ Run case: -unnamed-
Alpha = 5.00000 pb/2V = -0.00000 p'b/2V = -0.00000
Beta = 0.00000 qc/2V = 0.00000
@@ -25,7 +25,7 @@
CDtot = 0.00624
CDvis = 0.00000 CDind = 0.0062360
CLff = 0.35118 CDff = 0.0064539 | Trefftz
- CYff = 0.00000 e = 1.0688 | Plane
+ CYff = 0.00000 e = 1.0688 | Plane
---------------------------------------------------------------
\ No newline at end of file
diff --git a/ceasiompy/PyAVL/tests/st_template.txt b/ceasiompy/PyAVL/tests/st_template.txt
new file mode 100644
index 000000000..ab1c0bb8c
--- /dev/null
+++ b/ceasiompy/PyAVL/tests/st_template.txt
@@ -0,0 +1,69 @@
+ ---------------------------------------------------------------
+ Vortex Lattice Output -- Total Forces
+
+ Configuration: D150
+ # Surfaces = 5
+ # Strips = 250
+ # Vortices =5000
+
+ Sref = 122.40 Cref = 4.1930 Bref = 29.192
+ Xref = 0.0000 Yref = 0.0000 Zref = 0.0000
+
+ Standard axis orientation, X fwd, Z down
+
+ Run case: -unnamed-
+
+ Alpha = 0.00000 pb/2V = -0.00000 p'b/2V = -0.00000
+ Beta = 0.00000 qc/2V = 0.00000
+ Mach = 0.300 rb/2V = -0.00000 r'b/2V = -0.00000
+
+ CXtot = -0.00119 Cltot = 0.00001 Cl'tot = 0.00001
+ CYtot = 0.00008 Cmtot = -0.66525
+ CZtot = -0.18118 Cntot = -0.00009 Cn'tot = -0.00009
+
+ CLtot = 0.18118
+ CDtot = 0.00119
+ CDvis = 0.00000 CDind = 0.0011880
+ CLff = 0.18107 CDff = 0.0011865 | Trefftz
+ CYff = 0.00008 e = 1.2634 | Plane
+
+ flap = 5.00000
+ aileron = 5.00000
+ elevator = 0.00000
+ rudder = 0.00000
+
+ ---------------------------------------------------------------
+
+ Stability-axis derivativeMODULE_DIRs...
+
+ alpha beta
+ ---------------- ----------------
+ z' force CL | CLa = 5.504637 CLb = 0.010030
+ y force CY | CYa = -0.008115 CYb = -0.371049
+ x' mom. Cl'| Cla = -0.001342 Clb = -0.156791
+ y mom. Cm | Cma = -24.343220 Cmb = -0.077647
+ z' mom. Cn'| Cna = 0.009207 Cnb = 0.410176
+
+ roll rate p' pitch rate q' yaw rate r'
+ ---------------- ---------------- ----------------
+ z' force CL | CLp = 0.001886 CLq = 57.244704 CLr = -0.024502
+ y force CY | CYp = -0.136885 CYq = -0.153675 CYr = 0.897168
+ x' mom. Cl'| Clp = -0.629023 Clq = -0.023587 Clr = 0.325843
+ y mom. Cm | Cmp = -0.014871 Cmq =-285.068674 Cmr = 0.190199
+ z' mom. Cn'| Cnp = 0.099537 Cnq = 0.174763 Cnr = -1.001663
+
+ flap d01 aileron d02 elevator d03 rudder d04
+ ---------------- ---------------- ---------------- ----------------
+ z' force CL | CLd01 = 0.000000 CLd02 = 0.000000 CLd03 = 0.000000 CLd04 = 0.000000
+ y force CY | CYd01 = 0.000000 CYd02 = 0.000000 CYd03 = 0.000000 CYd04 = 0.000000
+ x' mom. Cl'| Cld01 = -0.000000 Cld02 = -0.000000 Cld03 = -0.000000 Cld04 = -0.000000
+ y mom. Cm | Cmd01 = 0.000000 Cmd02 = 0.000000 Cmd03 = 0.000000 Cmd04 = 0.000000
+ z' mom. Cn'| Cnd01 = -0.000000 Cnd02 = -0.000000 Cnd03 = -0.000000 Cnd04 = -0.000000
+ Trefftz drag| CDffd01 = 0.000000 CDffd02 = 0.000000 CDffd03 = 0.000000 CDffd04 = 0.000000
+ span eff. | ed01 = 0.000000 ed02 = 0.000000 ed03 = 0.000000 ed04 = 0.000000
+
+
+
+ Neutral point Xnp = 18.542752
+
+ Clb Cnr / Clr Cnb = 1.175071 ( > 1 if spirally stable )
\ No newline at end of file
diff --git a/ceasiompy/PyAVL/tests/test_avlconfig.py b/ceasiompy/PyAVL/tests/test_avlconfig.py
index acf31e749..0c1d0f82c 100644
--- a/ceasiompy/PyAVL/tests/test_avlconfig.py
+++ b/ceasiompy/PyAVL/tests/test_avlconfig.py
@@ -5,10 +5,11 @@
Test functions of 'ceasiompy/PyAVL/func/avlconfig.py'
-Python version: >=3.8
| Author : Romain Gauthier
| Creation: 2024-06-06
+| Modified: Leon Deligny
+| Date: 21 March 2025
"""
@@ -16,82 +17,80 @@
# IMPORTS
# =================================================================================================
+import unittest
+
from pathlib import Path
-from ceasiompy.PyAVL.func.avlconfig import (
+from ceasiompy.utils.decorators import log_test
+from ceasiompy.PyAVL.func.config import write_command_file
+
+from ceasiompy.utils.ceasiompyutils import (
+ current_workflow_dir,
get_aeromap_conditions,
- get_option_settings,
- write_command_file,
)
-from ceasiompy.utils.commonpaths import CPACS_FILES_PATH
-CPACS_IN_PATH = Path(CPACS_FILES_PATH, "labARscaled.xml")
+from cpacspy.cpacspy import CPACS
+from ceasiompy.utils.ceasiompytest import CeasiompyTest
-MODULE_DIR = Path(__file__).parent
-CASE_DIR = Path(MODULE_DIR, "AVLpytest")
-COMMAND_TEM_DIR = Path(MODULE_DIR, "avl_command_template.txt")
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
+from ceasiompy.PyAVL import MODULE_DIR, AVL_AEROMAP_UID_XPATH
+from ceasiompy.utils.commonpaths import CPACS_FILES_PATH
# =================================================================================================
-# FUNCTIONS
+# CLASSES
# =================================================================================================
-def test_write_command_file(tmp_path):
- test_path = Path("/path", "to", "avl", "input", "file", "aircraft.avl")
-
- write_command_file(
- avl_path=test_path,
- case_dir_path=tmp_path,
- alpha=5.0,
- beta=0.0,
- mach_number=0.3,
- ref_velocity=100.93037463067732,
- ref_density=1.1116596736996904,
- g_acceleration=9.803565306802405,
- save_plots=True,
- )
-
- file_exists = Path(tmp_path, "avl_commands.txt").exists()
- assert file_exists, "File 'avl_commands.txt' not found."
-
- if file_exists:
- COMMAND_DIR = Path(tmp_path, "avl_commands.txt")
- with open(COMMAND_TEM_DIR, "r") as file1, open(COMMAND_DIR, "r") as file2:
- for line1, line2 in zip(file1, file2):
- if "mass" not in line1:
- assert line1 == line2, "File 'avl_commands.txt' not correct."
-
- # Check for any remaining lines in either file
- assert not file1.read() or not file2.read(), "File 'avl_commands.txt' not correct."
-
-
-def test_get_aeromap_conditions():
- alt_list, mach_list, aoa_list, aos_list = get_aeromap_conditions(CPACS_IN_PATH)
- assert alt_list[0] == 1000.0, "Altitude from aeromap not correct, should be 1000.0 meters."
- assert mach_list[0] == 0.3, "Mach number from aeromap not correct, should be 0.3."
- assert aoa_list[0] == 5.0, "Aoa from aeromap not correct, should be 5.0 degrees."
- assert aos_list[0] == 0.0, "Altitude from aeromap not correct, should be 0.0 degrees"
-
-
-def test_get_option_settings():
- (
- save_plots,
- vortex_distribution,
- Nchordwise,
- Nspanwise,
- integrate_fuselage,
- ) = get_option_settings(CPACS_IN_PATH)
-
- assert not save_plots, "Option 'save_plots' should be 'False'."
- assert vortex_distribution == 3.0, "Option 'vortex_distribution' should be '3.0'."
- assert Nchordwise == 5, "Option 'Nchordwise' should be '5'."
- assert Nspanwise == 20, "Option 'Nspanwise' should be '20'."
- assert not integrate_fuselage, "Option 'integrate_fuselage' should be 'False'."
+class TestModuleTemplate(CeasiompyTest):
+
+ @classmethod
+ def setUpClass(cls):
+ cpacs_path = Path(CPACS_FILES_PATH, "labARscaled.xml")
+ cls.cpacs = CPACS(cpacs_path)
+ cls.wkdir = current_workflow_dir()
+ cls.command_dir = Path(MODULE_DIR, "tests", "avl_command_template.txt")
+ cls.avl_path = Path(MODULE_DIR, "tests", "aircraft.avl")
+
+ @log_test
+ def test_get_aeromap_conditions(self) -> None:
+ self.assert_equal_function(
+ f=get_aeromap_conditions,
+ input_args=(self.cpacs, AVL_AEROMAP_UID_XPATH),
+ expected=([1000.0], [0.3], [5.0], [0.0]),
+ )
+
+ @log_test
+ def test_write_command_file(self):
+
+ write_command_file(
+ tixi=self.cpacs.tixi,
+ avl_path=self.avl_path,
+ case_dir_path=self.wkdir,
+ alpha=5.0,
+ beta=0.0,
+ pitch_rate=0.0,
+ roll_rate=0.0,
+ yaw_rate=0.0,
+ mach_number=0.3,
+ alt=1000.0,
+ aileron=0.0,
+ elevator=0.0,
+ rudder=0.0,
+ save_plots=True,
+ )
+
+ file_exists = Path(self.wkdir, "avl_commands.txt").exists()
+ assert file_exists, "File 'avl_commands.txt' not found."
+
+ if file_exists:
+ self.command_dir = Path(self.wkdir, "avl_commands.txt")
+ with open(self.command_dir, "r") as file1, open(self.command_dir, "r") as file2:
+ for line1, line2 in zip(file1, file2):
+ if "mass" not in line1:
+ assert line1 == line2, "File 'avl_commands.txt' not correct."
+
+ # Check for any remaining lines in either file
+ assert not file1.read() or not file2.read(), "File 'avl_commands.txt' not correct."
# =================================================================================================
@@ -100,6 +99,4 @@ def test_get_option_settings():
if __name__ == "__main__":
- print("Test avlconfig.py")
- print("To run test use the following command:")
- print(">> pytest -v")
+ unittest.main(verbosity=0)
diff --git a/ceasiompy/PyAVL/tests/test_avlresults.py b/ceasiompy/PyAVL/tests/test_avlresults.py
index 4b1909b80..3352ebbb6 100644
--- a/ceasiompy/PyAVL/tests/test_avlresults.py
+++ b/ceasiompy/PyAVL/tests/test_avlresults.py
@@ -5,65 +5,78 @@
Test functions of 'ceasiompy/PyAVL/func/avlresults.py'
-Python version: >=3.8
| Author : Romain Gauthier
| Creation: 2024-06-07
+| Modified: Leon Deligny
+| Date: 21 March 2025
"""
# =================================================================================================
# IMPORTS
# =================================================================================================
-from pathlib import Path
-import pytest
-from ceasiompy.PyAVL.func.avlresults import get_avl_aerocoefs, plot_lift_distribution
-
-from ceasiompy.utils.commonpaths import CPACS_FILES_PATH
-MODULE_DIR = Path(__file__).parent
+import unittest
-CPACS_IN_PATH = Path(CPACS_FILES_PATH, "labARscaled.xml")
-FT_TEMPLATE = Path(MODULE_DIR, "ft_template.txt")
+from ceasiompy.utils.decorators import log_test
+from ceasiompy.PyAVL.func.results import get_avl_aerocoefs
+from ceasiompy.PyAVL.func.plot import plot_lift_distribution
+from ceasiompy.utils.ceasiompyutils import current_workflow_dir
-# =================================================================================================
-# CLASSES
-# =================================================================================================
+from pathlib import Path
+from ceasiompy.utils.ceasiompytest import CeasiompyTest
+from ceasiompy.PyAVL import MODULE_DIR
# =================================================================================================
-# FUNCTIONS
+# CLASSES
# =================================================================================================
-def test_plot_lift_distribution(tmp_path):
- plot_lift_distribution(
- force_file_fs=Path(MODULE_DIR, "fs_template.txt"),
- aoa=5,
- aos=0,
- mach=0.3,
- alt=1000,
- wkdir=tmp_path,
- )
-
- assert Path(
- tmp_path, "lift_distribution.png"
- ).exists(), "Plot 'lift_distribution.png' does not exist."
-
-
-def test_get_avl_aerocoefs():
- assert FT_TEMPLATE.exists(), "Result file 'ft.txt' not found!"
- cl, cd, cm = get_avl_aerocoefs(FT_TEMPLATE)
- assert cl == pytest.approx(0.35063, rel=1e-4), "CLtot is not correct!"
- assert cd == pytest.approx(0.00624, rel=1e-4), "CDtot is not correct!"
- assert cm == pytest.approx(-0.01362, rel=1e-4), "Cmtot is not correct!"
-
+class TestAvlResults(CeasiompyTest):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.force_file_fs = Path(MODULE_DIR, "tests", "fs_template.txt")
+ cls.wkdir = current_workflow_dir()
+ cls.ft_template = Path(MODULE_DIR, "tests", "st_template.txt")
+
+ @log_test
+ def test_template(self) -> None:
+ assert self.ft_template.exists(), "Result file 'ft.txt' not found!"
+
+ @log_test
+ def test_get_avl_aerocoefs(self) -> None:
+ self.assert_equal_function(
+ f=get_avl_aerocoefs,
+ input_args=(self.ft_template,),
+ expected=(
+ 0.00119, 0.00008, 0.18118,
+ 0.00001, -0.66525, -0.00009,
+ -0.156791, -24.343220, 0.410176,
+ ),
+ )
+
+ @log_test
+ def test_plot_lift_distribution(self):
+ plot_lift_distribution(
+ force_file_fs=self.force_file_fs,
+ aoa=5,
+ aos=0,
+ mach=0.3,
+ alt=1000,
+ wkdir=self.wkdir,
+ )
+
+ assert Path(
+ self.wkdir, "lift_distribution.png"
+ ).exists(), "Plot 'lift_distribution.png' does not exist."
# =================================================================================================
# MAIN
# =================================================================================================
+
if __name__ == "__main__":
- print("Test avlconfig.py")
- print("To run test use the following command:")
- print(">> pytest -v")
+ unittest.main(verbosity=0)
diff --git a/ceasiompy/PyTornado/.gitignore b/ceasiompy/PyTornado/.gitignore
deleted file mode 100644
index 28ac87668..000000000
--- a/ceasiompy/PyTornado/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-# Ignore PyTornado working dir
-wkdir*
-
-log*
diff --git a/ceasiompy/PyTornado/README.md b/ceasiompy/PyTornado/README.md
deleted file mode 100644
index 525b11b5e..000000000
--- a/ceasiompy/PyTornado/README.md
+++ /dev/null
@@ -1,77 +0,0 @@
-
-
-# PyTornado
-
-**Categories:** Aerodynamics, CFD, aeromaps
-
-**State**: :heavy_check_mark:
-
-
-
-`PyTornado` module is a launcher for the software [PyTornado](https://github.com/airinnova/pytornado) which has been developed by [Airinnova](https://airinnova.se/) based on the Matlab version of [Tornado](https://tornado.redhammer.se/index.php). It is a Vortex Lattice Method (VLM) solver for low-fidelity aerodynamic analysis of wings.
-
-
-
-## Inputs
-
-`PyTornado` takes as input a CPACS file, the aircraft geometry will be use to create the mesh required by the VLM solver. To define the flight conditions an aeromap on which the calculation will be done is also required.
-
-## Analyses
-
-`PyTornado` calculates aerodynamic coefficients of an aircraft for a given aeromap and write back results in a CPACS file.
-
-Optionally, `PyTornado` can also plot some results.
-
-
-
-
-
-Example of PyTornado results.
-
-
-## Outputs
-
-`PyTornado` outputs a CPACS files with the calculated aerodynamic coefficients added into the aeromap.
-
-## Installation or requirements
-
-`PyTornado` can be installed by first cloning the [PyTornado repository](https://github.com/airinnova/pytornado.git) and then use pip to install it. The command should be the following:
-
-```bash
-git clone https://github.com/airinnova/pytornado.git
-cd pytornado
-pip install -e .
-```
-
-If you follow an automatic installation procedure on the [CEASIOMpy installation page](../../installation/INSTALLATION.md), PyTornado should be installed automatically with the other tools.
-
-## Limitations
-
-`PyTornado` is a Vortex Lattice Method (VLM) solver and the assumption are the following:
-
-* The flow field is incompressible (Ma < 0.5), inviscid and irrotational.
-
-* The lifting surfaces are thin. The influence of the thickness on aerodynamic forces are neglected.
-* The angle of attack and the angle of sideslip are both small (small angle approximation)
-
-:warning: No fuselage is modelled by `PyTornado` so the drag coefficient is not correct, generally underestimated.
-
-## More information
-
-* [PyTornado Github repository](ttps://github.com/airinnova/pytornado)
-
-* [PyTornado Documentation](https://pytornado.readthedocs.io/en/latest/)
-
-* [Original Matlab version of Tornado](https://tornado.redhammer.se/index.php)
-
-* [Flight Vehicle Aerodynamics](https://mitpress.mit.edu/books/flight-vehicle-aerodynamics) by Mark Drela
-
-*
-
-## References
-
-[1] Melin, T.: A Vortex Lattice MATLAB Implementation for Linear Aerodynamic Wing Applications, Diss., December 2000.
-
-[2] Drela, M.: Flight Vehicle Aerodynamics. Cambridge, Massachusetts: MIT Press, 2014. – ISBN 978-0-262-52644-9
-
-[3] Dettmann, A.: Loosely coupled, modular framework for linear static aeroelastic analyses. Master Thesis (2019). KTH Royal Institute of Technology.
diff --git a/ceasiompy/PyTornado/ToolInput/.keep b/ceasiompy/PyTornado/ToolInput/.keep
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/ceasiompy/PyTornado/ToolInput/.keep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ceasiompy/PyTornado/ToolOutput/.keep b/ceasiompy/PyTornado/ToolOutput/.keep
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/ceasiompy/PyTornado/ToolOutput/.keep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ceasiompy/PyTornado/__init__.py b/ceasiompy/PyTornado/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/ceasiompy/PyTornado/__specs__.py b/ceasiompy/PyTornado/__specs__.py
deleted file mode 100644
index adc04f918..000000000
--- a/ceasiompy/PyTornado/__specs__.py
+++ /dev/null
@@ -1,220 +0,0 @@
-from pathlib import Path
-
-from ceasiompy.utils.moduleinterfaces import CPACSInOut
-from ceasiompy.utils.commonxpath import (
- REF_XPATH,
- WINGS_XPATH,
- PYTORNADO_XPATH,
- AEROPERFORMANCE_XPATH,
-)
-
-# ===== Module Status =====
-# True if the module is active
-# False if the module is disabled (not working or not ready)
-module_status = True
-
-# ===== Results directory path =====
-
-RESULTS_DIR = Path("Results", "PyTornado")
-
-
-# ===== CPACS inputs and outputs =====
-
-cpacs_inout = CPACSInOut()
-
-# ===== Input =====
-
-cpacs_inout.add_input(
- var_name="",
- var_type=list,
- default_value=None,
- unit=None,
- descr="Name of the aero map to evaluate",
- xpath=PYTORNADO_XPATH + "/aeroMapUID",
- gui=True,
- gui_name="__AEROMAP_SELECTION",
- gui_group="Aeromap settings",
-)
-
-cpacs_inout.add_input(
- var_name="delete_old_wkdirs",
- var_type=bool,
- default_value=False,
- unit=None,
- descr="Delete old PyTornado working directories (if existent)",
- xpath=PYTORNADO_XPATH + "/deleteOldWKDIRs",
- gui=False,
- gui_name="Delete",
- gui_group="Delete old working directories",
-)
-
-
-# ----- Discretisation -----
-# TO BE IMPROVED IN NEW PYTORNADO VERSION
-cpacs_inout.add_input(
- var_name="",
- var_type=int,
- default_value=20,
- unit=None,
- descr="The number of chordwise VLM panels",
- xpath=PYTORNADO_XPATH + "/vlm_autopanels_c",
- gui=True,
- gui_name="Number of chordwise panels",
- gui_group="Dicretisation",
-)
-
-cpacs_inout.add_input(
- var_name="",
- var_type=int,
- default_value=5,
- unit=None,
- descr="The number of spanwise VLM panels",
- xpath=PYTORNADO_XPATH + "/vlm_autopanels_s",
- gui=True,
- gui_name="Number of spanwise panels",
- gui_group="Dicretisation",
-)
-
-cpacs_inout.add_input(
- var_name="",
- var_type=bool,
- default_value=True,
- unit=None,
- descr="Save plot of the aircraft geometry",
- xpath=PYTORNADO_XPATH + "/plot/geometry/save",
- gui=True,
- gui_name="Save geometry plot",
- gui_group="Plots",
-)
-
-cpacs_inout.add_input(
- var_name="",
- var_type=bool,
- default_value=True,
- unit=None,
- descr="Save plot of the results (pressure coefficient)",
- xpath=PYTORNADO_XPATH + "/plot/results/save",
- gui=True,
- gui_name="Save results plot",
- gui_group="Plots",
-)
-
-cpacs_inout.add_input(
- var_name="",
- var_type=bool,
- default_value=False,
- unit=None,
- descr="Save plot of the lattices",
- xpath=PYTORNADO_XPATH + "/plot/lattice/save",
- gui=True,
- gui_name="Save lattices plot",
- gui_group="Plots",
-)
-
-cpacs_inout.add_input(
- var_name="",
- var_type=bool,
- default_value=False,
- unit=None,
- descr="Save the downwash matrix plot",
- xpath=PYTORNADO_XPATH + "/plot/matrix_downwash/save",
- gui=True,
- gui_name="Save matrix_downwash plot",
- gui_group="Plots",
-)
-
-cpacs_inout.add_input(
- var_name="",
- var_type=bool,
- default_value=False,
- unit=None,
- descr="Save PyTornado global results as a json file",
- xpath=PYTORNADO_XPATH + "/save_results/global",
- gui=True,
- gui_name="Save global results",
- gui_group="Save CPACS external results",
-)
-
-cpacs_inout.add_input(
- var_name="",
- var_type=bool,
- default_value=False,
- unit=None,
- descr="Save PyTornado panelwise results as a dat file",
- xpath=PYTORNADO_XPATH + "/save_results/panelwise",
- gui=True,
- gui_name="Save panelwise results",
- gui_group="Save CPACS external results",
-)
-
-cpacs_inout.add_input(
- var_name="check_extract_loads",
- var_type=bool,
- default_value=False,
- unit="1",
- descr="Option to extract loads from results (only last calculated case)",
- xpath=PYTORNADO_XPATH + "/save_results/extractLoads",
- gui=True,
- gui_name="Extract loads",
- gui_group="Save CPACS external results",
-)
-
-cpacs_inout.add_input(
- var_name="x_CG",
- default_value=None,
- unit="m",
- descr="Centre of gravity (x-coordinate)",
- xpath=REF_XPATH + "/point/x",
-)
-
-cpacs_inout.add_input(
- var_name="y_CG",
- default_value=None,
- unit="m",
- descr="Centre of gravity (y-coordinate)",
- xpath=REF_XPATH + "/point/y",
-)
-
-cpacs_inout.add_input(
- var_name="z_CG",
- default_value=None,
- unit="m",
- descr="Centre of gravity (z-coordinate)",
- xpath=REF_XPATH + "/point/z",
-)
-
-cpacs_inout.add_input(
- var_name="area",
- default_value=None,
- unit="m^2",
- descr="Reference area for force and moment coefficients",
- xpath=REF_XPATH + "/area",
-)
-
-cpacs_inout.add_input(
- var_name="length",
- default_value=None,
- unit="m",
- descr="Reference length for force and moment coefficients",
- xpath=REF_XPATH + "/length",
-)
-
-cpacs_inout.add_input(
- var_name="wing",
- default_value=None,
- unit="-",
- descr="Aircraft lifting surface",
- xpath=WINGS_XPATH,
-)
-
-
-# ----- Output -----
-
-cpacs_inout.add_output(
- var_name="aeromap_PyTornado", # name to change...
- # var_type=CPACS_aeroMap, # no type for output, would it be useful?
- default_value=None,
- unit="-",
- descr="aeroMap with aero coefficients calculated by PyTornado",
- xpath=AEROPERFORMANCE_XPATH + "/aeroMap/aeroPerformanceMap",
-)
diff --git a/ceasiompy/PyTornado/doc/paraview.pvsm b/ceasiompy/PyTornado/doc/paraview.pvsm
deleted file mode 100644
index ab926cd56..000000000
--- a/ceasiompy/PyTornado/doc/paraview.pvsm
+++ /dev/null
@@ -1,12301 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/ceasiompy/PyTornado/files/pytornado_example.png b/ceasiompy/PyTornado/files/pytornado_example.png
deleted file mode 100644
index 3698e97c2..000000000
Binary files a/ceasiompy/PyTornado/files/pytornado_example.png and /dev/null differ
diff --git a/ceasiompy/PyTornado/files/pytornado_logo.svg b/ceasiompy/PyTornado/files/pytornado_logo.svg
deleted file mode 100644
index 9b7ec352a..000000000
--- a/ceasiompy/PyTornado/files/pytornado_logo.svg
+++ /dev/null
@@ -1,492 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- image/svg+xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- PyTornado
- PyTornado
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/ceasiompy/PyTornado/runpytornado.py b/ceasiompy/PyTornado/runpytornado.py
deleted file mode 100644
index 4a2e6a999..000000000
--- a/ceasiompy/PyTornado/runpytornado.py
+++ /dev/null
@@ -1,363 +0,0 @@
-"""
-CEASIOMpy: Conceptual Aircraft Design Software
-
-Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-
-This is a wrapper module for PyTornado. PyTornado allows to perform aerodynamic
-analyses using the vortex-lattice method (VLM). Note that PyTornado is being
-developed in a separate repository on Github. For installation guides and
-general documentation refer to:
-
-* https://github.com/airinnova/pytornado
-
-Please report any issues with PyTornado or this wrapper here:
-
-* https://github.com/airinnova/pytornado/issues
-
-PyTornado supports:
-
-* AeroperformanceMap analyses
-
-Python version: >=3.8
-
-| Author: Aaron Dettmann
-| Creation: 2019-08-12
-
-
-TODO:
-
- * Dict parser --> Cast list/tuple
-
-"""
-
-# =================================================================================================
-# IMPORTS
-# =================================================================================================
-
-import json
-import re
-import shutil
-from functools import partial
-from importlib import import_module
-from pathlib import Path
-
-import numpy as np
-import pandas as pd
-import xmltodict as xml
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.ceasiompyutils import get_results_directory
-from ceasiompy.utils.commonxpath import PYTORNADO_EXTRACT_LOAD_XPATH
-from ceasiompy.utils.moduleinterfaces import (
- check_cpacs_input_requirements,
- get_toolinput_file_path,
- get_tooloutput_file_path,
-)
-from cpacspy.cpacsfunctions import get_value_or_default, open_tixi
-
-log = get_logger()
-
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
-
-dump_pretty_json = partial(json.dump, indent=4, separators=(",", ": "))
-
-REGEX_INT = re.compile(r"^[-+]?[0-9]+$")
-REGEX_FLOAT = re.compile(r"^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$")
-
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
-
-
-def import_pytornado(module_name):
- """Try to import PyTornado and return module if successful
-
- Args:
- module_name (str): Name of the module
-
- Returns:
- module: Loaded module
-
- Raises:
- ModuleNotFoundError: If PyTornado is not found
- """
-
- try:
- module_name = import_module(module_name)
- except ModuleNotFoundError:
- err_msg = """\n
- | PyTornado was not found. CEASIOMpy cannot run an analysis.
- | Make sure that PyTornado is correctly installed. Please refer to:
- |
- | * https://github.com/airinnova/pytornado
- """
- log.error(err_msg)
- raise ModuleNotFoundError(err_msg)
-
- return module_name
-
-
-def get_pytornado_settings(cpacs_in_path):
- """Return a default settings dictionary
-
- The default PyTornado settings will be used. Settings defined in CPACS will
- be loaded and will overwrite the PyTornado default dictionary.
-
- Notes:
- * We expect that the CPACS XML has the same structure as the JSON
- settings file (see PyTornado documentation). Note that the structure
- defined in XML will not be checked here. However, PyTornado will
- perform a runtime check.
-
- Args:
- :cpacs_in_path (str): CPACS file path
-
- Returns:
- :settings (dict): CPACS settings dictionary
- """
-
- # ----- Fetch PyTornado's default settings -----
- # Note:
- # * First, get a default settings dictionary from PyTornado
- # * The dictionary is described in the PyTornado documentation, see
- # --> https://pytornado.readthedocs.io/en/latest/user_guide/input_file_settings.html
-
- ps = import_pytornado("pytornado.objects.settings")
- settings = ps.get_default_dict(ps.DEFAULT_SETTINGS)
-
- # ----- Modify the default dict -----
- settings["aircraft"] = "ToolInput.xml" # Aircraft input file
- settings["state"] = "__CPACS" # Load aeroperformance map from CPACS
- settings["plot"]["results"]["show"] = False
- settings["plot"]["results"]["save"] = False
- settings["save_results"]["aeroperformance"] = True
-
- # ----- Try to read PyTornado settings from CPACS -----
- cpacs_settings = get_pytornado_settings_from_CPACS(cpacs_in_path)
- if cpacs_settings is not None:
- update_dict(settings, cpacs_settings)
- parse_pytornado_settings_dict(settings)
- return settings
-
-
-def get_pytornado_settings_from_CPACS(cpacs_in_path):
- """Try to read PyTornado settings from CPACS
-
- Note:
- * Returns None if PyTornado settings not found in CPACS
-
- Args:
- cpacs_in_path (str): Path to CPACS file
-
- Returns:
- cpacs_settings (dict): PyTornado settings dictionary read from CPACS
- """
-
- with open(cpacs_in_path, "r") as fp:
- cpacs_as_dict = xml.parse(fp.read())
- cpacs_settings = cpacs_as_dict.get("cpacs", {}).get("toolspecific", {}).get("pytornado", None)
- parse_pytornado_settings_dict(cpacs_settings)
- return cpacs_settings
-
-
-def update_dict(to_update, other_dict):
- """Update 'to_update' dict with 'other_dict' recursively
-
- Note:
- * Entries from 'other_dict' are only updated if they exist in 'to_update'
-
- Args:
- to_update (dict): Dictionary which is to be updated
- other_dict (dict): Dictionary with new values
- """
-
- for key, value in other_dict.items():
- if isinstance(value, dict):
- update_dict(to_update.get(key, {}), value)
- elif to_update.get(key, None) is not None:
- to_update[key] = value
-
-
-def parse_pytornado_settings_dict(dictionary):
- """Parse the PyTornado settings dict
-
- Note:
- * Parses dictionary recursively
- * Replaces strings 'True' or 'true' with boolean True
- * Replaces strings 'False' or 'false' with boolean False
- * Converts float-like strings to float numbers
- * Converts int-like strings to integer numbers
-
- Args:
- dictionary (dict): Dictionary to parse
- """
-
- for k, v in dictionary.items():
-
- # Parse dictionary recursively
- if isinstance(v, dict):
- parse_pytornado_settings_dict(v)
-
- # Convert strings to bool, float, int
- elif isinstance(v, str):
- if v.lower() == "true":
- v = True
- elif v.lower() == "false":
- v = False
- # First check integer, then float!
- elif REGEX_INT.fullmatch(v):
- v = int(v)
- elif REGEX_FLOAT.fullmatch(v):
- v = float(v)
-
- # TODO: list/tuple
-
- dictionary[k] = v
-
-
-def _get_load_fields(pytornado_results, dir_pyt_results):
- """
- Return load fields from PyTornado results (only extract load from last results)
- (TODO: Maybe this function could integrated to PyTornado...?)
- Args:
- :pytornado_results: (obj) PyTornado results data structure
- :dir_pyt_results: (str): Path to the results dir
- Returns:
- :load_fields: (dict) AeroFrame load fields
- """
-
- vlmdata = pytornado_results["vlmdata"]
- lattice = pytornado_results["lattice"]
-
- # PyTornado API provides access to loads on main wing and on mirrored side
- bookkeeping_lists = (
- (lattice.bookkeeping_by_wing_uid, ""),
- (lattice.bookkeeping_by_wing_uid_mirror, "_m"),
- )
-
- frames = []
-
- load_fields = {}
- for (bookkeeping_list, suffix) in bookkeeping_lists:
- for wing_uid, panellist in bookkeeping_list.items():
- # Count number of panels
- num_pan = 0
- for entry in panellist:
- num_pan += len(entry.pan_idx)
-
- load_field = np.empty((1, 6))
- for entry in panellist:
- # pan_idx: Panel index in PyTornado book keeping system
- for pan_idx in entry.pan_idx:
- load_field_entry = np.zeros((1, 6))
- # load_field_entry[0, 0:3] = self.points_of_attack_undeformed[pan_idx]
- load_field_entry[0, 0:3] = lattice.bound_leg_midpoints[
- pan_idx
- ] # TODO: correct to use this field as coordinates??
- load_field_entry[0, 3] = vlmdata.panelwise["fx"][pan_idx]
- load_field_entry[0, 4] = vlmdata.panelwise["fy"][pan_idx]
- load_field_entry[0, 5] = vlmdata.panelwise["fz"][pan_idx]
- load_field = np.append(load_field, load_field_entry, axis=0)
-
- # Write one CSV file per Wing
- load_fields[wing_uid + suffix] = load_field[1:, :]
- df = pd.DataFrame(load_field[1:, :])
- df.columns = ["x", "y", "z", "fx", "fy", "fz"]
- df["wing_uid"] = wing_uid + suffix
- frames.append(df)
-
- # Write aircraft load in a CSV file
- df_tot = pd.concat(frames)
- csv_path = Path(dir_pyt_results, "aircraft_loads.csv")
- df_tot.to_csv(csv_path, sep=",", index=False)
-
-
-# =================================================================================================
-# MAIN
-# =================================================================================================
-
-
-def main(cpacs_in_path, cpacs_out_path):
-
- log.info("Running PyTornado...")
-
- # ===== Delete old working directories =====
- settings_from_CPACS = get_pytornado_settings_from_CPACS(cpacs_in_path)
- if settings_from_CPACS is not None:
- if settings_from_CPACS.get("deleteOldWKDIRs", False):
- wkdirs = [p for p in MODULE_DIR.iterdir() if "wkdir_" in p.name]
- for wkdir in wkdirs:
- shutil.rmtree(wkdir, ignore_errors=True)
-
- # ===== Paths =====
- dir_pyt_wkdir = Path(MODULE_DIR, "wkdir_temp")
- dir_pyt_aircraft = Path(dir_pyt_wkdir, "aircraft")
- dir_pyt_settings = Path(dir_pyt_wkdir, "settings")
- dir_pyt_results = Path(dir_pyt_wkdir, "_results")
- dir_pyt_plots = Path(dir_pyt_wkdir, "_plots")
- file_pyt_aircraft = Path(dir_pyt_aircraft, "ToolInput.xml")
- file_pyt_settings = Path(dir_pyt_settings, "cpacs_run.json")
-
- # ===== Make directories =====
- if dir_pyt_wkdir.exists():
- shutil.rmtree(dir_pyt_wkdir, ignore_errors=True)
- Path(dir_pyt_wkdir).mkdir(parents=True, exist_ok=True)
- Path(dir_pyt_aircraft).mkdir(parents=True, exist_ok=True)
- Path(dir_pyt_settings).mkdir(parents=True, exist_ok=True)
- Path(dir_pyt_results).mkdir(parents=True, exist_ok=True)
-
- # ===== Setup =====
- shutil.copy(src=cpacs_in_path, dst=file_pyt_aircraft)
- check_cpacs_input_requirements(cpacs_in_path)
-
- # ===== Get PyTornado settings =====
- cpacs_settings = get_pytornado_settings(cpacs_in_path)
- with open(file_pyt_settings, "w") as fp:
- dump_pretty_json(cpacs_settings, fp)
-
- # ===== PyTornado analysis =====
- pytornado = import_pytornado("pytornado.stdfun.run")
- results = pytornado.standard_run(
- args=pytornado.StdRunArgs(run=file_pyt_settings, verbose=True)
- )
-
- # ===== Extract load =====
- tixi = open_tixi(cpacs_in_path)
- extract_loads = get_value_or_default(tixi, PYTORNADO_EXTRACT_LOAD_XPATH, False)
- if extract_loads:
- _get_load_fields(results, dir_pyt_results)
-
- # ===== Clean up =====
- shutil.copy(src=file_pyt_aircraft, dst=cpacs_out_path)
-
- # ===== Copy files from `results` and `plots`in the wkflow results directory =====
- last_file = False
- i = 0
- while not last_file:
- pyt_plot_case_dir = Path(dir_pyt_results, f"cpacs_run_{i:03}")
- pyt_res_case_dir = Path(dir_pyt_plots, f"cpacs_run_{i:03}")
-
- wkflow_results_case_dir = Path(get_results_directory("PyTornado"), f"Case_{i:03}")
-
- if pyt_plot_case_dir.exists() and pyt_res_case_dir.exists():
- shutil.copytree(pyt_plot_case_dir, wkflow_results_case_dir)
- shutil.copytree(pyt_res_case_dir, wkflow_results_case_dir, dirs_exist_ok=True)
- i += 1
- else:
- last_file = True
-
- log.info("PyTornado analysis completed")
-
-
-if __name__ == "__main__":
-
- cpacs_in_path = get_toolinput_file_path(MODULE_NAME)
- cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
-
- main(cpacs_in_path, cpacs_out_path)
diff --git a/ceasiompy/PyTornado/tests/.gitignore b/ceasiompy/PyTornado/tests/.gitignore
deleted file mode 100644
index 47652f851..000000000
--- a/ceasiompy/PyTornado/tests/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-wkdir*
diff --git a/ceasiompy/PyTornado/tests/ToolInput/.keep b/ceasiompy/PyTornado/tests/ToolInput/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/ceasiompy/PyTornado/tests/ToolOutput/.keep b/ceasiompy/PyTornado/tests/ToolOutput/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/ceasiompy/PyTornado/tests/cpacs_test_file.xml b/ceasiompy/PyTornado/tests/cpacs_test_file.xml
deleted file mode 100644
index 2cca037b1..000000000
--- a/ceasiompy/PyTornado/tests/cpacs_test_file.xml
+++ /dev/null
@@ -1,4272 +0,0 @@
-
-
-
- B777 Test AeroPerfomanceMap of CPACSv3.1
- ...
- Daniel Boehnke, DLR-LK
- 2010-07-07T12:00:00
- 1.0
- 3.1
-
-
- Converted to cpacs 3.0 using cpacs2to3
- cpacs2to3
- 2018-10-24T13:41:46
- ver1
- 3.0
-
-
- Add aeroPerformanceMap version 3.1
- Aidan Jungo
- 2019-08-16T08:41:46
- ver1
- 3.1
-
-
-
-
-
-
- DLR's D150 Release Bird for CPACS 2.0
- ...
-
- 427.8
- 8.75
-
- 0.0
- 0.0
- 0.0
-
-
-
-
- multi_wing_1
- Fuslage_1ID
- cpacs wing template
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 21.6312
- 0
- -1.6156
-
-
-
-
- multi_wing_1section1
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
- multi_wing_1section1element1
- NACA2315
-
-
- 13.4342
- 13.4342
- 13.4342
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- multi_wing_1section2
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
- multi_wing_1section2element1
- NACA1310
-
-
- 13.4342
- 13.4342
- 13.4342
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- multi_wing_1section3
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
-
-
- multi_wing_1section3element1
- NACA1309
-
-
- 0
- 0
- 0
-
-
- 9.5662
- 9.5662
- 9.5662
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- multi_wing_1section4
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
-
-
- multi_wing_1section4element1
- NACA1308
-
-
- 0
- 0
- 0
-
-
- 8.3531
- 8.3531
- 8.3531
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- multi_wing_1section5
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
-
-
- multi_wing_1section5element1
- NACA1508
-
-
- 0
- 0
- 0
-
-
- 4.147
- 4.147
- 4.147
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- multi_wing_1section6
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
-
-
- multi_wing_1section6element1
- NACA1508
-
-
- 0
- 0
- 0
-
-
- 2.1706
- 2.1706
- 2.1706
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- multi_wing_1section7
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
-
-
- multi_wing_1section7element1
- NACA1508
-
-
- 0
- 0
- 0
-
-
- 1.4778
- 1.4778
- 1.4778
-
-
- 0
- 0
- 0
-
-
-
-
-
-
-
-
- multi_wing_1positioning1
- 0
- 0
- 0
- multi_wing_1section1ID
-
-
- multi_wing_1positioning2
- 2.9937
- 0
- 0
- multi_wing_1section1ID
- multi_wing_1section2ID
-
-
- multi_wing_1positioning3
- 6
- 34.1706
- multi_wing_1section3ID
- multi_wing_1section2ID
- 6.2389
-
-
- multi_wing_1positioning4
- 6
- 34.1706
- multi_wing_1section4ID
- multi_wing_1section3ID
- 2.4542
-
-
- multi_wing_1positioning5
- 6
- 34.1706
- multi_wing_1section5ID
- multi_wing_1section4ID
- 13.7392
-
-
- multi_wing_1positioning6
- 6
- 34.1706
- multi_wing_1section6ID
- multi_wing_1section5ID
- 8.1397
-
-
- multi_wing_1positioning7
- 6
- 34.1706
- multi_wing_1section7ID
- multi_wing_1section6ID
- 2.8211
-
-
-
-
- multi_wing_1segment1
- multi_wing_1section1element1ID
- multi_wing_1section2element1ID
-
-
- multi_wing_1segment2
- multi_wing_1section2element1ID
- multi_wing_1section3element1ID
-
-
- multi_wing_1segment3
- multi_wing_1section3element1ID
- multi_wing_1section4element1ID
-
-
- multi_wing_1segment4
- multi_wing_1section4element1ID
- multi_wing_1section5element1ID
-
-
- multi_wing_1segment5
- multi_wing_1section5element1ID
- multi_wing_1section6element1ID
-
-
- multi_wing_1segment6
- multi_wing_1section6element1ID
- multi_wing_1section7element1ID
-
-
-
-
- cpacs wing template
- multi_wing_2
- Fuslage_1ID
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 52.6478
- 0
- 0.77675
-
-
-
-
- multi_wing_2section1
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
-
-
- multi_wing_2section1element1
- NACA0012
-
-
- 0
- 0
- 0
-
-
- 7.0392
- 7.0392
- 7.0392
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- multi_wing_2section2
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
-
-
- multi_wing_2section2element1
- NACA0008
-
-
- 0
- 0
- 0
-
-
- 6.3529
- 6.3529
- 6.3529
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- multi_wing_2section3
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
-
-
- multi_wing_2section3element1
- NACA0008
-
-
- 0
- 0
- 0
-
-
- 2.4637
- 2.4637
- 2.4637
-
-
- 0
- 0
- 0
-
-
-
-
-
-
-
-
- multi_wing_2positioning1
- 0
- 0
- multi_wing_2section1ID
- 0
-
-
- multi_wing_2positioning2
- 0
- 38.657
- multi_wing_2section2ID
- multi_wing_2section1ID
- 2.0248
-
-
- multi_wing_2positioning3
- 8
- 38.384
- multi_wing_2section3ID
- multi_wing_2section2ID
- 11.8403
-
-
-
-
- multi_wing_2segment1
- multi_wing_2section1element1ID
- multi_wing_2section2element1ID
-
-
- multi_wing_2segment2
- multi_wing_2section2element1ID
- multi_wing_2section3element1ID
-
-
-
-
- cpacs wing template
- multi_wing_3
- Fuslage_1ID
-
-
- 90
- 0
- -4
-
-
- 1
- 1
- 1
-
-
- 50.6946
- 0
- 2.8786
-
-
-
-
- multi_wing_3section1
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
-
-
- multi_wing_3section1element1
- NACA0008
-
-
- 0
- 0
- 0
-
-
- 8.4084
- 8.4084
- 8.4084
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- multi_wing_3section2
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
-
-
- multi_wing_3section2element1
- NACA0008
-
-
- 0
- 0
- 0
-
-
- 2.5225
- 2.5225
- 2.5225
-
-
- 0
- 0
- 0
-
-
-
-
-
-
-
-
- multi_wing_3positioning1
- 0
- 0
- multi_wing_3section1ID
- 0
-
-
- multi_wing_3positioning2
- 0
- 40.72
- multi_wing_3section2ID
- multi_wing_3section1ID
- 12.8472
-
-
-
-
- multi_wing_3segment1
- multi_wing_3section1element1ID
- multi_wing_3section2element1ID
-
-
-
-
-
-
- Fuslage template
- Fuslage_1
-
-
- 0
- 0
- 0
-
-
- 1.0
- 1.0
- 1.0
-
-
- 0
- 0
- 0
-
-
-
-
- Fuslage_1section1
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.61455
-
-
-
-
- Fuslage_1section1element1
- B777_FL1_Sec1_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section2
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.6016
-
-
-
-
- Fuslage_1section2element1
- B777_FL1_Sec2_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 0.34091
- 0.39073
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section3
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.56439
-
-
-
-
- Fuslage_1section3element1
- B777_FL1_Sec3_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 0.5534
- 0.5723
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section4
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.56184
-
-
-
-
- Fuslage_1section4element1
- B777_FL1_Sec4_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 0.7177
- 0.7075
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section5
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.56876
-
-
-
-
- Fuslage_1section5element1
- B777_FL1_Sec5_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 0.8503
- 0.82245
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section6
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.57634
-
-
-
-
- Fuslage_1section6element1
- B777_FL1_Sec6_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 0.95925
- 0.9268
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section7
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.58382
-
-
-
-
- Fuslage_1section7element1
- B777_FL1_Sec7_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 1.0512
- 1.0221
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section8
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.59015
-
-
-
-
- Fuslage_1section8element1
- B777_FL1_Sec8_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 1.1311
- 1.1101
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section9
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.41198
-
-
-
-
- Fuslage_1section9element1
- B777_FL1_Sec9_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 1.3357
- 1.5284
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section10
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.3505
-
-
-
-
- Fuslage_1section10element1
- B777_FL1_Sec10_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 1.527
- 1.7763
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section11
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.30728
-
-
-
-
- Fuslage_1section11element1
- B777_FL1_Sec11_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 1.7226
- 1.9673
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section12
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.26468
-
-
-
-
- Fuslage_1section12element1
- B777_FL1_Sec12_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 1.9164
- 2.1313
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section13
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.2264
-
-
-
-
- Fuslage_1section13element1
- B777_FL1_Sec13_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 2.096
- 2.2746
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section14
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.19494
-
-
-
-
- Fuslage_1section14element1
- B777_FL1_Sec14_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 2.2522
- 2.4019
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section15
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.17155
-
-
-
-
- Fuslage_1section15element1
- B777_FL1_Sec15_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 2.3828
- 2.5168
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section16
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.1563
-
-
-
-
- Fuslage_1section16element1
- B777_FL1_Sec16_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 2.493
- 2.6218
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section17
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.14803
-
-
-
-
- Fuslage_1section17element1
- B777_FL1_Sec17_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 2.5917
- 2.7179
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section18
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.14435
-
-
-
-
- Fuslage_1section18element1
- B777_FL1_Sec18_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 2.6883
- 2.8053
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section19
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.14169
-
-
-
-
- Fuslage_1section19element1
- B777_FL1_Sec19_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 2.7876
- 2.8828
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section20
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.11245
-
-
-
-
- Fuslage_1section20element1
- B777_FL1_Sec20_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 2.8872
- 2.9249
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section21
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.09451
-
-
-
-
- Fuslage_1section21element1
- B777_FL1_Sec21_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 2.9772
- 2.972
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section22
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.075648
-
-
-
-
- Fuslage_1section22element1
- B777_FL1_Sec22_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 3.0444
- 3.0143
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section23
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.054557
-
-
-
-
- Fuslage_1section23element1
- B777_FL1_Sec23_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 3.0814
- 3.0528
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section24
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- -0.032814
-
-
-
-
- Fuslage_1section24element1
- B777_FL1_Sec24_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 3.1024
- 3.0889
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section25
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
-
-
- Fuslage_1section25element1
- B777_FL1_Sec25_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 3.107
- 3.107
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section26
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
-
-
- Fuslage_1section26element1
- B777_FL1_Sec26_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 3.107
- 3.107
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section27
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
-
-
- Fuslage_1section27element1
- B777_FL1_Sec27_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 3.107
- 3.107
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section28
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
-
-
- Fuslage_1section28element1
- B777_FL1_Sec28_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 3.107
- 3.107
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section29
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
-
-
- Fuslage_1section29element1
- B777_FL1_Sec29_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 3.107
- 3.107
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section30
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
-
-
- Fuslage_1section30element1
- B777_FL1_Sec30_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 3.107
- 3.107
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section31
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0.017424
-
-
-
-
- Fuslage_1section31element1
- B777_FL1_Sec31_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 3.0924
- 3.0896
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section32
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0.045325
-
-
-
-
- Fuslage_1section32element1
- B777_FL1_Sec32_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 3.0947
- 3.0617
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section33
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0.16004
-
-
-
-
- Fuslage_1section33element1
- B777_FL1_Sec33_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 3.0768
- 2.947
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section34
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0.29171
-
-
-
-
- Fuslage_1section34element1
- B777_FL1_Sec34_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 2.983
- 2.791
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section35
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0.41976
-
-
-
-
- Fuslage_1section35element1
- B777_FL1_Sec35_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 2.8653
- 2.6186
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section36
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0.54388
-
-
-
-
- Fuslage_1section36element1
- B777_FL1_Sec36_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 2.7306
- 2.4404
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section37
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0.66089
-
-
-
-
- Fuslage_1section37element1
- B777_FL1_Sec37_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 2.5335
- 2.2529
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section38
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0.76404
-
-
-
-
- Fuslage_1section38element1
- B777_FL1_Sec38_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 2.2481
- 2.0539
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section39
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0.81068
-
-
-
-
- Fuslage_1section39element1
- B777_FL1_Sec39_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 2.079
- 1.948
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section40
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0.8546
-
-
-
-
- Fuslage_1section40element1
- B777_FL1_Sec40_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 1.9035
- 1.8377
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section41
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0.89732
-
-
-
-
- Fuslage_1section41element1
- B777_FL1_Sec41_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 1.7293
- 1.7208
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section42
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0.97522
-
-
-
-
- Fuslage_1section42element1
- B777_FL1_Sec42_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 1.4398
- 1.4862
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section43
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 1.049
-
-
-
-
- Fuslage_1section43element1
- B777_FL1_Sec43_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 1.2053
- 1.2348
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section44
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 1.1094
-
-
-
-
- Fuslage_1section44element1
- B777_FL1_Sec44_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 0.91775
- 0.97185
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section45
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 1.153
-
-
-
-
- Fuslage_1section45element1
- B777_FL1_Sec45_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 0.43255
- 0.69725
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section46
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 1.179
-
-
-
-
- Fuslage_1section46element1
- B777_FL1_Sec46_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 0.13264
- 0.5523
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Fuslage_1section47
-
-
- 0
- 0
- 0
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 1.179
-
-
-
-
- Fuslage_1section47element1
- B777_FL1_Sec47_Elem1UID
-
-
- 0
- 0
- 0
-
-
- 1
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
-
-
-
-
- Fuslage_1positioning1
- 0
- 90
- Fuslage_1section1ID
- 0
-
-
- Fuslage_1positioning2
- 0
- 90
- Fuslage_1section2ID
- Fuslage_1section1ID
- 0.18027
-
-
- Fuslage_1positioning3
- 0
- 90
- Fuslage_1section3ID
- Fuslage_1section2ID
- 0.18027
-
-
- Fuslage_1positioning4
- 0
- 90
- Fuslage_1section4ID
- Fuslage_1section3ID
- 0.18027
-
-
- Fuslage_1positioning5
- 0
- 90
- Fuslage_1section5ID
- Fuslage_1section4ID
- 0.1803
-
-
- Fuslage_1positioning6
- 0
- 90
- Fuslage_1section6ID
- Fuslage_1section5ID
- 0.18026
-
-
- Fuslage_1positioning7
- 0
- 90
- Fuslage_1section7ID
- Fuslage_1section6ID
- 0.18026
-
-
- Fuslage_1positioning8
- 0
- 90
- Fuslage_1section8ID
- Fuslage_1section7ID
- 0.18026
-
-
- Fuslage_1positioning9
- 0
- 90
- Fuslage_1section9ID
- Fuslage_1section8ID
- 0.54154
-
-
- Fuslage_1positioning10
- 0
- 90
- Fuslage_1section10ID
- Fuslage_1section9ID
- 0.54147
-
-
- Fuslage_1positioning11
- 0
- 90
- Fuslage_1section11ID
- Fuslage_1section10ID
- 0.54154
-
-
- Fuslage_1positioning12
- 0
- 90
- Fuslage_1section12ID
- Fuslage_1section11ID
- 0.54154
-
-
- Fuslage_1positioning13
- 0
- 90
- Fuslage_1section13ID
- Fuslage_1section12ID
- 0.54147
-
-
- Fuslage_1positioning14
- 0
- 90
- Fuslage_1section14ID
- Fuslage_1section13ID
- 0.54154
-
-
- Fuslage_1positioning15
- 0
- 90
- Fuslage_1section15ID
- Fuslage_1section14ID
- 0.54154
-
-
- Fuslage_1positioning16
- 0
- 90
- Fuslage_1section16ID
- Fuslage_1section15ID
- 0.54147
-
-
- Fuslage_1positioning17
- 0
- 90
- Fuslage_1section17ID
- Fuslage_1section16ID
- 0.54084
-
-
- Fuslage_1positioning18
- 0
- 90
- Fuslage_1section18ID
- Fuslage_1section17ID
- 0.54122
-
-
- Fuslage_1positioning19
- 0
- 90
- Fuslage_1section19ID
- Fuslage_1section18ID
- 0.54191
-
-
- Fuslage_1positioning20
- 0
- 90
- Fuslage_1section20ID
- Fuslage_1section19ID
- 0.54128
-
-
- Fuslage_1positioning21
- 0
- 90
- Fuslage_1section21ID
- Fuslage_1section20ID
- 0.54191
-
-
- Fuslage_1positioning22
- 0
- 90
- Fuslage_1section22ID
- Fuslage_1section21ID
- 0.54128
-
-
- Fuslage_1positioning23
- 0
- 90
- Fuslage_1section23ID
- Fuslage_1section22ID
- 0.54128
-
-
- Fuslage_1positioning24
- 0
- 90
- Fuslage_1section24ID
- Fuslage_1section23ID
- 0.54191
-
-
- Fuslage_1positioning25
- 0
- 90
- Fuslage_1section25ID
- Fuslage_1section24ID
- 0.54128
-
-
- Fuslage_1positioning26
- 0
- 90
- Fuslage_1section26ID
- Fuslage_1section25ID
- 5.9837
-
-
- Fuslage_1positioning27
- 0
- 90
- Fuslage_1section27ID
- Fuslage_1section26ID
- 5.9843
-
-
- Fuslage_1positioning28
- 0
- 90
- Fuslage_1section28ID
- Fuslage_1section27ID
- 5.9837
-
-
- Fuslage_1positioning29
- 0
- 90
- Fuslage_1section29ID
- Fuslage_1section28ID
- 5.9837
-
-
- Fuslage_1positioning30
- 0
- 90
- Fuslage_1section30ID
- Fuslage_1section29ID
- 5.9843
-
-
- Fuslage_1positioning31
- 0
- 90
- Fuslage_1section31ID
- Fuslage_1section30ID
- 0.58282
-
-
- Fuslage_1positioning32
- 0
- 90
- Fuslage_1section32ID
- Fuslage_1section31ID
- 0.60234
-
-
- Fuslage_1positioning33
- 0
- 90
- Fuslage_1section33ID
- Fuslage_1section32ID
- 1.768
-
-
- Fuslage_1positioning34
- 0
- 90
- Fuslage_1section34ID
- Fuslage_1section33ID
- 1.79
-
-
- Fuslage_1positioning35
- 0
- 90
- Fuslage_1section35ID
- Fuslage_1section34ID
- 1.7686
-
-
- Fuslage_1positioning36
- 0
- 90
- Fuslage_1section36ID
- Fuslage_1section35ID
- 1.768
-
-
- Fuslage_1positioning37
- 0
- 90
- Fuslage_1section37ID
- Fuslage_1section36ID
- 1.7888
-
-
- Fuslage_1positioning38
- 0
- 90
- Fuslage_1section38ID
- Fuslage_1section37ID
- 1.7692
-
-
- Fuslage_1positioning39
- 0
- 90
- Fuslage_1section39ID
- Fuslage_1section38ID
- 0.88368
-
-
- Fuslage_1positioning40
- 0
- 90
- Fuslage_1section40ID
- Fuslage_1section39ID
- 0.88431
-
-
- Fuslage_1positioning41
- 0
- 90
- Fuslage_1section41ID
- Fuslage_1section40ID
- 0.90571
-
-
- Fuslage_1positioning42
- 0
- 90
- Fuslage_1section42ID
- Fuslage_1section41ID
- 1.746
-
-
- Fuslage_1positioning43
- 0
- 90
- Fuslage_1section43ID
- Fuslage_1section42ID
- 1.795
-
-
- Fuslage_1positioning44
- 0
- 90
- Fuslage_1section44ID
- Fuslage_1section43ID
- 1.7843
-
-
- Fuslage_1positioning45
- 0
- 90
- Fuslage_1section45ID
- Fuslage_1section44ID
- 1.768
-
-
- Fuslage_1positioning46
- 0
- 90
- Fuslage_1section46ID
- Fuslage_1section45ID
- 0.94851
-
-
- Fuslage_1positioning47
- 0
- 90
- Fuslage_1section47ID
- Fuslage_1section46ID
- 0
-
-
-
-
- Fuslage_1segment1
- Fuslage_1section1element1ID
- Fuslage_1section2element1ID
-
-
- Fuslage_1segment2
- Fuslage_1section2element1ID
- Fuslage_1section3element1ID
-
-
- Fuslage_1segment3
- Fuslage_1section3element1ID
- Fuslage_1section4element1ID
-
-
- Fuslage_1segment4
- Fuslage_1section4element1ID
- Fuslage_1section5element1ID
-
-
- Fuslage_1segment5
- Fuslage_1section5element1ID
- Fuslage_1section6element1ID
-
-
- Fuslage_1segment6
- Fuslage_1section6element1ID
- Fuslage_1section7element1ID
-
-
- Fuslage_1segment7
- Fuslage_1section7element1ID
- Fuslage_1section8element1ID
-
-
- Fuslage_1segment8
- Fuslage_1section8element1ID
- Fuslage_1section9element1ID
-
-
- Fuslage_1segment9
- Fuslage_1section9element1ID
- Fuslage_1section10element1ID
-
-
- Fuslage_1segment10
- Fuslage_1section10element1ID
- Fuslage_1section11element1ID
-
-
- Fuslage_1segment11
- Fuslage_1section11element1ID
- Fuslage_1section12element1ID
-
-
- Fuslage_1segment12
- Fuslage_1section12element1ID
- Fuslage_1section13element1ID
-
-
- Fuslage_1segment13
- Fuslage_1section13element1ID
- Fuslage_1section14element1ID
-
-
- Fuslage_1segment14
- Fuslage_1section14element1ID
- Fuslage_1section15element1ID
-
-
- Fuslage_1segment15
- Fuslage_1section15element1ID
- Fuslage_1section16element1ID
-
-
- Fuslage_1segment16
- Fuslage_1section16element1ID
- Fuslage_1section17element1ID
-
-
- Fuslage_1segment17
- Fuslage_1section17element1ID
- Fuslage_1section18element1ID
-
-
- Fuslage_1segment18
- Fuslage_1section18element1ID
- Fuslage_1section19element1ID
-
-
- Fuslage_1segment19
- Fuslage_1section19element1ID
- Fuslage_1section20element1ID
-
-
- Fuslage_1segment20
- Fuslage_1section20element1ID
- Fuslage_1section21element1ID
-
-
- Fuslage_1segment21
- Fuslage_1section21element1ID
- Fuslage_1section22element1ID
-
-
- Fuslage_1segment22
- Fuslage_1section22element1ID
- Fuslage_1section23element1ID
-
-
- Fuslage_1segment23
- Fuslage_1section23element1ID
- Fuslage_1section24element1ID
-
-
- Fuslage_1segment24
- Fuslage_1section24element1ID
- Fuslage_1section25element1ID
-
-
- Fuslage_1segment25
- Fuslage_1section25element1ID
- Fuslage_1section26element1ID
-
-
- Fuslage_1segment26
- Fuslage_1section26element1ID
- Fuslage_1section27element1ID
-
-
- Fuslage_1segment27
- Fuslage_1section27element1ID
- Fuslage_1section28element1ID
-
-
- Fuslage_1segment28
- Fuslage_1section28element1ID
- Fuslage_1section29element1ID
-
-
- Fuslage_1segment29
- Fuslage_1section29element1ID
- Fuslage_1section30element1ID
-
-
- Fuslage_1segment30
- Fuslage_1section30element1ID
- Fuslage_1section31element1ID
-
-
- Fuslage_1segment31
- Fuslage_1section31element1ID
- Fuslage_1section32element1ID
-
-
- Fuslage_1segment32
- Fuslage_1section32element1ID
- Fuslage_1section33element1ID
-
-
- Fuslage_1segment33
- Fuslage_1section33element1ID
- Fuslage_1section34element1ID
-
-
- Fuslage_1segment34
- Fuslage_1section34element1ID
- Fuslage_1section35element1ID
-
-
- Fuslage_1segment35
- Fuslage_1section35element1ID
- Fuslage_1section36element1ID
-
-
- Fuslage_1segment36
- Fuslage_1section36element1ID
- Fuslage_1section37element1ID
-
-
- Fuslage_1segment37
- Fuslage_1section37element1ID
- Fuslage_1section38element1ID
-
-
- Fuslage_1segment38
- Fuslage_1section38element1ID
- Fuslage_1section39element1ID
-
-
- Fuslage_1segment39
- Fuslage_1section39element1ID
- Fuslage_1section40element1ID
-
-
- Fuslage_1segment40
- Fuslage_1section40element1ID
- Fuslage_1section41element1ID
-
-
- Fuslage_1segment41
- Fuslage_1section41element1ID
- Fuslage_1section42element1ID
-
-
- Fuslage_1segment42
- Fuslage_1section42element1ID
- Fuslage_1section43element1ID
-
-
- Fuslage_1segment43
- Fuslage_1section43element1ID
- Fuslage_1section44element1ID
-
-
- Fuslage_1segment44
- Fuslage_1section44element1ID
- Fuslage_1section45element1ID
-
-
- Fuslage_1segment45
- Fuslage_1section45element1ID
- Fuslage_1section46element1ID
-
-
- Fuslage_1segment46
- Fuslage_1section46element1ID
- Fuslage_1section47element1ID
-
-
-
-
-
-
- name
- 3PW066
- multi_wing_1ID
-
-
- 0.0
- 0.0
- 0.0
-
-
-
-
-
-
-
- aeroMap_Test
- aeroMap to test CEASIOMpy calculated with PyTornado
-
- ISA
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0
- 0.3;0.3;0.3;0.3;0.3;0.3;0.3;0.3;0.3;0.3;0.3;0.3
- -8.0;-4.0;-2.0;-1.0;0.0;1.0;2.0;3.0;4.0;6.0;8.0;10.0
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
-
-
-
- aeroMap_Test
- aeroMap to test CEASIOMpy Calculated with SU2
-
- ISA
-
-
- 0;0;0;0;0;0
- 0.3;0.3;0.3;0.3;0.3;0.3
- -6.0;-2.0;0.0;2.0;4.0;8.0
- 0.0;0.0;0.0;0.0;0.0;0.0
-
-
-
-
-
-
- 335254
- Maximum take-off mass
- Maximum take off mass [kg], CoG coordinate [m] and moment of inertia.
-
-
- 216803
- Maximum zero fuel mass
- Maximum zero fuel mass [kg] and corresponding CoG coordinate [m], moment of inertia.
-
-
-
-
- 148064
- Max fuel mass
- Maximum fuel mass [kg].
-
-
-
-
- 171338
- Operating empty mass
- Operating empty mass [kg] and related inertia [kgm^2].
-
-
-
-
- 816
-
-
-
-
-
-
- 33936
-
-
-
-
- 111670
-
-
-
-
- 16032
-
-
-
-
-
-
- 45465
- Max payload mass
- Maximum payload mass [kg].
-
-
- 0
-
-
-
-
-
-
-
-
-
- Circle
- Profile build up from set of Points on Circle where may Dimensions are 1..-1
-
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- 0.0;0.0774924206719;0.154518792808;0.230615870742;0.305325997695;0.378199858172;0.4487991802;0.516699371152;0.581492071288;0.642787609687;0.700217347767;0.753435896328;0.802123192755;0.84598642592;0.884761797177;0.91821610688;0.946148156876;0.968389960528;0.984807753012;0.995302795793;0.999811970449;0.998308158271;0.990800403365;0.977333858251;0.957989512315;0.932883704732;0.902167424781;0.866025403784;0.824675004109;0.778364911924;0.727373641573;0.672007860556;0.612600545193;0.549508978071;0.483112599297;0.413810724505;0.342020143326;0.268172612761;0.192712260548;0.116092914125;0.0387753712568;-0.0387753712568;-0.116092914125;-0.192712260548;-0.268172612761;-0.342020143326;-0.413810724505;-0.483112599297;-0.549508978071;-0.612600545193;-0.672007860556;-0.727373641573;-0.778364911924;-0.824675004109;-0.866025403784;-0.902167424781;-0.932883704732;-0.957989512315;-0.977333858251;-0.990800403365;-0.998308158271;-0.999811970449;-0.995302795793;-0.984807753012;-0.968389960528;-0.946148156876;-0.91821610688;-0.884761797177;-0.84598642592;-0.802123192755;-0.753435896328;-0.700217347767;-0.642787609687;-0.581492071288;-0.516699371152;-0.4487991802;-0.378199858172;-0.305325997695;-0.230615870742;-0.154518792808;-0.0774924206719;0.0
- 1.0;0.996992941168;0.987989849477;0.97304487058;0.952247885338;0.925723969269;0.893632640323;0.85616689953;0.813552070263;0.766044443119;0.713929734558;0.657521368569;0.597158591703;0.533204432802;0.466043519703;0.396079766039;0.323733942058;0.249441144058;0.173648177667;0.0968108707032;0.0193913317718;-0.0581448289105;-0.13533129975;-0.211703872229;-0.286803232711;-0.360177724805;-0.431386065681;-0.5;-0.565606875487;-0.627812124672;-0.686241637869;-0.740544013109;-0.790392669519;-0.835487811413;-0.875558231302;-0.910362940966;-0.939692620786;-0.963370878616;-0.981255310627;-0.993238357742;-0.999247952504;-0.999247952504;-0.993238357742;-0.981255310627;-0.963370878616;-0.939692620786;-0.910362940966;-0.875558231302;-0.835487811413;-0.790392669519;-0.740544013109;-0.686241637869;-0.627812124672;-0.565606875487;-0.5;-0.431386065681;-0.360177724805;-0.286803232711;-0.211703872229;-0.13533129975;-0.0581448289105;0.0193913317718;0.0968108707032;0.173648177667;0.249441144058;0.323733942058;0.396079766039;0.466043519703;0.533204432802;0.597158591703;0.657521368569;0.713929734558;0.766044443119;0.813552070263;0.85616689953;0.893632640323;0.925723969269;0.952247885338;0.97304487058;0.987989849477;0.996992941168;1.0
-
-
-
- SuperEllipse
- Normalized SuperEllipse with Ratio
-
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- 0.0;0.0009765625;0.078125;0.15625;0.234375;0.30859375;0.3828125;0.453125;0.5234375;0.58984375;0.650390625;0.708984375;0.76171875;0.810546875;0.85546875;0.892578125;0.92578125;0.953125;0.97412109375;0.98876953125;0.99755859375;0.999999046326;0.996826171875;0.98779296875;0.97265625;0.951171875;0.923828125;0.890625;0.853515625;0.80859375;0.759765625;0.70703125;0.6484375;0.587890625;0.5234375;0.453125;0.3828125;0.30859375;0.234375;0.15625;0.078125;-0.078125;-0.15625;-0.234375;-0.30859375;-0.3828125;-0.453125;-0.5234375;-0.587890625;-0.6484375;-0.70703125;-0.759765625;-0.80859375;-0.853515625;-0.890625;-0.923828125;-0.951171875;-0.97265625;-0.98779296875;-0.996826171875;-0.999999046326;-0.99755859375;-0.98876953125;-0.97412109375;-0.953125;-0.92578125;-0.892578125;-0.85546875;-0.810546875;-0.76171875;-0.708984375;-0.650390625;-0.58984375;-0.5234375;-0.453125;-0.3828125;-0.30859375;-0.234375;-0.15625;-0.078125;-0.0009765625;-0.0
- 1.0;0.999999527931;0.996974135596;0.987840363937;0.97242480175;0.951681993298;0.92458779978;0.892532519209;0.853543431745;0.809442225561;0.762003918485;0.708171946587;0.651428743848;0.589817031084;0.522675892489;0.456383843105;0.384279024944;0.309551055683;0.233766489061;0.157953882246;0.0791361170447;-0.00138106760273;-0.0796089383489;-0.155772433017;-0.232249476503;-0.308661731041;-0.382807517506;-0.454738506589;-0.521067248904;-0.588367357576;-0.650197043263;-0.707182304306;-0.761267895418;-0.808940426136;-0.85206407247;-0.8914469891;-0.923826060384;-0.951193932624;-0.972146264394;-0.98771753933;-0.996943571309;-0.996943571309;-0.98771753933;-0.972146264394;-0.951193932624;-0.923826060384;-0.8914469891;-0.85206407247;-0.808940426136;-0.761267895418;-0.707182304306;-0.650197043263;-0.588367357576;-0.521067248904;-0.454738506589;-0.382807517506;-0.308661731041;-0.232249476503;-0.155772433017;-0.0796089383489;-0.00138106760273;0.0791361170447;0.157953882246;0.233766489061;0.309551055683;0.384279024944;0.456383843105;0.522675892489;0.589817031084;0.651428743848;0.708171946587;0.762003918485;0.809442225561;0.853543431745;0.892532519209;0.92458779978;0.951681993298;0.97242480175;0.987840363937;0.996974135596;0.999999527931;1.0
-
-
-
- SuperEllipse
- Normalized SuperEllipse with Ratio
-
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- 0.0;0.0009765625;0.078125;0.15625;0.234375;0.310546875;0.38671875;0.45703125;0.52734375;0.59765625;0.66015625;0.720703125;0.77734375;0.828125;0.873046875;0.912109375;0.943359375;0.96875;0.98681640625;0.9970703125;1.0;0.999999046326;0.996826171875;0.98779296875;0.97265625;0.951171875;0.923828125;0.890625;0.853515625;0.80859375;0.759765625;0.70703125;0.6484375;0.587890625;0.5234375;0.453125;0.3828125;0.30859375;0.234375;0.15625;0.078125;-0.078125;-0.15625;-0.234375;-0.30859375;-0.3828125;-0.453125;-0.5234375;-0.587890625;-0.6484375;-0.70703125;-0.759765625;-0.80859375;-0.853515625;-0.890625;-0.923828125;-0.951171875;-0.97265625;-0.98779296875;-0.996826171875;-0.999999046326;-1.0;-0.9970703125;-0.98681640625;-0.96875;-0.943359375;-0.912109375;-0.873046875;-0.828125;-0.77734375;-0.720703125;-0.66015625;-0.59765625;-0.52734375;-0.45703125;-0.38671875;-0.310546875;-0.234375;-0.15625;-0.078125;-0.0009765625;-0.0
- 1.0;0.999999566078;0.997218649891;0.98882296079;0.974653100598;0.955007840821;0.929199912187;0.899400080112;0.863183345829;0.819594738441;0.77352675453;0.720851883844;0.662459218268;0.600094559823;0.533749026445;0.463051709209;0.391913109184;0.315715658725;0.237277758921;0.159606327286;0.078125;-0.00138106760273;-0.0796089383489;-0.155772433017;-0.232249476503;-0.308661731041;-0.382807517506;-0.454738506589;-0.521067248904;-0.588367357576;-0.650197043263;-0.707182304306;-0.761267895418;-0.808940426136;-0.85206407247;-0.8914469891;-0.923826060384;-0.951193932624;-0.972146264394;-0.98771753933;-0.996943571309;-0.996943571309;-0.98771753933;-0.972146264394;-0.951193932624;-0.923826060384;-0.8914469891;-0.85206407247;-0.808940426136;-0.761267895418;-0.707182304306;-0.650197043263;-0.588367357576;-0.521067248904;-0.454738506589;-0.382807517506;-0.308661731041;-0.232249476503;-0.155772433017;-0.0796089383489;-0.00138106760273;0.078125;0.159606327286;0.237277758921;0.315715658725;0.391913109184;0.463051709209;0.533749026445;0.600094559823;0.662459218268;0.720851883844;0.77352675453;0.819594738441;0.863183345829;0.899400080112;0.929199912187;0.955007840821;0.974653100598;0.98882296079;0.997218649891;0.999999566078;1.0
-
-
-
- SuperEllipse
- Normalized SuperEllipse with Ratio
-
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- 0.0;0.0009765625;0.078125;0.15625;0.234375;0.3125;0.38671875;0.4609375;0.53125;0.6015625;0.666015625;0.7265625;0.78515625;0.8359375;0.880859375;0.9208984375;0.953125;0.9765625;0.9921875;0.99951171875;1.0;1.0;0.998291015625;0.990234375;0.9755859375;0.955078125;0.927734375;0.8955078125;0.857421875;0.8125;0.763671875;0.7109375;0.65234375;0.58984375;0.5234375;0.455078125;0.3828125;0.30859375;0.234375;0.15625;0.078125;-0.078125;-0.15625;-0.234375;-0.30859375;-0.3828125;-0.455078125;-0.5234375;-0.58984375;-0.65234375;-0.7109375;-0.763671875;-0.8125;-0.857421875;-0.8955078125;-0.927734375;-0.955078125;-0.9755859375;-0.990234375;-0.998291015625;-1.0;-1.0;-0.99951171875;-0.9921875;-0.9765625;-0.953125;-0.9208984375;-0.880859375;-0.8359375;-0.78515625;-0.7265625;-0.666015625;-0.6015625;-0.53125;-0.4609375;-0.38671875;-0.3125;-0.234375;-0.15625;-0.078125;-0.0009765625;-0.0
- 1.0;0.999999585152;0.997340907039;0.989314259217;0.975767250023;0.95642845085;0.932312003959;0.90206636276;0.867077143075;0.824978391462;0.778965877712;0.727777331485;0.668789014355;0.60747744522;0.541838888111;0.469128262941;0.393241836813;0.317253597862;0.238537389826;0.157184181011;0.078125;0.0;-0.0772696448108;-0.156624381494;-0.235225668175;-0.310427055301;-0.385776292066;-0.456144877013;-0.524321871657;-0.591301967002;-0.652692483555;-0.709190092354;-0.762764771388;-0.811367051565;-0.85502279102;-0.892642484469;-0.925349539176;-0.952170053972;-0.972703339106;-0.987963188543;-0.997004699883;-0.997004699883;-0.987963188543;-0.972703339106;-0.952170053972;-0.925349539176;-0.892642484469;-0.85502279102;-0.811367051565;-0.762764771388;-0.709190092354;-0.652692483555;-0.591301967002;-0.524321871657;-0.456144877013;-0.385776292066;-0.310427055301;-0.235225668175;-0.156624381494;-0.0772696448108;0.0;0.078125;0.157184181011;0.238537389826;0.317253597862;0.393241836813;0.469128262941;0.541838888111;0.60747744522;0.668789014355;0.727777331485;0.778965877712;0.824978391462;0.867077143075;0.90206636276;0.932312003959;0.95642845085;0.975767250023;0.989314259217;0.997340907039;0.999999585152;1.0
-
-
-
- SuperEllipse
- Normalized SuperEllipse with Ratio
-
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- 0.0;0.0009765625;0.078125;0.15625;0.234375;0.3125;0.38671875;0.4609375;0.53125;0.6015625;0.66796875;0.728515625;0.78515625;0.837890625;0.884765625;0.923828125;0.955078125;0.978515625;0.99365234375;0.999755859375;1.0;1.0;0.999145507813;0.99267578125;0.9794921875;0.958984375;0.9326171875;0.900390625;0.861328125;0.81640625;0.767578125;0.712890625;0.654296875;0.591796875;0.525390625;0.45703125;0.3828125;0.30859375;0.234375;0.15625;0.078125;-0.078125;-0.15625;-0.234375;-0.30859375;-0.3828125;-0.45703125;-0.525390625;-0.591796875;-0.654296875;-0.712890625;-0.767578125;-0.81640625;-0.861328125;-0.900390625;-0.9326171875;-0.958984375;-0.9794921875;-0.99267578125;-0.999145507813;-1.0;-1.0;-0.999755859375;-0.99365234375;-0.978515625;-0.955078125;-0.923828125;-0.884765625;-0.837890625;-0.78515625;-0.728515625;-0.66796875;-0.6015625;-0.53125;-0.4609375;-0.38671875;-0.3125;-0.234375;-0.15625;-0.078125;-0.0009765625;-0.0
- 1.0;0.99999958992;0.997371471326;0.989437083824;0.976045787379;0.956929273254;0.933090026902;0.903192036752;0.868604992005;0.826990134089;0.780002818101;0.729125098993;0.672596037179;0.609420820456;0.540791177311;0.469214465055;0.394864558733;0.317308499904;0.236745297636;0.159002334828;0.078125;0.0;-0.0796777908584;-0.155976555922;-0.233422858897;-0.312120578742;-0.386432572579;-0.457679134474;-0.52772711056;-0.594378875409;-0.655317181927;-0.71322422667;-0.765988281155;-0.813843643364;-0.856826700944;-0.893872611986;-0.926873017968;-0.953146175319;-0.973260413818;-0.988208837757;-0.997065828457;-0.997065828457;-0.988208837757;-0.973260413818;-0.953146175319;-0.926873017968;-0.893872611986;-0.856826700944;-0.813843643364;-0.765988281155;-0.71322422667;-0.655317181927;-0.594378875409;-0.52772711056;-0.457679134474;-0.386432572579;-0.312120578742;-0.233422858897;-0.155976555922;-0.0796777908584;0.0;0.078125;0.159002334828;0.236745297636;0.317308499904;0.394864558733;0.469214465055;0.540791177311;0.609420820456;0.672596037179;0.729125098993;0.780002818101;0.826990134089;0.868604992005;0.903192036752;0.933090026902;0.956929273254;0.976045787379;0.989437083824;0.997371471326;0.99999958992;1.0
-
-
-
- SuperEllipse
- Normalized SuperEllipse with Ratio
-
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- 0.0;0.0009765625;0.078125;0.15625;0.234375;0.3125;0.38671875;0.4609375;0.53125;0.6015625;0.66796875;0.728515625;0.78515625;0.837890625;0.884765625;0.923828125;0.955078125;0.978515625;0.99365234375;0.999755859375;1.0;1.0;0.99951171875;0.99365234375;0.98046875;0.9609375;0.9345703125;0.90234375;0.86328125;0.818359375;0.76953125;0.71484375;0.65625;0.59375;0.525390625;0.45703125;0.3828125;0.30859375;0.234375;0.15625;0.078125;-0.078125;-0.15625;-0.234375;-0.30859375;-0.3828125;-0.45703125;-0.525390625;-0.59375;-0.65625;-0.71484375;-0.76953125;-0.818359375;-0.86328125;-0.90234375;-0.9345703125;-0.9609375;-0.98046875;-0.99365234375;-0.99951171875;-1.0;-1.0;-0.999755859375;-0.99365234375;-0.978515625;-0.955078125;-0.923828125;-0.884765625;-0.837890625;-0.78515625;-0.728515625;-0.66796875;-0.6015625;-0.53125;-0.4609375;-0.38671875;-0.3125;-0.234375;-0.15625;-0.078125;-0.0009765625;-0.0
- 1.0;0.99999958992;0.997371471326;0.989437083824;0.976045787379;0.956929273254;0.933090026902;0.903192036752;0.868604992005;0.826990134089;0.780002818101;0.729125098993;0.672596037179;0.609420820456;0.540791177311;0.469214465055;0.394864558733;0.317308499904;0.236745297636;0.159002334828;0.078125;0.0;-0.0796838758164;-0.156869805528;-0.236841188772;-0.312927189136;-0.387989561557;-0.459466263011;-0.529487016265;-0.595971482633;-0.656678699053;-0.714320004072;-0.766816352767;-0.814417095632;-0.858318089476;-0.894978105611;-0.927634757364;-0.953634235993;-0.973538951174;-0.988331662363;-0.997096392744;-0.997096392744;-0.988331662363;-0.973538951174;-0.953634235993;-0.927634757364;-0.894978105611;-0.858318089476;-0.814417095632;-0.766816352767;-0.714320004072;-0.656678699053;-0.595971482633;-0.529487016265;-0.459466263011;-0.387989561557;-0.312927189136;-0.236841188772;-0.156869805528;-0.0796838758164;0.0;0.078125;0.159002334828;0.236745297636;0.317308499904;0.394864558733;0.469214465055;0.540791177311;0.609420820456;0.672596037179;0.729125098993;0.780002818101;0.826990134089;0.868604992005;0.903192036752;0.933090026902;0.956929273254;0.976045787379;0.989437083824;0.997371471326;0.99999958992;1.0
-
-
-
- SuperEllipse
- Normalized SuperEllipse with Ratio
-
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- 0.0;0.0009765625;0.078125;0.15625;0.234375;0.3125;0.38671875;0.4609375;0.53125;0.6015625;0.666015625;0.728515625;0.78515625;0.837890625;0.8828125;0.921875;0.953125;0.9775390625;0.99267578125;0.999633789063;1.0;1.0;0.99951171875;0.99365234375;0.98046875;0.9609375;0.9345703125;0.90234375;0.86328125;0.818359375;0.76953125;0.71484375;0.65625;0.59375;0.525390625;0.45703125;0.3828125;0.30859375;0.234375;0.15625;0.078125;-0.078125;-0.15625;-0.234375;-0.30859375;-0.3828125;-0.45703125;-0.525390625;-0.59375;-0.65625;-0.71484375;-0.76953125;-0.818359375;-0.86328125;-0.90234375;-0.9345703125;-0.9609375;-0.98046875;-0.99365234375;-0.99951171875;-1.0;-1.0;-0.999633789063;-0.99267578125;-0.9775390625;-0.953125;-0.921875;-0.8828125;-0.837890625;-0.78515625;-0.728515625;-0.666015625;-0.6015625;-0.53125;-0.4609375;-0.38671875;-0.3125;-0.234375;-0.15625;-0.078125;-0.0009765625;-0.0
- 1.0;0.999999587536;0.997356189183;0.98937567152;0.975906518701;0.956678862052;0.932701015431;0.902629199756;0.86784106754;0.825984262776;0.780236188759;0.727550244917;0.670692525767;0.607150011272;0.541312598467;0.470176600125;0.396728952693;0.317302482466;0.239499709242;0.158407605871;0.078125;0.0;-0.0796838758164;-0.156869805528;-0.236841188772;-0.312927189136;-0.387989561557;-0.459466263011;-0.529487016265;-0.595971482633;-0.656678699053;-0.714320004072;-0.766816352767;-0.814417095632;-0.858318089476;-0.894978105611;-0.927634757364;-0.953634235993;-0.973538951174;-0.988331662363;-0.997096392744;-0.997096392744;-0.988331662363;-0.973538951174;-0.953634235993;-0.927634757364;-0.894978105611;-0.858318089476;-0.814417095632;-0.766816352767;-0.714320004072;-0.656678699053;-0.595971482633;-0.529487016265;-0.459466263011;-0.387989561557;-0.312927189136;-0.236841188772;-0.156869805528;-0.0796838758164;0.0;0.078125;0.158407605871;0.239499709242;0.317302482466;0.396728952693;0.470176600125;0.541312598467;0.607150011272;0.670692525767;0.727550244917;0.780236188759;0.825984262776;0.86784106754;0.902629199756;0.932701015431;0.956678862052;0.975906518701;0.98937567152;0.997356189183;0.999999587536;1.0
-
-
-
- SuperEllipse
- Normalized SuperEllipse with Ratio
-
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- 0.0;0.0009765625;0.078125;0.15625;0.234375;0.310546875;0.38671875;0.4609375;0.53125;0.59765625;0.6640625;0.7265625;0.78125;0.833984375;0.87890625;0.91796875;0.9501953125;0.974609375;0.9912109375;0.9990234375;1.0;1.0;0.99951171875;0.99365234375;0.98046875;0.9609375;0.9345703125;0.90234375;0.86328125;0.818359375;0.76953125;0.71484375;0.65625;0.59375;0.525390625;0.45703125;0.3828125;0.30859375;0.234375;0.15625;0.078125;-0.078125;-0.15625;-0.234375;-0.30859375;-0.3828125;-0.45703125;-0.525390625;-0.59375;-0.65625;-0.71484375;-0.76953125;-0.818359375;-0.86328125;-0.90234375;-0.9345703125;-0.9609375;-0.98046875;-0.99365234375;-0.99951171875;-1.0;-1.0;-0.9990234375;-0.9912109375;-0.974609375;-0.9501953125;-0.91796875;-0.87890625;-0.833984375;-0.78125;-0.7265625;-0.6640625;-0.59765625;-0.53125;-0.4609375;-0.38671875;-0.310546875;-0.234375;-0.15625;-0.078125;-0.0009765625;-0.0
- 1.0;0.999999580383;0.997310342752;0.98919143461;0.975488712667;0.956491098816;0.931533981016;0.900940688769;0.865549294145;0.825542164646;0.77795581871;0.724648335295;0.669312069775;0.60557349208;0.53975520675;0.469054386656;0.394256441053;0.317042525451;0.236415975894;0.158881376964;0.078125;0.0;-0.0796838758164;-0.156869805528;-0.236841188772;-0.312927189136;-0.387989561557;-0.459466263011;-0.529487016265;-0.595971482633;-0.656678699053;-0.714320004072;-0.766816352767;-0.814417095632;-0.858318089476;-0.894978105611;-0.927634757364;-0.953634235993;-0.973538951174;-0.988331662363;-0.997096392744;-0.997096392744;-0.988331662363;-0.973538951174;-0.953634235993;-0.927634757364;-0.894978105611;-0.858318089476;-0.814417095632;-0.766816352767;-0.714320004072;-0.656678699053;-0.595971482633;-0.529487016265;-0.459466263011;-0.387989561557;-0.312927189136;-0.236841188772;-0.156869805528;-0.0796838758164;0.0;0.078125;0.158881376964;0.236415975894;0.317042525451;0.394256441053;0.469054386656;0.53975520675;0.60557349208;0.669312069775;0.724648335295;0.77795581871;0.825542164646;0.865549294145;0.900940688769;0.931533981016;0.956491098816;0.975488712667;0.98919143461;0.997310342752;0.999999580383;1.0
-
-
-
- SuperEllipse
- Normalized SuperEllipse with Ratio
-
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- 0.0;0.0009765625;0.078125;0.15625;0.234375;0.310546875;0.38671875;0.45703125;0.529296875;0.59765625;0.662109375;0.72265625;0.77734375;0.830078125;0.875;0.9140625;0.9462890625;0.970703125;0.98828125;0.997802734375;1.0;1.0;0.99951171875;0.99365234375;0.98046875;0.9609375;0.9345703125;0.90234375;0.86328125;0.818359375;0.76953125;0.71484375;0.65625;0.59375;0.525390625;0.45703125;0.3828125;0.30859375;0.234375;0.15625;0.078125;-0.078125;-0.15625;-0.234375;-0.30859375;-0.3828125;-0.45703125;-0.525390625;-0.59375;-0.65625;-0.71484375;-0.76953125;-0.818359375;-0.86328125;-0.90234375;-0.9345703125;-0.9609375;-0.98046875;-0.99365234375;-0.99951171875;-1.0;-1.0;-0.997802734375;-0.98828125;-0.970703125;-0.9462890625;-0.9140625;-0.875;-0.830078125;-0.77734375;-0.72265625;-0.662109375;-0.59765625;-0.529296875;-0.45703125;-0.38671875;-0.310546875;-0.234375;-0.15625;-0.078125;-0.0009765625;-0.0
- 1.0;0.999999570846;0.997249214178;0.988945785397;0.974931637955;0.955502260153;0.92997793513;0.900505573737;0.863593021624;0.821577213843;0.774466494487;0.722086838723;0.666168457627;0.601882404734;0.535710626448;0.465016019287;0.390989653177;0.316253806726;0.237379541518;0.159629352267;0.078125;0.0;-0.0796838758164;-0.156869805528;-0.236841188772;-0.312927189136;-0.387989561557;-0.459466263011;-0.529487016265;-0.595971482633;-0.656678699053;-0.714320004072;-0.766816352767;-0.814417095632;-0.858318089476;-0.894978105611;-0.927634757364;-0.953634235993;-0.973538951174;-0.988331662363;-0.997096392744;-0.997096392744;-0.988331662363;-0.973538951174;-0.953634235993;-0.927634757364;-0.894978105611;-0.858318089476;-0.814417095632;-0.766816352767;-0.714320004072;-0.656678699053;-0.595971482633;-0.529487016265;-0.459466263011;-0.387989561557;-0.312927189136;-0.236841188772;-0.156869805528;-0.0796838758164;0.0;0.078125;0.159629352267;0.237379541518;0.316253806726;0.390989653177;0.465016019287;0.535710626448;0.601882404734;0.666168457627;0.722086838723;0.774466494487;0.821577213843;0.863593021624;0.900505573737;0.92997793513;0.955502260153;0.974931637955;0.988945785397;0.997249214178;0.999999570846;1.0
-
-
-
- SuperEllipse
- Normalized SuperEllipse with Ratio
-
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- 0.0;0.0009765625;0.078125;0.15625;0.234375;0.30859375;0.3828125;0.45703125;0.525390625;0.59375;0.65625;0.71484375;0.76953125;0.818359375;0.86328125;0.90234375;0.9345703125;0.9609375;0.98046875;0.99365234375;0.99951171875;1.0;0.999145507813;0.99267578125;0.9794921875;0.958984375;0.9326171875;0.900390625;0.861328125;0.81640625;0.767578125;0.712890625;0.654296875;0.591796875;0.525390625;0.45703125;0.3828125;0.30859375;0.234375;0.15625;0.078125;-0.078125;-0.15625;-0.234375;-0.30859375;-0.3828125;-0.45703125;-0.525390625;-0.591796875;-0.654296875;-0.712890625;-0.767578125;-0.81640625;-0.861328125;-0.900390625;-0.9326171875;-0.958984375;-0.9794921875;-0.99267578125;-0.999145507813;-1.0;-0.99951171875;-0.99365234375;-0.98046875;-0.9609375;-0.9345703125;-0.90234375;-0.86328125;-0.818359375;-0.76953125;-0.71484375;-0.65625;-0.59375;-0.525390625;-0.45703125;-0.3828125;-0.30859375;-0.234375;-0.15625;-0.078125;-0.0009765625;-0.0
- 1.0;0.999999547005;0.997096392744;0.988331662363;0.973538951174;0.953634235993;0.927634757364;0.894978105611;0.858318089476;0.814417095632;0.766816352767;0.714320004072;0.656678699053;0.595971482633;0.529487016265;0.459466263011;0.387989561557;0.312927189136;0.236841188772;0.156869805528;0.0796838758164;0.0;-0.0796777908584;-0.155976555922;-0.233422858897;-0.312120578742;-0.386432572579;-0.457679134474;-0.52772711056;-0.594378875409;-0.655317181927;-0.71322422667;-0.765988281155;-0.813843643364;-0.856826700944;-0.893872611986;-0.926873017968;-0.953146175319;-0.973260413818;-0.988208837757;-0.997065828457;-0.997065828457;-0.988208837757;-0.973260413818;-0.953146175319;-0.926873017968;-0.893872611986;-0.856826700944;-0.813843643364;-0.765988281155;-0.71322422667;-0.655317181927;-0.594378875409;-0.52772711056;-0.457679134474;-0.386432572579;-0.312120578742;-0.233422858897;-0.155976555922;-0.0796777908584;0.0;0.0796838758164;0.156869805528;0.236841188772;0.312927189136;0.387989561557;0.459466263011;0.529487016265;0.595971482633;0.656678699053;0.714320004072;0.766816352767;0.814417095632;0.858318089476;0.894978105611;0.927634757364;0.953634235993;0.973538951174;0.988331662363;0.997096392744;0.999999547005;1.0
-
-
-
- SuperEllipse
- Normalized SuperEllipse with Ratio
-
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- 0.0;0.0009765625;0.078125;0.15625;0.234375;0.30859375;0.3828125;0.453125;0.5234375;0.58984375;0.650390625;0.708984375;0.76171875;0.810546875;0.85546875;0.892578125;0.92578125;0.953125;0.97412109375;0.98876953125;0.99755859375;1.0;0.99755859375;0.98876953125;0.97412109375;0.953125;0.92578125;0.892578125;0.85546875;0.810546875;0.76171875;0.708984375;0.650390625;0.58984375;0.5234375;0.453125;0.3828125;0.30859375;0.234375;0.15625;0.078125;-0.078125;-0.15625;-0.234375;-0.30859375;-0.3828125;-0.453125;-0.5234375;-0.58984375;-0.650390625;-0.708984375;-0.76171875;-0.810546875;-0.85546875;-0.892578125;-0.92578125;-0.953125;-0.97412109375;-0.98876953125;-0.99755859375;-1.0;-0.99755859375;-0.98876953125;-0.97412109375;-0.953125;-0.92578125;-0.892578125;-0.85546875;-0.810546875;-0.76171875;-0.708984375;-0.650390625;-0.58984375;-0.5234375;-0.453125;-0.3828125;-0.30859375;-0.234375;-0.15625;-0.078125;-0.0009765625;-0.0
- 1.0;0.999999527931;0.996974135596;0.987840363937;0.97242480175;0.951681993298;0.92458779978;0.892532519209;0.853543431745;0.809442225561;0.762003918485;0.708171946587;0.651428743848;0.589817031084;0.522675892489;0.456383843105;0.384279024944;0.309551055683;0.233766489061;0.157953882246;0.0791361170447;0.0;-0.0791361170447;-0.157953882246;-0.233766489061;-0.309551055683;-0.384279024944;-0.456383843105;-0.522675892489;-0.589817031084;-0.651428743848;-0.708171946587;-0.762003918485;-0.809442225561;-0.853543431745;-0.892532519209;-0.92458779978;-0.951681993298;-0.97242480175;-0.987840363937;-0.996974135596;-0.996974135596;-0.987840363937;-0.97242480175;-0.951681993298;-0.92458779978;-0.892532519209;-0.853543431745;-0.809442225561;-0.762003918485;-0.708171946587;-0.651428743848;-0.589817031084;-0.522675892489;-0.456383843105;-0.384279024944;-0.309551055683;-0.233766489061;-0.157953882246;-0.0791361170447;0.0;0.0791361170447;0.157953882246;0.233766489061;0.309551055683;0.384279024944;0.456383843105;0.522675892489;0.589817031084;0.651428743848;0.708171946587;0.762003918485;0.809442225561;0.853543431745;0.892532519209;0.92458779978;0.951681993298;0.97242480175;0.987840363937;0.996974135596;0.999999527931;1.0
-
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec1_Elem1
- profile12
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec2_Elem1
- profile13
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec3_Elem1
- profile14
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec4_Elem1
- profile15
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec5_Elem1
- profile16
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec6_Elem1
- profile17
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec7_Elem1
- profile18
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec8_Elem1
- profile19
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec9_Elem1
- profile20
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec10_Elem1
- profile21
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec11_Elem1
- profile22
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec12_Elem1
- profile23
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec13_Elem1
- profile24
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec14_Elem1
- profile25
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec15_Elem1
- profile26
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec16_Elem1
- profile27
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec17_Elem1
- profile28
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec18_Elem1
- profile29
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec19_Elem1
- profile30
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec20_Elem1
- profile31
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec21_Elem1
- profile32
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec22_Elem1
- profile33
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec23_Elem1
- profile34
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec24_Elem1
- profile35
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec25_Elem1
- profile36
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec26_Elem1
- profile37
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec27_Elem1
- profile38
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec28_Elem1
- profile39
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec29_Elem1
- profile40
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec30_Elem1
- profile41
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec31_Elem1
- profile42
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec32_Elem1
- profile43
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec33_Elem1
- profile44
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec34_Elem1
- profile45
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec35_Elem1
- profile46
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec36_Elem1
- profile47
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec37_Elem1
- profile48
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec38_Elem1
- profile49
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec39_Elem1
- profile50
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec40_Elem1
- profile51
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.077492;0.15452;0.23062;0.30533;0.3782;0.4488;0.5167;0.58149;0.64279;0.70022;0.75344;0.80212;0.84599;0.88476;0.91822;0.94615;0.96839;0.98481;0.9953;0.99981;0.99831;0.9908;0.97733;0.95799;0.93288;0.90217;0.86603;0.82468;0.77836;0.72737;0.67201;0.6126;0.54951;0.48311;0.41381;0.34202;0.26817;0.19271;0.11609;0.038775;0;0;-0.038775;-0.11609;-0.19271;-0.26817;-0.34202;-0.41381;-0.48311;-0.54951;-0.6126;-0.67201;-0.72737;-0.77836;-0.82468;-0.86603;-0.90217;-0.93288;-0.95799;-0.97733;-0.9908;-0.99831;-0.99981;-0.9953;-0.98481;-0.96839;-0.94615;-0.91822;-0.88476;-0.84599;-0.80212;-0.75344;-0.70022;-0.64279;-0.58149;-0.5167;-0.4488;-0.3782;-0.30533;-0.23062;-0.15452;-0.077492;0
- 1;0.99699;0.98799;0.97304;0.95225;0.92572;0.89363;0.85617;0.81355;0.76604;0.71393;0.65752;0.59716;0.5332;0.46604;0.39608;0.32373;0.24944;0.17365;0.096811;0.019391;-0.058145;-0.13533;-0.2117;-0.2868;-0.36018;-0.43139;-0.5;-0.56561;-0.62781;-0.68624;-0.74054;-0.79039;-0.83549;-0.87556;-0.91036;-0.93969;-0.96337;-0.98126;-0.99324;-0.99925;-1;-1;-0.99925;-0.99324;-0.98126;-0.96337;-0.93969;-0.91036;-0.87556;-0.83549;-0.79039;-0.74054;-0.68624;-0.62781;-0.56561;-0.5;-0.43139;-0.36018;-0.2868;-0.2117;-0.13533;-0.058145;0.019391;0.096811;0.17365;0.24944;0.32373;0.39608;0.46604;0.5332;0.59716;0.65752;0.71393;0.76604;0.81355;0.85617;0.89363;0.92572;0.95225;0.97304;0.98799;0.99699;1
-
- B777_FL1_Sec41_Elem1
- profile52
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.00097656;0.078125;0.15625;0.23828;0.31641;0.39453;0.46875;0.54297;0.61133;0.67578;0.73438;0.78711;0.83398;0.87305;0.9082;0.93652;0.95996;0.97754;0.99023;0.99756;1;0.99756;0.99023;0.97754;0.95996;0.93652;0.9082;0.87305;0.83398;0.78711;0.73438;0.67578;0.61133;0.54297;0.46875;0.39453;0.31641;0.23828;0.15625;0.078125;0;0;-0.078125;-0.15625;-0.23828;-0.31641;-0.39453;-0.46875;-0.54297;-0.61133;-0.67578;-0.73438;-0.78711;-0.83398;-0.87305;-0.9082;-0.93652;-0.95996;-0.97754;-0.99023;-0.99756;-1;-0.99756;-0.99023;-0.97754;-0.95996;-0.93652;-0.9082;-0.87305;-0.83398;-0.78711;-0.73438;-0.67578;-0.61133;-0.54297;-0.46875;-0.39453;-0.31641;-0.23828;-0.15625;-0.078125;-0.00097656;0
- 1;1;0.99915;0.99516;0.98604;0.97144;0.94986;0.92172;0.88474;0.84131;0.7903;0.73337;0.67108;0.60401;0.53648;0.46253;0.38887;0.31163;0.23497;0.15568;0.078053;-0.0015441;-0.078053;-0.15568;-0.23497;-0.31163;-0.38887;-0.46253;-0.53648;-0.60401;-0.67108;-0.73337;-0.7903;-0.84131;-0.88474;-0.92172;-0.94986;-0.97144;-0.98604;-0.99516;-0.99915;-1;-1;-0.99915;-0.99516;-0.98604;-0.97144;-0.94986;-0.92172;-0.88474;-0.84131;-0.7903;-0.73337;-0.67108;-0.60401;-0.53648;-0.46253;-0.38887;-0.31163;-0.23497;-0.15568;-0.078053;-0.0015441;0.078053;0.15568;0.23497;0.31163;0.38887;0.46253;0.53648;0.60401;0.67108;0.73337;0.7903;0.84131;0.88474;0.92172;0.94986;0.97144;0.98604;0.99516;0.99915;1;1
-
- B777_FL1_Sec42_Elem1
- profile53
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.00097656;0.078125;0.15625;0.23828;0.31641;0.39453;0.46875;0.54297;0.61133;0.67578;0.73438;0.78711;0.83398;0.87305;0.9082;0.93652;0.95996;0.97754;0.99023;0.99756;1;0.99756;0.99023;0.97754;0.95996;0.93652;0.9082;0.87305;0.83398;0.78711;0.73438;0.67578;0.61133;0.54297;0.46875;0.39453;0.31641;0.23828;0.15625;0.078125;0;0;-0.078125;-0.15625;-0.23828;-0.31641;-0.39453;-0.46875;-0.54297;-0.61133;-0.67578;-0.73438;-0.78711;-0.83398;-0.87305;-0.9082;-0.93652;-0.95996;-0.97754;-0.99023;-0.99756;-1;-0.99756;-0.99023;-0.97754;-0.95996;-0.93652;-0.9082;-0.87305;-0.83398;-0.78711;-0.73438;-0.67578;-0.61133;-0.54297;-0.46875;-0.39453;-0.31641;-0.23828;-0.15625;-0.078125;-0.00097656;0
- 1;1;0.99915;0.99516;0.98604;0.97144;0.94986;0.92172;0.88474;0.84131;0.7903;0.73337;0.67108;0.60401;0.53648;0.46253;0.38887;0.31163;0.23497;0.15568;0.078053;-0.0015441;-0.078053;-0.15568;-0.23497;-0.31163;-0.38887;-0.46253;-0.53648;-0.60401;-0.67108;-0.73337;-0.7903;-0.84131;-0.88474;-0.92172;-0.94986;-0.97144;-0.98604;-0.99516;-0.99915;-1;-1;-0.99915;-0.99516;-0.98604;-0.97144;-0.94986;-0.92172;-0.88474;-0.84131;-0.7903;-0.73337;-0.67108;-0.60401;-0.53648;-0.46253;-0.38887;-0.31163;-0.23497;-0.15568;-0.078053;-0.0015441;0.078053;0.15568;0.23497;0.31163;0.38887;0.46253;0.53648;0.60401;0.67108;0.73337;0.7903;0.84131;0.88474;0.92172;0.94986;0.97144;0.98604;0.99516;0.99915;1;1
-
- B777_FL1_Sec43_Elem1
- profile54
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.00097656;0.078125;0.1582;0.23828;0.32031;0.40234;0.48047;0.55859;0.62891;0.69531;0.75391;0.80664;0.85156;0.88867;0.91992;0.94629;0.96582;0.98096;0.9917;0.99792;1;0.99792;0.9917;0.98096;0.96582;0.94629;0.91992;0.88867;0.85156;0.80664;0.75391;0.69531;0.62891;0.55859;0.48047;0.40234;0.32031;0.23828;0.1582;0.078125;0;0;-0.078125;-0.1582;-0.23828;-0.32031;-0.40234;-0.48047;-0.55859;-0.62891;-0.69531;-0.75391;-0.80664;-0.85156;-0.88867;-0.91992;-0.94629;-0.96582;-0.98096;-0.9917;-0.99792;-1;-0.99792;-0.9917;-0.98096;-0.96582;-0.94629;-0.91992;-0.88867;-0.85156;-0.80664;-0.75391;-0.69531;-0.62891;-0.55859;-0.48047;-0.40234;-0.32031;-0.23828;-0.1582;-0.078125;-0.00097656;0
- 1;1;0.99976;0.99802;0.99321;0.98343;0.96689;0.94291;0.90868;0.86675;0.81477;0.75598;0.68931;0.61845;0.54606;0.47065;0.39068;0.31476;0.23674;0.15715;0.078821;-0.0016915;-0.078821;-0.15715;-0.23674;-0.31476;-0.39068;-0.47065;-0.54606;-0.61845;-0.68931;-0.75598;-0.81477;-0.86675;-0.90868;-0.94291;-0.96689;-0.98343;-0.99321;-0.99802;-0.99976;-1;-1;-0.99976;-0.99802;-0.99321;-0.98343;-0.96689;-0.94291;-0.90868;-0.86675;-0.81477;-0.75598;-0.68931;-0.61845;-0.54606;-0.47065;-0.39068;-0.31476;-0.23674;-0.15715;-0.078821;-0.0016915;0.078821;0.15715;0.23674;0.31476;0.39068;0.47065;0.54606;0.61845;0.68931;0.75598;0.81477;0.86675;0.90868;0.94291;0.96689;0.98343;0.99321;0.99802;0.99976;1;1
-
- B777_FL1_Sec44_Elem1
- profile55
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.00097656;0.078125;0.1582;0.23828;0.32031;0.40234;0.48047;0.55859;0.62891;0.69531;0.75391;0.80664;0.85156;0.88867;0.91992;0.94629;0.96582;0.98096;0.9917;0.99792;1;0.99792;0.9917;0.98096;0.96582;0.94629;0.91992;0.88867;0.85156;0.80664;0.75391;0.69531;0.62891;0.55859;0.48047;0.40234;0.32031;0.23828;0.1582;0.078125;0;0;-0.078125;-0.1582;-0.23828;-0.32031;-0.40234;-0.48047;-0.55859;-0.62891;-0.69531;-0.75391;-0.80664;-0.85156;-0.88867;-0.91992;-0.94629;-0.96582;-0.98096;-0.9917;-0.99792;-1;-0.99792;-0.9917;-0.98096;-0.96582;-0.94629;-0.91992;-0.88867;-0.85156;-0.80664;-0.75391;-0.69531;-0.62891;-0.55859;-0.48047;-0.40234;-0.32031;-0.23828;-0.1582;-0.078125;-0.00097656;0
- 1;1;0.99976;0.99802;0.99321;0.98343;0.96689;0.94291;0.90868;0.86675;0.81477;0.75598;0.68931;0.61845;0.54606;0.47065;0.39068;0.31476;0.23674;0.15715;0.078821;-0.0016915;-0.078821;-0.15715;-0.23674;-0.31476;-0.39068;-0.47065;-0.54606;-0.61845;-0.68931;-0.75598;-0.81477;-0.86675;-0.90868;-0.94291;-0.96689;-0.98343;-0.99321;-0.99802;-0.99976;-1;-1;-0.99976;-0.99802;-0.99321;-0.98343;-0.96689;-0.94291;-0.90868;-0.86675;-0.81477;-0.75598;-0.68931;-0.61845;-0.54606;-0.47065;-0.39068;-0.31476;-0.23674;-0.15715;-0.078821;-0.0016915;0.078821;0.15715;0.23674;0.31476;0.39068;0.47065;0.54606;0.61845;0.68931;0.75598;0.81477;0.86675;0.90868;0.94291;0.96689;0.98343;0.99321;0.99802;0.99976;1;1
-
- B777_FL1_Sec45_Elem1
- profile56
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.00097656;0.078125;0.1582;0.23828;0.32031;0.40234;0.48047;0.55859;0.62891;0.69531;0.75391;0.80664;0.85156;0.88867;0.91992;0.94629;0.96582;0.98096;0.9917;0.99792;1;0.99792;0.9917;0.98096;0.96582;0.94629;0.91992;0.88867;0.85156;0.80664;0.75391;0.69531;0.62891;0.55859;0.48047;0.40234;0.32031;0.23828;0.1582;0.078125;0;0;-0.078125;-0.1582;-0.23828;-0.32031;-0.40234;-0.48047;-0.55859;-0.62891;-0.69531;-0.75391;-0.80664;-0.85156;-0.88867;-0.91992;-0.94629;-0.96582;-0.98096;-0.9917;-0.99792;-1;-0.99792;-0.9917;-0.98096;-0.96582;-0.94629;-0.91992;-0.88867;-0.85156;-0.80664;-0.75391;-0.69531;-0.62891;-0.55859;-0.48047;-0.40234;-0.32031;-0.23828;-0.1582;-0.078125;-0.00097656;0
- 1;1;0.99976;0.99802;0.99321;0.98343;0.96689;0.94291;0.90868;0.86675;0.81477;0.75598;0.68931;0.61845;0.54606;0.47065;0.39068;0.31476;0.23674;0.15715;0.078821;-0.0016915;-0.078821;-0.15715;-0.23674;-0.31476;-0.39068;-0.47065;-0.54606;-0.61845;-0.68931;-0.75598;-0.81477;-0.86675;-0.90868;-0.94291;-0.96689;-0.98343;-0.99321;-0.99802;-0.99976;-1;-1;-0.99976;-0.99802;-0.99321;-0.98343;-0.96689;-0.94291;-0.90868;-0.86675;-0.81477;-0.75598;-0.68931;-0.61845;-0.54606;-0.47065;-0.39068;-0.31476;-0.23674;-0.15715;-0.078821;-0.0016915;0.078821;0.15715;0.23674;0.31476;0.39068;0.47065;0.54606;0.61845;0.68931;0.75598;0.81477;0.86675;0.90868;0.94291;0.96689;0.98343;0.99321;0.99802;0.99976;1;1
-
- B777_FL1_Sec46_Elem1
- profile57
-
-
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;0.00097656;0.078125;0.1582;0.23828;0.32031;0.40234;0.48047;0.55859;0.62891;0.69531;0.75391;0.80664;0.85156;0.88867;0.91992;0.94629;0.96582;0.98096;0.9917;0.99792;1;0.99792;0.9917;0.98096;0.96582;0.94629;0.91992;0.88867;0.85156;0.80664;0.75391;0.69531;0.62891;0.55859;0.48047;0.40234;0.32031;0.23828;0.1582;0.078125;0;0;-0.078125;-0.1582;-0.23828;-0.32031;-0.40234;-0.48047;-0.55859;-0.62891;-0.69531;-0.75391;-0.80664;-0.85156;-0.88867;-0.91992;-0.94629;-0.96582;-0.98096;-0.9917;-0.99792;-1;-0.99792;-0.9917;-0.98096;-0.96582;-0.94629;-0.91992;-0.88867;-0.85156;-0.80664;-0.75391;-0.69531;-0.62891;-0.55859;-0.48047;-0.40234;-0.32031;-0.23828;-0.1582;-0.078125;-0.00097656;0
- 1;1;0.99976;0.99802;0.99321;0.98343;0.96689;0.94291;0.90868;0.86675;0.81477;0.75598;0.68931;0.61845;0.54606;0.47065;0.39068;0.31476;0.23674;0.15715;0.078821;-0.0016915;-0.078821;-0.15715;-0.23674;-0.31476;-0.39068;-0.47065;-0.54606;-0.61845;-0.68931;-0.75598;-0.81477;-0.86675;-0.90868;-0.94291;-0.96689;-0.98343;-0.99321;-0.99802;-0.99976;-1;-1;-0.99976;-0.99802;-0.99321;-0.98343;-0.96689;-0.94291;-0.90868;-0.86675;-0.81477;-0.75598;-0.68931;-0.61845;-0.54606;-0.47065;-0.39068;-0.31476;-0.23674;-0.15715;-0.078821;-0.0016915;0.078821;0.15715;0.23674;0.31476;0.39068;0.47065;0.54606;0.61845;0.68931;0.75598;0.81477;0.86675;0.90868;0.94291;0.96689;0.98343;0.99321;0.99802;0.99976;1;1
-
- B777_FL1_Sec47_Elem1
- profile58
-
-
-
-
- NACA0006
-
- 1.000000;0.998459;0.993844;0.986185;0.975528;0.961940;0.945503;0.926320;0.904508;0.880203;0.853553;0.824724;0.793893;0.761249;0.726995;0.691342;0.654508;0.616723;0.578217;0.539230;0.500000;0.460770;0.421783;0.383277;0.345492;0.308658;0.273005;0.238751;0.206107;0.175276;0.146447;0.119797;0.095492;0.073680;0.054497;0.038060;0.024472;0.013815;0.006156;0.001541;0.000000;0.001541;0.006156;0.013815;0.024472;0.038060;0.054497;0.073680;0.095492;0.119797;0.146447;0.175276;0.206107;0.238751;0.273005;0.308658;0.345492;0.383277;0.421783;0.460770;0.500000;0.539230;0.578217;0.616723;0.654508;0.691342;0.726995;0.761249;0.793893;0.824724;0.853553;0.880203;0.904508;0.926320;0.945503;0.961940;0.975528;0.986185;0.993844;0.998459;1.000000
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- 0.000000;0.000112;0.000446;0.000995;0.001751;0.002699;0.003825;0.005111;0.006535;0.008079;0.009719;0.011434;0.013202;0.015000;0.016805;0.018594;0.020343;0.022027;0.023621;0.025098;0.026431;0.027592;0.028554;0.029291;0.029778;0.029994;0.029921;0.029544;0.028856;0.027854;0.026541;0.024927;0.023024;0.020853;0.018433;0.015790;0.012947;0.009927;0.006752;0.003438;0.000000;-0.003438;-0.006752;-0.009927;-0.012947;-0.015790;-0.018433;-0.020853;-0.023024;-0.024927;-0.026541;-0.027854;-0.028856;-0.029544;-0.029921;-0.029994;-0.029778;-0.029291;-0.028554;-0.027592;-0.026431;-0.025098;-0.023621;-0.022027;-0.020343;-0.018594;-0.016805;-0.015000;-0.013202;-0.011434;-0.009719;-0.008079;-0.006535;-0.005111;-0.003825;-0.002699;-0.001751;-0.000995;-0.000446;-0.000112;0.000000
-
-
-
- NACA0008
-
- 1.000000;0.998459;0.993844;0.986185;0.975528;0.961940;0.945503;0.926320;0.904508;0.880203;0.853553;0.824724;0.793893;0.761249;0.726995;0.691342;0.654508;0.616723;0.578217;0.539230;0.500000;0.460770;0.421783;0.383277;0.345492;0.308658;0.273005;0.238751;0.206107;0.175276;0.146447;0.119797;0.095492;0.073680;0.054497;0.038060;0.024472;0.013815;0.006156;0.001541;0.000000;0.001541;0.006156;0.013815;0.024472;0.038060;0.054497;0.073680;0.095492;0.119797;0.146447;0.175276;0.206107;0.238751;0.273005;0.308658;0.345492;0.383277;0.421783;0.460770;0.500000;0.539230;0.578217;0.616723;0.654508;0.691342;0.726995;0.761249;0.793893;0.824724;0.853553;0.880203;0.904508;0.926320;0.945503;0.961940;0.975528;0.986185;0.993844;0.998459;1.000000
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- 0.000000;0.000149;0.000594;0.001327;0.002334;0.003599;0.005101;0.006814;0.008714;0.010772;0.012959;0.015246;0.017603;0.020000;0.022407;0.024792;0.027124;0.029370;0.031495;0.033464;0.035241;0.036789;0.038072;0.039055;0.039705;0.039992;0.039894;0.039392;0.038475;0.037139;0.035388;0.033236;0.030699;0.027804;0.024578;0.021053;0.017262;0.013236;0.009002;0.004584;0.000000;-0.004584;-0.009002;-0.013236;-0.017262;-0.021053;-0.024578;-0.027804;-0.030699;-0.033236;-0.035388;-0.037139;-0.038475;-0.039392;-0.039894;-0.039992;-0.039705;-0.039055;-0.038072;-0.036789;-0.035241;-0.033464;-0.031495;-0.029370;-0.027124;-0.024792;-0.022407;-0.020000;-0.017603;-0.015246;-0.012959;-0.010772;-0.008714;-0.006814;-0.005101;-0.003599;-0.002334;-0.001327;-0.000594;-0.000149;0.000000
-
-
-
- NACA0012
-
- 1.000000;0.998459;0.993844;0.986185;0.975528;0.961940;0.945503;0.926320;0.904508;0.880203;0.853553;0.824724;0.793893;0.761249;0.726995;0.691342;0.654508;0.616723;0.578217;0.539230;0.500000;0.460770;0.421783;0.383277;0.345492;0.308658;0.273005;0.238751;0.206107;0.175276;0.146447;0.119797;0.095492;0.073680;0.054497;0.038060;0.024472;0.013815;0.006156;0.001541;0.000000;0.001541;0.006156;0.013815;0.024472;0.038060;0.054497;0.073680;0.095492;0.119797;0.146447;0.175276;0.206107;0.238751;0.273005;0.308658;0.345492;0.383277;0.421783;0.460770;0.500000;0.539230;0.578217;0.616723;0.654508;0.691342;0.726995;0.761249;0.793893;0.824724;0.853553;0.880203;0.904508;0.926320;0.945503;0.961940;0.975528;0.986185;0.993844;0.998459;1.000000
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- 0.000000;0.000224;0.000891;0.001990;0.003501;0.005399;0.007651;0.010221;0.013071;0.016158;0.019438;0.022869;0.026405;0.030000;0.033610;0.037188;0.040686;0.044055;0.047242;0.050196;0.052862;0.055184;0.057108;0.058582;0.059557;0.059988;0.059841;0.059088;0.057712;0.055708;0.053083;0.049854;0.046049;0.041705;0.036867;0.031580;0.025893;0.019854;0.013503;0.006877;0.000000;-0.006877;-0.013503;-0.019854;-0.025893;-0.031580;-0.036867;-0.041705;-0.046049;-0.049854;-0.053083;-0.055708;-0.057712;-0.059088;-0.059841;-0.059988;-0.059557;-0.058582;-0.057108;-0.055184;-0.052862;-0.050196;-0.047242;-0.044055;-0.040686;-0.037188;-0.033610;-0.030000;-0.026405;-0.022869;-0.019438;-0.016158;-0.013071;-0.010221;-0.007651;-0.005399;-0.003501;-0.001990;-0.000891;-0.000224;0.000000
-
-
-
- NACA1308
-
- 1.000000;0.998463;0.993861;0.986222;0.975593;0.962037;0.945638;0.926494;0.904723;0.880458;0.853846;0.825050;0.794247;0.761626;0.727386;0.691738;0.654901;0.617102;0.578575;0.539556;0.500288;0.461012;0.421972;0.383410;0.345565;0.308672;0.272765;0.238215;0.205305;0.174247;0.145240;0.118467;0.094098;0.072283;0.053158;0.036837;0.023417;0.012975;0.005569;0.001238;0.000000;0.001845;0.006742;0.014655;0.025527;0.039284;0.055836;0.075076;0.096885;0.121127;0.147653;0.176305;0.206910;0.239287;0.273244;0.308644;0.345418;0.383145;0.421594;0.460529;0.499712;0.538903;0.577860;0.616343;0.654116;0.690946;0.726605;0.760873;0.793538;0.824398;0.853261;0.879948;0.904294;0.926146;0.945369;0.961843;0.975464;0.986148;0.993827;0.998454;1.000000
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- 0.000000;0.000193;0.000769;0.001717;0.003020;0.004656;0.006595;0.008806;0.011254;0.013899;0.016702;0.019623;0.022621;0.025655;0.028683;0.031664;0.034556;0.037320;0.039913;0.042294;0.044424;0.046261;0.047769;0.048913;0.049662;0.049991;0.049812;0.048971;0.047487;0.045396;0.042748;0.039601;0.036020;0.032077;0.027844;0.023394;0.018795;0.014109;0.009389;0.004677;0.000000;-0.004472;-0.008577;-0.012309;-0.015665;-0.018641;-0.021238;-0.023460;-0.025315;-0.026817;-0.027988;-0.028853;-0.029446;-0.029805;-0.029974;-0.029994;-0.029747;-0.029196;-0.028374;-0.027316;-0.026056;-0.024630;-0.023072;-0.021414;-0.019686;-0.017915;-0.016124;-0.014339;-0.012578;-0.010862;-0.009209;-0.007639;-0.006169;-0.004818;-0.003602;-0.002540;-0.001646;-0.000935;-0.000419;-0.000105;0.000000
-
-
-
- NACA1309
-
- 1.000000;0.998463;0.993863;0.986227;0.975601;0.962049;0.945654;0.926516;0.904750;0.880490;0.853883;0.825091;0.794292;0.761673;0.727435;0.691787;0.654950;0.617150;0.578620;0.539597;0.500324;0.461042;0.421996;0.383427;0.345574;0.308674;0.272736;0.238148;0.205204;0.174118;0.145089;0.118301;0.093924;0.072109;0.052991;0.036684;0.023285;0.012870;0.005496;0.001200;0.000000;0.001883;0.006816;0.014760;0.025659;0.039437;0.056003;0.075251;0.097059;0.121293;0.147804;0.176434;0.207010;0.239354;0.273274;0.308642;0.345409;0.383128;0.421570;0.460499;0.499676;0.538862;0.577815;0.616296;0.654067;0.690896;0.726556;0.760826;0.793493;0.824357;0.853224;0.879916;0.904267;0.926124;0.945352;0.961830;0.975456;0.986143;0.993825;0.998454;1.000000
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- 0.000000;0.000212;0.000843;0.001883;0.003312;0.005105;0.007233;0.009658;0.012342;0.015245;0.018322;0.021529;0.024821;0.028154;0.031483;0.034762;0.037947;0.040991;0.043850;0.046477;0.048828;0.050859;0.052528;0.053795;0.054625;0.054990;0.054799;0.053895;0.052295;0.050037;0.047169;0.043752;0.039854;0.035548;0.030912;0.026021;0.020949;0.015760;0.010512;0.005249;0.000000;-0.005044;-0.009700;-0.013961;-0.017819;-0.021268;-0.024306;-0.026931;-0.029148;-0.030968;-0.032409;-0.033493;-0.034254;-0.034729;-0.034961;-0.034993;-0.034710;-0.034078;-0.033133;-0.031914;-0.030461;-0.028813;-0.027009;-0.025085;-0.023076;-0.021013;-0.018925;-0.016838;-0.014778;-0.012767;-0.010829;-0.008985;-0.007258;-0.005669;-0.004240;-0.002990;-0.001938;-0.001101;-0.000493;-0.000124;0.000000
-
-
-
- NACA1310
-
- 1.000000;0.998464;0.993865;0.986231;0.975609;0.962061;0.945671;0.926538;0.904777;0.880522;0.853919;0.825132;0.794336;0.761720;0.727483;0.691837;0.654999;0.617197;0.578664;0.539638;0.500360;0.461072;0.422019;0.383443;0.345584;0.308676;0.272706;0.238081;0.205104;0.173990;0.144938;0.118135;0.093749;0.071934;0.052823;0.036531;0.023153;0.012765;0.005423;0.001162;0.000000;0.001921;0.006889;0.014865;0.025790;0.039589;0.056170;0.075426;0.097234;0.121459;0.147955;0.176562;0.207111;0.239421;0.273304;0.308641;0.345399;0.383111;0.421546;0.460469;0.499640;0.538821;0.577770;0.616248;0.654018;0.690847;0.726507;0.760779;0.793449;0.824316;0.853187;0.879884;0.904240;0.926102;0.945335;0.961818;0.975448;0.986139;0.993823;0.998453;1.000000
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- 0.000000;0.000230;0.000917;0.002049;0.003604;0.005555;0.007870;0.010509;0.013431;0.016591;0.019941;0.023434;0.027021;0.030654;0.034284;0.037861;0.041337;0.044662;0.047786;0.050660;0.053233;0.055458;0.057287;0.058677;0.059588;0.059989;0.059786;0.058818;0.057103;0.054677;0.051590;0.047903;0.043687;0.039019;0.033980;0.028648;0.023102;0.017411;0.011635;0.005821;0.000000;-0.005616;-0.010823;-0.015612;-0.019973;-0.023896;-0.027373;-0.030402;-0.032982;-0.035119;-0.036830;-0.038134;-0.039062;-0.039652;-0.039948;-0.039992;-0.039673;-0.038960;-0.037892;-0.036513;-0.034866;-0.032996;-0.030946;-0.028756;-0.026466;-0.024112;-0.021725;-0.019338;-0.016978;-0.014672;-0.012448;-0.010331;-0.008347;-0.006521;-0.004877;-0.003439;-0.002230;-0.001267;-0.000567;-0.000142;0.000000
-
-
-
- NACA1508
-
- 1.000000;0.998465;0.993868;0.986237;0.975617;0.962073;0.945685;0.926552;0.904790;0.880530;0.853920;0.825120;0.794306;0.761667;0.727402;0.691721;0.654844;0.616997;0.578414;0.539335;0.500000;0.460655;0.421545;0.382913;0.345001;0.308046;0.272280;0.237928;0.205203;0.174312;0.145446;0.118787;0.094499;0.072732;0.053621;0.037283;0.023816;0.013301;0.005800;0.001359;0.000000;0.001724;0.006511;0.014329;0.025128;0.038838;0.055372;0.074628;0.096484;0.120807;0.147447;0.176240;0.207012;0.239574;0.273729;0.309270;0.345982;0.383642;0.422021;0.460886;0.500000;0.539125;0.578020;0.616448;0.654173;0.690962;0.726588;0.760831;0.793479;0.824328;0.853187;0.879875;0.904227;0.926088;0.945322;0.961807;0.975440;0.986133;0.993821;0.998453;1.000000
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- 0.000000;0.000211;0.000838;0.001871;0.003287;0.005061;0.007158;0.009540;0.012164;0.014985;0.017954;0.021023;0.024143;0.027266;0.030342;0.033325;0.036167;0.038823;0.041249;0.043402;0.045241;0.046727;0.047827;0.048508;0.048747;0.048523;0.047826;0.046653;0.045009;0.042908;0.040374;0.037438;0.034138;0.030517;0.026623;0.022503;0.018205;0.013771;0.009240;0.004642;0.000000;-0.004519;-0.008751;-0.012681;-0.016295;-0.019574;-0.022501;-0.025057;-0.027228;-0.029003;-0.030374;-0.031344;-0.031919;-0.032113;-0.031949;-0.031452;-0.030656;-0.029598;-0.028316;-0.026851;-0.025241;-0.023525;-0.021739;-0.019913;-0.018077;-0.016254;-0.014464;-0.012726;-0.011053;-0.009459;-0.007954;-0.006549;-0.005254;-0.004080;-0.003036;-0.002132;-0.001378;-0.000781;-0.000349;-0.000088;0.000000
-
-
-
- NACA2315
-
- 1.000000;0.998475;0.993907;0.986324;0.975769;0.962304;0.946007;0.926972;0.905314;0.881159;0.854650;0.825947;0.795222;0.762660;0.728459;0.692826;0.655980;0.618146;0.579558;0.540455;0.501079;0.461676;0.422492;0.383775;0.345768;0.308711;0.272107;0.236741;0.203100;0.171422;0.141929;0.114822;0.090281;0.068462;0.049498;0.033496;0.020538;0.010684;0.003970;0.000411;0.000000;0.002672;0.008342;0.016946;0.028406;0.042625;0.059495;0.078897;0.100702;0.124772;0.150964;0.179130;0.209115;0.240761;0.273902;0.308605;0.345215;0.382780;0.421073;0.459865;0.498921;0.538004;0.576876;0.615299;0.653037;0.689857;0.725532;0.759838;0.792563;0.823501;0.852457;0.879247;0.903703;0.925668;0.945000;0.961576;0.975287;0.986046;0.993781;0.998443;1.000000
- 0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0;0.0
- 0.000000;0.000367;0.001462;0.003265;0.005744;0.008854;0.012543;0.016749;0.021403;0.026434;0.031766;0.037322;0.043023;0.048790;0.054546;0.060211;0.065707;0.070955;0.075878;0.080397;0.084435;0.087919;0.090776;0.092943;0.094361;0.094982;0.094634;0.092999;0.090118;0.086071;0.080960;0.074902;0.068031;0.060488;0.052418;0.043963;0.035256;0.026419;0.017549;0.008726;0.000000;-0.008316;-0.015925;-0.022819;-0.028997;-0.034457;-0.039205;-0.043252;-0.046619;-0.049334;-0.051439;-0.052985;-0.054036;-0.054666;-0.054958;-0.054989;-0.054530;-0.053509;-0.051987;-0.050029;-0.047701;-0.045069;-0.042197;-0.039144;-0.035966;-0.032713;-0.029429;-0.026158;-0.022935;-0.019798;-0.016780;-0.013915;-0.011234;-0.008771;-0.006557;-0.004623;-0.002996;-0.001702;-0.000762;-0.000191;0.000000
-
-
-
-
-
-
-
- 800
-
-
-
-
- Engine1
-
-
- 8016
-
- 446.568
-
-
-
- Engine2
-
-
- 8016
-
- 446.568
-
-
-
-
-
-
-
- 6
- 0
-
-
- User geometry input
- 1
- 0.00014263
- 2700
- 2.3
-
- 1990.0
- 426.794
- 60.9421
-
-
-
- 2.48264
- 272
- 12000
- 0.78
- 1.05
-
-
-
-
- 2
- 102
-
-
- 68
- 9
-
-
-
- 105
- 1904889488
- 50
- 433
- 45465
- 9
-
- Maximum amount of fuel with maximum payload [kg]
- 118451
-
-
-
- Desired max fuel volume [m^3] and payload mass [kg]
- 0
- 0
-
-
-
-
- 0.5
-
- False
- False
-
-
-
- aeroMap_SU2
- 0.5
- NO
-
- 100
-
-
-
-
-
-
-
- aeroMap_pyTornado;
- False
-
-
-
-
- aeroMap_pyTornado
- 5
- 2
-
-
- False
- False
-
-
- False
- False
-
-
- False
- False
-
-
- False
- False
-
-
-
- False
- False
- False
-
-
-
-
diff --git a/ceasiompy/PyTornado/tests/test_pytornado.py b/ceasiompy/PyTornado/tests/test_pytornado.py
deleted file mode 100644
index ed2816f4e..000000000
--- a/ceasiompy/PyTornado/tests/test_pytornado.py
+++ /dev/null
@@ -1,86 +0,0 @@
-"""
-CEASIOMpy: Conceptual Aircraft Design Software
-
-Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-
-Test functions for 'ceasiompy/PyTornado/runpytornado.py'
-
-Python version: >=3.8
-
-| Author : Aaron Dettmann
-| Creation: 2019-09-11
-
-"""
-
-# =================================================================================================
-# IMPORTS
-# =================================================================================================
-
-import importlib
-import os
-import shutil
-from contextlib import contextmanager
-from pathlib import Path
-
-import pytest
-
-PYTORNADO_MAIN_MODULE = "ceasiompy.PyTornado.runpytornado"
-
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
-CPACS_TEST_FILE = Path(MODULE_DIR, "cpacs_test_file.xml")
-
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
-
-
-@contextmanager
-def run_module_test_locally(module_name, test_dir):
- """Context manager which copies a single module
-
- Args:
- module_name (str): Full 'path' of module which is to be loaded
- test_dir (Path): Dictionary in which we run the local test
- """
-
- module = importlib.import_module(module_name)
- orig_module_file = Path(module.__file__)
- local_module_file = Path(test_dir, orig_module_file.name)
- shutil.copy(src=orig_module_file, dst=local_module_file)
- try:
- yield local_module_file
- finally:
- local_module_file.unlink()
-
-
-@pytest.mark.skip(
- reason=(
- "It erase the log file for unknown reasons."
- "Skip for know, will be implemented in integration tests"
- )
-)
-def test_basic_run():
- """Make sure that the PyTornado run successfully"""
-
- shutil.copy(src=CPACS_TEST_FILE, dst=Path(MODULE_DIR, "ToolInput", "ToolInput.xml"))
-
- with run_module_test_locally(PYTORNADO_MAIN_MODULE, MODULE_DIR) as main:
- os.system(f"python {main}")
-
-
-# =================================================================================================
-# MAIN
-# =================================================================================================
-
-if __name__ == "__main__":
-
- print("Running Test PyTornado")
- print("To run test use the following command:")
- print(">> pytest -v")
diff --git a/ceasiompy/Range/ToolInput/.keep b/ceasiompy/Range/ToolInput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/Range/ToolInput/.keep
+++ b/ceasiompy/Range/ToolInput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/Range/__init__.py b/ceasiompy/Range/__init__.py
index e69de29bb..de37763a0 100644
--- a/ceasiompy/Range/__init__.py
+++ b/ceasiompy/Range/__init__.py
@@ -0,0 +1,32 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for Range module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = False
+
+# ===== Include GUI =====
+include_gui = False
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
diff --git a/ceasiompy/Range/func/AinFunc/getdatafromcpacs.py b/ceasiompy/Range/func/AinFunc/getdatafromcpacs.py
index 17920eff6..e39066047 100644
--- a/ceasiompy/Range/func/AinFunc/getdatafromcpacs.py
+++ b/ceasiompy/Range/func/AinFunc/getdatafromcpacs.py
@@ -43,26 +43,14 @@
from cpacspy.cpacsfunctions import add_uid, create_branch, open_tixi
from cpacspy.cpacsfunctions import get_value_or_default
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from ceasiompy.utils.commonxpath import RANGE_LD_RATIO_XPATH, TURBOPROP_XPATH
-log = get_logger()
-
-
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-"""All classes are defined inside the classes folder in the
- range_output_class script and into the Input_classes/Conventional
- folder inside the range_user_input.py script."""
-
# =============================================================================
# FUNCTIONS
# =============================================================================
-
def get_data(mw, ri, cpacs_in):
"""The function extracts from the xml file the required input data,
the code will use the default value when they are missing.
diff --git a/ceasiompy/Range/func/AoutFunc/cpacsrangeupdate.py b/ceasiompy/Range/func/AoutFunc/cpacsrangeupdate.py
index 688f5164a..68c76af61 100644
--- a/ceasiompy/Range/func/AoutFunc/cpacsrangeupdate.py
+++ b/ceasiompy/Range/func/AoutFunc/cpacsrangeupdate.py
@@ -17,9 +17,7 @@
from cpacspy.cpacsfunctions import add_uid, create_branch, open_tixi
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
+from ceasiompy import log
# =============================================================================
diff --git a/ceasiompy/Range/func/Crew/crewmembers.py b/ceasiompy/Range/func/Crew/crewmembers.py
index ccc312878..7a94e1eae 100644
--- a/ceasiompy/Range/func/Crew/crewmembers.py
+++ b/ceasiompy/Range/func/Crew/crewmembers.py
@@ -17,9 +17,7 @@
# =============================================================================
from ceasiompy.WeightConventional.func.weightutils import CABIN_CREW_MASS, PILOT_MASS
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
+from ceasiompy import log
# =============================================================================
diff --git a/ceasiompy/Range/func/Fuel/fuelconsumption.py b/ceasiompy/Range/func/Fuel/fuelconsumption.py
index 5358bf305..b1590502a 100644
--- a/ceasiompy/Range/func/Fuel/fuelconsumption.py
+++ b/ceasiompy/Range/func/Fuel/fuelconsumption.py
@@ -19,9 +19,7 @@
import math
from ceasiompy.WeightConventional.func.weightutils import UNUSABLE_FUEL_RATIO
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
+from ceasiompy import log
# =============================================================================
diff --git a/ceasiompy/Range/func/RangeEstimation/breguetrange.py b/ceasiompy/Range/func/RangeEstimation/breguetrange.py
index 2c068b734..7596e5432 100644
--- a/ceasiompy/Range/func/RangeEstimation/breguetrange.py
+++ b/ceasiompy/Range/func/RangeEstimation/breguetrange.py
@@ -19,9 +19,7 @@
import math
from ceasiompy.WeightConventional.func.weightutils import UNUSABLE_FUEL_RATIO
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
+from ceasiompy import log
# =============================================================================
@@ -35,7 +33,6 @@
def breguet_cruise_range(LDcru, ri, mw):
-
"""The function estimates the maximum range using the Breguet equation.
Source: Raymer, D.P. "Aircraft design: a conceptual approach"
diff --git a/ceasiompy/Range/func/rangeclass.py b/ceasiompy/Range/func/rangeclass.py
index ba12b9487..12013bd1b 100644
--- a/ceasiompy/Range/func/rangeclass.py
+++ b/ceasiompy/Range/func/rangeclass.py
@@ -5,7 +5,6 @@
The script contains the user inputs required for the range analysis.
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-09-27
diff --git a/ceasiompy/Range/rangemain.py b/ceasiompy/Range/range.py
similarity index 92%
rename from ceasiompy/Range/rangemain.py
rename to ceasiompy/Range/range.py
index 0b7031dbd..689f0ecab 100644
--- a/ceasiompy/Range/rangemain.py
+++ b/ceasiompy/Range/range.py
@@ -18,7 +18,6 @@
ToolInput folder after copying it into the ToolOutput
folder as ToolOutput.xml
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-09-27
@@ -48,26 +47,14 @@
from ceasiompy.utils.ceasiompyutils import aircraft_name
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from ceasiompy.utils.moduleinterfaces import (
check_cpacs_input_requirements,
get_toolinput_file_path,
get_tooloutput_file_path,
)
-log = get_logger()
-
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-"""All classes are defined inside the classes folder in the
- range_output_class script and into the Input_classes/Conventional
- folder inside the range_user_input.py script."""
-
+from ceasiompy.Range import MODULE_NAME
# =================================================================================================
# FUNCTIONS
@@ -228,15 +215,13 @@ def get_range_estimation(cpacs_path, cpacs_out_path):
log.info("############### Range estimation completed ###############")
-def main(cpacs_path, cpacs_out_path):
-
- log.info("----- Start of " + MODULE_NAME + " -----")
-
- check_cpacs_input_requirements(cpacs_path)
+def main(cpacs_path: Path, cpacs_out_path: Path) -> None:
+ module_name = MODULE_NAME
+ log.info("----- Start of " + module_name + " -----")
get_range_estimation(cpacs_path, cpacs_out_path)
- log.info("----- End of " + MODULE_NAME + " -----")
+ log.info("----- End of " + module_name + " -----")
# =================================================================================================
@@ -247,5 +232,6 @@ def main(cpacs_path, cpacs_out_path):
cpacs_path = get_toolinput_file_path(MODULE_NAME)
cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
+ check_cpacs_input_requirements(cpacs_path)
main(cpacs_path, cpacs_out_path)
diff --git a/ceasiompy/SMTrain/ToolInput/.keep b/ceasiompy/SMTrain/ToolInput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/SMTrain/ToolInput/.keep
+++ b/ceasiompy/SMTrain/ToolInput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/SMTrain/ToolOutput/.keep b/ceasiompy/SMTrain/ToolOutput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/SMTrain/ToolOutput/.keep
+++ b/ceasiompy/SMTrain/ToolOutput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/SMTrain/__init__.py b/ceasiompy/SMTrain/__init__.py
index e69de29bb..4729042a5 100644
--- a/ceasiompy/SMTrain/__init__.py
+++ b/ceasiompy/SMTrain/__init__.py
@@ -0,0 +1,32 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for SMTrain module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = False
+
+# ===== Include GUI =====
+include_gui = False
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
diff --git a/ceasiompy/SMTrain/__specs__.py b/ceasiompy/SMTrain/__specs__.py
index 735a30d55..d3d8e7140 100644
--- a/ceasiompy/SMTrain/__specs__.py
+++ b/ceasiompy/SMTrain/__specs__.py
@@ -1,5 +1,5 @@
from pathlib import Path
-
+import streamlit as st
from ceasiompy.utils.moduleinterfaces import CPACSInOut
from ceasiompy.utils.commonxpath import SMTRAIN_XPATH
@@ -71,7 +71,7 @@
cpacs_inout.add_input(
var_name="",
var_type=list,
- default_value=None,
+ default_value=st.session_state.cpacs.get_aeromap_uid_list(),
unit=None,
descr="Name of the aero map to evaluate",
xpath=SMTRAIN_XPATH + "/aeroMapUID",
diff --git a/ceasiompy/SMTrain/smtrain.py b/ceasiompy/SMTrain/smtrain.py
index ce0abf475..e055c3e7b 100755
--- a/ceasiompy/SMTrain/smtrain.py
+++ b/ceasiompy/SMTrain/smtrain.py
@@ -8,7 +8,6 @@
must be provided to train the model, except if the values are taken from an
aeromap, in which case they can all be found in the CPACS file.
-Python version: >=3.8
| Author: Vivien Riolo
| Creation: 2020-07-06
@@ -31,20 +30,25 @@
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
-import smt.surrogate_models as sms
from ceasiompy.SMUse.smuse import Surrogate_model
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from ceasiompy.utils.ceasiompyutils import get_results_directory
-from ceasiompy.utils.moduleinterfaces import get_toolinput_file_path, get_tooloutput_file_path
+from ceasiompy.utils.moduleinterfaces import (
+ get_toolinput_file_path,
+ get_tooloutput_file_path,
+ check_cpacs_input_requirements,
+)
from ceasiompy.utils.commonxpath import OPTWKDIR_XPATH, SMFILE_XPATH, SMTRAIN_XPATH
from cpacspy.cpacsfunctions import create_branch, get_value_or_default
from cpacspy.cpacspy import CPACS
from cpacspy.utils import COEFS, PARAMS
-log = get_logger()
+from ceasiompy.SMTrain import MODULE_NAME
+
+# =================================================================================================
+# CONSTANTS
+# =================================================================================================
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
# Working surrogate models, the hyperparameters can be changed here for experienced users.
model_dict = {
@@ -166,7 +170,10 @@ def extract_data_set(Tool):
for v in var_list:
if not v.isdigit() and v != "" and v in df.index:
exec('{} = df.loc["{}"]'.format(v, v))
- df_data = Tool.df.append({"Name": obj, "type": "obj"}, ignore_index=True)
+ df_data = pd.concat(
+ [Tool.df, pd.DataFrame([{"Name": obj, "type": "obj"}])],
+ ignore_index=True,
+ )
y.loc[obj] = eval(obj)
Tool.objectives = y.index
@@ -362,7 +369,7 @@ def gen_df_from_am(tixi):
x["type"] = "des"
y["type"] = "obj"
- df = x.append(y, ignore_index=True)
+ df = pd.concat([x, y], ignore_index=True)
df["getcmd"] = "-"
df["setcmd"] = "-"
df["initial value"] = "-"
@@ -451,9 +458,9 @@ def generate_model(Tool, cpacs_path):
# =================================================================================================
-def main(cpacs_path, cpacs_out_path):
-
- log.info("----- Start of " + MODULE_NAME + " -----")
+def main(cpacs_path: Path, cpacs_out_path: Path) -> None:
+ module_name = MODULE_NAME
+ log.info("----- Start of " + module_name + " -----")
# Get results directory
results_dir = get_results_directory("SMTrain")
@@ -463,12 +470,13 @@ def main(cpacs_path, cpacs_out_path):
generate_model(Tool, cpacs_path)
save_model(Tool, cpacs_path, cpacs_out_path)
- log.info("----- End of " + MODULE_NAME + " -----")
+ log.info("----- End of " + module_name + " -----")
if __name__ == "__main__":
cpacs_path = get_toolinput_file_path(MODULE_NAME)
cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
+ check_cpacs_input_requirements(cpacs_path)
main(cpacs_path, cpacs_out_path)
diff --git a/ceasiompy/SMUse/ToolInput/.keep b/ceasiompy/SMUse/ToolInput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/SMUse/ToolInput/.keep
+++ b/ceasiompy/SMUse/ToolInput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/SMUse/ToolOutput/.keep b/ceasiompy/SMUse/ToolOutput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/SMUse/ToolOutput/.keep
+++ b/ceasiompy/SMUse/ToolOutput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/SMUse/__init__.py b/ceasiompy/SMUse/__init__.py
index e69de29bb..31141a55d 100644
--- a/ceasiompy/SMUse/__init__.py
+++ b/ceasiompy/SMUse/__init__.py
@@ -0,0 +1,32 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for SMUse module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = False
+
+# ===== Include GUI =====
+include_gui = False
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
diff --git a/ceasiompy/SMUse/__specs__.py b/ceasiompy/SMUse/__specs__.py
index 111ed793a..afdcf39cd 100644
--- a/ceasiompy/SMUse/__specs__.py
+++ b/ceasiompy/SMUse/__specs__.py
@@ -1,5 +1,5 @@
from pathlib import Path
-
+import streamlit as st
from ceasiompy.utils.moduleinterfaces import CPACSInOut
from ceasiompy.utils.commonxpath import SMUSE_XPATH
@@ -47,7 +47,7 @@
cpacs_inout.add_input(
var_name="",
var_type=list,
- default_value=None,
+ default_value=st.session_state.cpacs.get_aeromap_uid_list(),
descr="To which aeroMap the model shall take andn write the entries",
xpath=SMUSE_XPATH + "/aeroMapUID",
gui=True,
diff --git a/ceasiompy/SMUse/smuse.py b/ceasiompy/SMUse/smuse.py
index fe2556df2..564a1fd9e 100755
--- a/ceasiompy/SMUse/smuse.py
+++ b/ceasiompy/SMUse/smuse.py
@@ -8,7 +8,6 @@
points of an aeromap in one run, as they can be stored as vectors into the
CPACS file.
-Python version: >=3.8
| Author: Vivien Riolo
| Creation: 2020-07-06
@@ -29,18 +28,18 @@
import numpy as np
import pandas as pd
import smt.surrogate_models as sms # Use after loading the model
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.moduleinterfaces import get_toolinput_file_path, get_tooloutput_file_path
+from ceasiompy import log
+from ceasiompy.utils.moduleinterfaces import (
+ get_toolinput_file_path,
+ get_tooloutput_file_path,
+ check_cpacs_input_requirements
+)
from ceasiompy.utils.commonxpath import SMTRAIN_XPATH, SMUSE_XPATH
from cpacspy.cpacsfunctions import create_branch, get_value_or_default
from cpacspy.cpacspy import CPACS
from cpacspy.utils import PARAMS_COEFS
-log = get_logger()
-
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
-
+from ceasiompy.SMUse import MODULE_NAME
# =================================================================================================
# ClASSES
@@ -153,7 +152,8 @@ def write_inouts(v, inout, tixi):
def aeromap_calculation(sm, cpacs):
- """Make a prediction using only the aeromap entries.
+ """
+ Make a prediction using only the aeromap entries.
By using only the aeromap functions this module is way faster to execute. Only
works with the aeromap as the other values of the CPACs are not vectors and
@@ -252,9 +252,10 @@ def check_aeromap(tixi):
# ==================================================================================================
-def main(cpacs_path, cpacs_out_path):
+def main(cpacs_path: Path, cpacs_out_path: Path) -> None:
- log.info("----- Start of " + MODULE_NAME + " -----")
+ module_name = MODULE_NAME
+ log.info("----- Start of " + module_name + " -----")
# Load the model
cpacs = CPACS(cpacs_path)
@@ -269,12 +270,13 @@ def main(cpacs_path, cpacs_out_path):
cpacs.save_cpacs(cpacs_out_path, overwrite=True)
- log.info("----- End of " + MODULE_NAME + " -----")
+ log.info("----- End of " + module_name + " -----")
if __name__ == "__main__":
cpacs_path = get_toolinput_file_path("SMUse")
cpacs_out_path = get_tooloutput_file_path("SMUse")
+ check_cpacs_input_requirements(cpacs_path)
main(cpacs_path, cpacs_out_path)
diff --git a/ceasiompy/SU2MeshDef/ToolInput/.keep b/ceasiompy/SU2MeshDef/ToolInput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/SU2MeshDef/ToolInput/.keep
+++ b/ceasiompy/SU2MeshDef/ToolInput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/SU2MeshDef/ToolOutput/.keep b/ceasiompy/SU2MeshDef/ToolOutput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/SU2MeshDef/ToolOutput/.keep
+++ b/ceasiompy/SU2MeshDef/ToolOutput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/SU2MeshDef/__init__.py b/ceasiompy/SU2MeshDef/__init__.py
index e69de29bb..b210fb709 100644
--- a/ceasiompy/SU2MeshDef/__init__.py
+++ b/ceasiompy/SU2MeshDef/__init__.py
@@ -0,0 +1,32 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for SU2MeshDef module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = False
+
+# ===== Include GUI =====
+include_gui = False
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
diff --git a/ceasiompy/SU2MeshDef/__specs__.py b/ceasiompy/SU2MeshDef/__specs__.py
index 1ecdb9074..5a11a05ee 100644
--- a/ceasiompy/SU2MeshDef/__specs__.py
+++ b/ceasiompy/SU2MeshDef/__specs__.py
@@ -82,19 +82,6 @@
gui_group=None,
)
-for direction in ["x", "y", "z"]:
- cpacs_inout.add_input(
- var_name=f"ref_ori_moment_{direction}",
- var_type=float,
- default_value=0.0,
- unit="m",
- descr=f"Fuselage scaling on {direction} axis",
- xpath=REF_XPATH + f"/point/{direction}",
- gui=False,
- gui_name=None,
- gui_group=None,
- )
-
# ----- Output -----
diff --git a/ceasiompy/SU2MeshDef/files/DefaultConfig_v7.cfg b/ceasiompy/SU2MeshDef/files/DefaultConfig_v7.cfg
index ab761b6b8..9b32f64a5 100644
--- a/ceasiompy/SU2MeshDef/files/DefaultConfig_v7.cfg
+++ b/ceasiompy/SU2MeshDef/files/DefaultConfig_v7.cfg
@@ -48,11 +48,11 @@ DISCARD_INFILES= NO
%
% System of measurements (SI, US)
% International system of units (SI): ( meters, kilograms, Kelvins,
-% Newtons = kg m/s^2, Pascals = N/m^2,
+% Newtons = kg m/s^2, Pascals = N/m^2,
% Density = kg/m^3, Speed = m/s,
% Equiv. Area = m^2 )
-% United States customary units (US): ( inches, slug, Rankines, lbf = slug ft/s^2,
-% psf = lbf/ft^2, Density = slug/ft^3,
+% United States customary units (US): ( inches, slug, Rankines, lbf = slug ft/s^2,
+% psf = lbf/ft^2, Density = slug/ft^3,
% Speed = ft/s, Equiv. Area = ft^2 )
SYSTEM_MEASUREMENTS= SI
%
@@ -68,7 +68,7 @@ OUTER_ITER= 1
% Maximum number of time iterations
TIME_ITER= 1
%
-% Convergence field
+% Convergence field
CONV_FIELD= DRAG
%
% Min value of the residual (log10 of the residual)
@@ -174,8 +174,8 @@ INC_DENSITY_INIT= 1.2886
% Initial velocity for incompressible flows (1.0,0,0 m/s by default)
INC_VELOCITY_INIT= ( 1.0, 0.0, 0.0 )
%
-% Initial temperature for incompressible flows that include the
-% energy equation (288.15 K by default). Value is ignored if
+% Initial temperature for incompressible flows that include the
+% energy equation (288.15 K by default). Value is ignored if
% INC_ENERGY_EQUATION is false.
INC_TEMPERATURE_INIT= 288.15
%
@@ -190,7 +190,7 @@ INC_DENSITY_REF= 1.0
% Reference velocity for incompressible flows (1.0 m/s by default)
INC_VELOCITY_REF= 1.0
%
-% Reference temperature for incompressible flows that include the
+% Reference temperature for incompressible flows that include the
% energy equation (1.0 K by default)
INC_TEMPERATURE_REF = 1.0
%
@@ -265,11 +265,11 @@ CRITICAL_PRESSURE= 3588550.0
% Acentri factor (0.035 (air))
ACENTRIC_FACTOR= 0.035
%
-% Specific heat at constant pressure, Cp (1004.703 J/kg*K (air)).
+% Specific heat at constant pressure, Cp (1004.703 J/kg*K (air)).
% Incompressible fluids with energy eqn. only (CONSTANT_DENSITY, INC_IDEAL_GAS).
SPECIFIC_HEAT_CP= 1004.703
%
-% Thermal expansion coefficient (0.00347 K^-1 (air))
+% Thermal expansion coefficient (0.00347 K^-1 (air))
% Used with Boussinesq approx. (incompressible, BOUSSINESQ density model only)
THERMAL_EXPANSION_COEFF= 0.00347
%
@@ -303,7 +303,7 @@ MU_POLYCOEFFS= (0.0, 0.0, 0.0, 0.0, 0.0)
% --------------------------- THERMAL CONDUCTIVITY MODEL ----------------------%
%
-% Laminar Conductivity model (CONSTANT_CONDUCTIVITY, CONSTANT_PRANDTL,
+% Laminar Conductivity model (CONSTANT_CONDUCTIVITY, CONSTANT_PRANDTL,
% POLYNOMIAL_CONDUCTIVITY).
CONDUCTIVITY_MODEL= CONSTANT_PRANDTL
%
@@ -361,7 +361,7 @@ PLUNGING_OMEGA= 0.0 0.0 0.0
% Plunging amplitude (m or ft) in x, y, & z directions
PLUNGING_AMPL= 0.0 0.0 0.0
%
-% Type of dynamic surface movement (NONE, DEFORMING,
+% Type of dynamic surface movement (NONE, DEFORMING,
% MOVING_WALL, FLUID_STRUCTURE, FLUID_STRUCTURE_STATIC,
% AEROELASTIC, EXTERNAL, EXTERNAL_ROTATION,
% AEROELASTIC_RIGID_MOTION)
@@ -403,7 +403,7 @@ MOVE_MOTION_ORIGIN = 0
% If BUFFET objective/constraint is specified, the objective is given by
% the integrated sensor normalized by reference area
%
-% See doi: 10.2514/1.J055172
+% See doi: 10.2514/1.J055172
%
% Evaluate buffet sensor on Navier-Stokes markers (NO, YES)
%BUFFET_MONITORING= NO
@@ -677,7 +677,7 @@ ENGINE_INFLOW_TYPE= FAN_FACE_MACH
% Format: (engine inflow marker, fan face Mach, ... )
MARKER_ENGINE_INFLOW= ( NONE )
%
-% Engine exhaust boundary marker(s) with the following formats (NONE = no marker)
+% Engine exhaust boundary marker(s) with the following formats (NONE = no marker)
% Format: (engine exhaust marker, total nozzle temp, total nozzle pressure, ... )
MARKER_ENGINE_EXHAUST= ( NONE )
%
@@ -801,7 +801,7 @@ RK_ALPHA_COEFF= ( 0.66667, 0.66667, 1.000000 )
% FORCE_X, FORCE_Y, FORCE_Z, THRUST,
% TORQUE, TOTAL_HEATFLUX,
% MAXIMUM_HEATFLUX, INVERSE_DESIGN_PRESSURE,
-% INVERSE_DESIGN_HEATFLUX, SURFACE_TOTAL_PRESSURE,
+% INVERSE_DESIGN_HEATFLUX, SURFACE_TOTAL_PRESSURE,
% SURFACE_MASSFLOW, SURFACE_STATIC_PRESSURE, SURFACE_MACH)
% For a weighted sum of objectives: separate by commas, add OBJECTIVE_WEIGHT and MARKER_MONITORING in matching order.
OBJECTIVE_FUNCTION= DRAG
@@ -1106,7 +1106,7 @@ GEO_MODE= FUNCTION
% Kind of deformation (NO_DEFORMATION, SCALE_GRID, TRANSLATE_GRID, ROTATE_GRID,
% FFD_SETTING, FFD_NACELLE,
% FFD_CONTROL_POINT, FFD_CAMBER, FFD_THICKNESS, FFD_TWIST
-% FFD_CONTROL_POINT_2D, FFD_CAMBER_2D, FFD_THICKNESS_2D,
+% FFD_CONTROL_POINT_2D, FFD_CAMBER_2D, FFD_THICKNESS_2D,
% FFD_TWIST_2D, HICKS_HENNE, SURFACE_BUMP, SURFACE_FILE,
% FFD_ROTATION)
DV_KIND= FFD_SETTING
@@ -1288,14 +1288,14 @@ VOLUME_OUTPUT= (COORDINATES, SOLUTION, PRIMITIVE)
SCREEN_WRT_FREQ_INNER= 1
%
SCREEN_WRT_FREQ_OUTER= 1
-%
+%
SCREEN_WRT_FREQ_TIME= 1
%
% Writing frequency for history output
HISTORY_WRT_FREQ_INNER= 1
%
HISTORY_WRT_FREQ_OUTER= 1
-%
+%
HISTORY_WRT_FREQ_TIME= 1
%
% Writing frequency for volume/surface output
@@ -1321,7 +1321,7 @@ SOLUTION_ADJ_FILENAME= solution_adj.dat
% Output tabular file format (TECPLOT, CSV)
TABULAR_FORMAT= TECPLOT
%
-% Files to output
+% Files to output
OUTPUT_FILES= (RESTART, PARAVIEW, SURFACE_PARAVIEW)
%
% Output file convergence history (w/o extension)
@@ -1365,7 +1365,7 @@ REORIENT_ELEMENTS= YES
% --------------------- OPTIMAL SHAPE DESIGN DEFINITION -----------------------%
%
% Available flow based objective functions or constraint functions
-% DRAG, LIFT, SIDEFORCE, EFFICIENCY, BUFFET,
+% DRAG, LIFT, SIDEFORCE, EFFICIENCY, BUFFET,
% FORCE_X, FORCE_Y, FORCE_Z,
% MOMENT_X, MOMENT_Y, MOMENT_Z,
% THRUST, TORQUE, FIGURE_OF_MERIT,
@@ -1457,4 +1457,4 @@ FIN_DIFF_STEP = 0.001
DEFINITION_DV= ( 1, 1.0 | airfoil | 0, 0.05 ); ( 1, 1.0 | airfoil | 0, 0.10 ); ( 1, 1.0 | airfoil | 0, 0.15 ); ( 1, 1.0 | airfoil | 0, 0.20 ); ( 1, 1.0 | airfoil | 0, 0.25 ); ( 1, 1.0 | airfoil | 0, 0.30 ); ( 1, 1.0 | airfoil | 0, 0.35 ); ( 1, 1.0 | airfoil | 0, 0.40 ); ( 1, 1.0 | airfoil | 0, 0.45 ); ( 1, 1.0 | airfoil | 0, 0.50 ); ( 1, 1.0 | airfoil | 0, 0.55 ); ( 1, 1.0 | airfoil | 0, 0.60 ); ( 1, 1.0 | airfoil | 0, 0.65 ); ( 1, 1.0 | airfoil | 0, 0.70 ); ( 1, 1.0 | airfoil | 0, 0.75 ); ( 1, 1.0 | airfoil | 0, 0.80 ); ( 1, 1.0 | airfoil | 0, 0.85 ); ( 1, 1.0 | airfoil | 0, 0.90 ); ( 1, 1.0 | airfoil | 0, 0.95 ); ( 1, 1.0 | airfoil | 1, 0.05 ); ( 1, 1.0 | airfoil | 1, 0.10 ); ( 1, 1.0 | airfoil | 1, 0.15 ); ( 1, 1.0 | airfoil | 1, 0.20 ); ( 1, 1.0 | airfoil | 1, 0.25 ); ( 1, 1.0 | airfoil | 1, 0.30 ); ( 1, 1.0 | airfoil | 1, 0.35 ); ( 1, 1.0 | airfoil | 1, 0.40 ); ( 1, 1.0 | airfoil | 1, 0.45 ); ( 1, 1.0 | airfoil | 1, 0.50 ); ( 1, 1.0 | airfoil | 1, 0.55 ); ( 1, 1.0 | airfoil | 1, 0.60 ); ( 1, 1.0 | airfoil | 1, 0.65 ); ( 1, 1.0 | airfoil | 1, 0.70 ); ( 1, 1.0 | airfoil | 1, 0.75 ); ( 1, 1.0 | airfoil | 1, 0.80 ); ( 1, 1.0 | airfoil | 1, 0.85 ); ( 1, 1.0 | airfoil | 1, 0.90 ); ( 1, 1.0 | airfoil | 1, 0.95 )
%
% Use combined objective within gradient evaluation: may reduce cost to compute gradients when using the adjoint formulation.
-OPT_COMBINE_OBJECTIVE = NO
+OPT_COMBINE_OBJECTIVE = NO
diff --git a/ceasiompy/SU2MeshDef/su2meshdef.py b/ceasiompy/SU2MeshDef/su2meshdef.py
index 21714e0a8..5d77939f7 100644
--- a/ceasiompy/SU2MeshDef/su2meshdef.py
+++ b/ceasiompy/SU2MeshDef/su2meshdef.py
@@ -5,7 +5,6 @@
Module to create new deformed mesh with SU2 mesh deformation
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2020-02-27
@@ -20,8 +19,7 @@
* Mesh def for FSI (with surface disp)
* Create the doc for this module
* Create test functions
- * Use Pathlib and asolute path when refactor this module
- * Use 'run_software' function to run SU2
+ * Use Pathlib and absolute path when refactor this module
"""
@@ -36,13 +34,13 @@
import numpy as np
import pandas as pd
-from ceasiompy.SU2Run.func.su2utils import get_mesh_markers
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy.SU2Run.func.utils import get_mesh_markers, su2_format
+from ceasiompy import log
from ceasiompy.utils.ceasiompyutils import (
aircraft_name,
get_reasonable_nb_cpu,
get_results_directory,
- run_soft,
+ run_software,
)
from ceasiompy.utils.commonxpath import (
REF_XPATH,
@@ -52,7 +50,11 @@
WINGS_XPATH,
)
from ceasiompy.utils.configfiles import ConfigFile
-from ceasiompy.utils.moduleinterfaces import get_toolinput_file_path, get_tooloutput_file_path
+from ceasiompy.utils.moduleinterfaces import (
+ get_toolinput_file_path,
+ get_tooloutput_file_path,
+ check_cpacs_input_requirements
+)
from cpacspy.cpacsfunctions import (
add_string_vector,
get_uid,
@@ -63,16 +65,7 @@
)
from tixi3.tixi3wrapper import Tixi3Exception
-log = get_logger()
-
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
-
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
+from ceasiompy.SU2MeshDef import MODULE_NAME, MODULE_DIR
# =================================================================================================
# FUNCTIONS
@@ -273,9 +266,6 @@ def get_ted_corner(tixi, tigl, ted_uid):
* UP: Upper surface
* LOW: Lower surface
- Source: * A part of this function as been copy from the pyTornado code
- * CPACS Documentation https://www.cpacs.de/pages/documentation.html
-
Args:
tixi (handles): TIXI Handle of the CPACS file
tigl (handles): TIGL Handle of the CPACS file
@@ -340,9 +330,6 @@ def get_ted_hinge(tixi, tigl, ted_uid):
* IN: Inbord side
* OUT: Outbord side
- Source: * A part of this function as been copy from the pyTornado code
- * CPACS Documentation
-
Args:
tixi (handles): TIXI Handle of the CPACS file
tigl (handles): TIGL Handle of the CPACS file
@@ -596,9 +583,9 @@ def generate_mesh_def_config(tixi, wkdir, ted_uid, wing_uid, sym_dir, defl_list)
# Mesh Marker
mesh_markers = get_mesh_markers(su2_mesh_path)
- bc_wall_str = "(" + ",".join(mesh_markers["wall"]) + ")"
+ bc_wall_str = su2_format(",".join(mesh_markers["wall"]))
cfg["MARKER_EULER"] = bc_wall_str
- cfg["MARKER_FAR"] = " (Farfield)"
+ cfg["MARKER_FAR"] = " ( Farfield )"
cfg["MARKER_SYM"] = " (0)"
cfg["MARKER_PLOTTING"] = bc_wall_str
cfg["MARKER_MONITORING"] = bc_wall_str
@@ -607,13 +594,14 @@ def generate_mesh_def_config(tixi, wkdir, ted_uid, wing_uid, sym_dir, defl_list)
# FFD BOX definition
cfg["DV_KIND"] = "FFD_SETTING"
- cfg["DV_MARKER"] = "( " + wing_uid + ")"
+ cfg["DV_MARKER"] = su2_format(wing_uid)
cfg["FFD_CONTINUITY"] = "2ND_DERIVATIVE"
- cfg["FFD_DEFINITION"] = "( " + ted_uid + ", " + ",".join(ted_corner_list) + ")"
+ cfg["FFD_DEFINITION"] = su2_format(ted_uid + ", " + ",".join(ted_corner_list))
cfg["FFD_DEGREE"] = "( 6, 10, 3 )" # TODO: how to chose/calculate these value?
if sym_dir:
- cfg["FFD_DEFINITION"] += "; (" + ted_uid + "_sym, " + ",".join(ted_corner_sym_list) + ")"
- cfg["FFD_DEGREE"] += ";( 6, 10, 3 )" # TODO: how to chose/calculate these value?
+ cfg["FFD_DEFINITION"] += "; " + \
+ su2_format(ted_uid + "_sym, " + ",".join(ted_corner_sym_list))
+ cfg["FFD_DEGREE"] += "; ( 6, 10, 3 )" # TODO: how to chose/calculate these value?
cfg["MESH_OUT_FILENAME"] = "_mesh_ffd_box.su2"
# Write Config definition for FFD box
@@ -626,8 +614,8 @@ def generate_mesh_def_config(tixi, wkdir, ted_uid, wing_uid, sym_dir, defl_list)
for defl in defl_list:
cfg["DV_KIND"] = "FFD_ROTATION"
- cfg["DV_MARKER"] = "( " + wing_uid + ")"
- cfg["DV_PARAM"] = "( " + ted_uid + ", " + ",".join(hinge_list) + ")"
+ cfg["DV_MARKER"] = su2_format(wing_uid)
+ cfg["DV_PARAM"] = su2_format(ted_uid + ", " + ",".join(hinge_list))
cfg["DV_VALUE"] = str(defl / 1000) # SU2 use 1/1000 degree...
cfg["MESH_FILENAME"] = "_mesh_ffd_box.su2"
@@ -644,8 +632,8 @@ def generate_mesh_def_config(tixi, wkdir, ted_uid, wing_uid, sym_dir, defl_list)
if sym_dir:
# TODO: add a condition for anti symmetric deflection (e.g. ailerons)
- cfg["DV_MARKER"] = "( " + wing_uid + ")"
- cfg["DV_PARAM"] = "( " + ted_uid + "_sym, " + ",".join(hinge_sym_list) + ")"
+ cfg["DV_MARKER"] = su2_format(wing_uid)
+ cfg["DV_PARAM"] = su2_format(ted_uid + "_sym, " + ",".join(hinge_sym_list))
cfg["DV_VALUE"] = str(defl / 1000) # SU2 use 1/1000 degree...
cfg["MESH_FILENAME"] = defl_mesh_name
@@ -712,7 +700,7 @@ def generate_config_deformed_mesh(cpacs_path, cpacs_out_path, skip_config=False,
# else: calculate all TED adn all deflections from CPACS
# active_ted_list = ted_list
- for i, row in ted_df.iterrows():
+ for _, row in ted_df.iterrows():
# Unwrap TED data from the dataframe
ted_uid = row["ted_uid"]
@@ -767,7 +755,7 @@ def run_mesh_deformation(tixi, wkdir):
for cfg_file in sorted(cfg_file_list):
if os.path.isfile(cfg_file):
- run_soft("SU2_DEF", cfg_file, ted_dir, nb_proc)
+ run_software("SU2_DEF", cfg_file, ted_dir, nb_proc)
else:
raise ValueError("Not correct configuration file to run!")
@@ -793,9 +781,10 @@ def run_mesh_deformation(tixi, wkdir):
# =================================================================================================
-def main(cpacs_path, cpacs_out_path):
+def main(cpacs_path: Path, cpacs_out_path: Path) -> None:
- log.info("----- Start of " + MODULE_NAME + " -----")
+ module_name = MODULE_NAME
+ log.info("----- Start of " + module_name + " -----")
if len(sys.argv) > 1:
if sys.argv[1] == "-c":
@@ -807,12 +796,12 @@ def main(cpacs_path, cpacs_out_path):
else: # if no argument given
generate_config_deformed_mesh(cpacs_path, cpacs_out_path)
- log.info("----- End of " + MODULE_NAME + " -----")
+ log.info("----- End of " + module_name + " -----")
if __name__ == "__main__":
-
cpacs_path = get_toolinput_file_path(MODULE_NAME)
cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
+ check_cpacs_input_requirements(cpacs_path)
main(cpacs_path, cpacs_out_path)
diff --git a/ceasiompy/SU2Run/README.md b/ceasiompy/SU2Run/README.md
index 975ab8b87..20a4f2f88 100644
--- a/ceasiompy/SU2Run/README.md
+++ b/ceasiompy/SU2Run/README.md
@@ -56,6 +56,8 @@ If you want to perform calculation on an aircraft with an actuator disk you must
:warning: The mesh convergence should also be checked. If you use a mesh too coarse, the aerodynamic coefficients might be far from the values you would expect with a finer mesh.
+For unsteady simulations, it uses by default time marching : DUAL_TIME_STEPPING-2ND_ORDER.
+
## More information
* [SU2 website](https://su2code.github.io/)
diff --git a/ceasiompy/SU2Run/ToolInput/.keep b/ceasiompy/SU2Run/ToolInput/.keep
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/ceasiompy/SU2Run/ToolInput/.keep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ceasiompy/SU2Run/__init__.py b/ceasiompy/SU2Run/__init__.py
index e69de29bb..a2ba96f0c 100644
--- a/ceasiompy/SU2Run/__init__.py
+++ b/ceasiompy/SU2Run/__init__.py
@@ -0,0 +1,56 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for SU2Run module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+from ceasiompy import log
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = True
+
+# ===== Include GUI =====
+include_gui = True
+
+# ===== Add a Results Directory =====
+RES_DIR = True
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+# Specific to SU2Run module
+
+# List of control surface scenarios
+CONTROL_SURFACE_LIST = ["flap", "rudder", "aileron"]
+
+# Either Euler or Rans simulation
+TEMPLATE_TYPE = ["EULER", "RANS"]
+
+# Name of used software
+SOFTWARE_NAME = "SU2_CFD"
+REQUIRED_VERSION = "8.1.0"
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/SU2Run/__specs__.py b/ceasiompy/SU2Run/__specs__.py
index 8a009fae7..46344ab0e 100644
--- a/ceasiompy/SU2Run/__specs__.py
+++ b/ceasiompy/SU2Run/__specs__.py
@@ -1,194 +1,145 @@
-from pathlib import Path
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+GUI Interface of ModuleTemplate.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+import streamlit as st
from ceasiompy.utils.ceasiompyutils import get_reasonable_nb_cpu
+
+from ceasiompy.utils.moduleinterfaces import CPACSInOut
+
+from ceasiompy import log, NO_YES_LIST
+from ceasiompy.SU2Run import include_gui, TEMPLATE_TYPE
+
from ceasiompy.utils.commonxpath import (
- AEROPERFORMANCE_XPATH,
- GEOM_XPATH,
- PROP_XPATH,
- RANGE_XPATH,
- REF_XPATH,
- SU2_ACTUATOR_DISK_XPATH,
SU2_AEROMAP_UID_XPATH,
- SU2_BC_FARFIELD_XPATH,
- SU2_BC_WALL_XPATH,
+ SU2_DAMPING_DER_XPATH,
+ SU2_ROTATION_RATE_XPATH,
+ SU2_CONTROL_SURF_BOOL_XPATH,
+ SU2_CONTROL_SURF_ANGLE_XPATH,
+ SU2_NB_CPU_XPATH,
+ SU2_CONFIG_RANS_XPATH,
+ SU2_MAX_ITER_XPATH,
SU2_CFL_NB_XPATH,
SU2_CFL_ADAPT_XPATH,
SU2_CFL_ADAPT_PARAM_DOWN_XPATH,
SU2_CFL_ADAPT_PARAM_UP_XPATH,
SU2_CFL_MIN_XPATH,
SU2_CFL_MAX_XPATH,
- SU2_CONTROL_SURF_XPATH,
- SU2_DAMPING_DER_XPATH,
- SU2_EXTRACT_LOAD_XPATH,
- SU2_FIXED_CL_XPATH,
- SU2_MAX_ITER_XPATH,
SU2_MG_LEVEL_XPATH,
- SU2_NB_CPU_XPATH,
- SU2_ROTATION_RATE_XPATH,
- SU2_TARGET_CL_XPATH,
SU2_UPDATE_WETTED_AREA_XPATH,
- SU2MESH_XPATH,
- SU2_CONFIG_RANS_XPATH,
+ SU2_EXTRACT_LOAD_XPATH,
+ SU2_ACTUATOR_DISK_XPATH,
+ PROPELLER_THRUST_XPATH,
+ PROPELLER_BLADE_LOSS_XPATH,
+ SU2_DYNAMICDERIVATIVES_BOOL_XPATH,
+ SU2_DYNAMICDERIVATIVES_TIMESIZE_XPATH,
+ SU2_DYNAMICDERIVATIVES_AMPLITUDE_XPATH,
+ SU2_DYNAMICDERIVATIVES_FREQUENCY_XPATH,
+ SU2_DYNAMICDERIVATIVES_INNERITER_XPATH,
+ USED_SU2_MESH_XPATH,
+ RANGE_CRUISE_MACH_XPATH,
+ RANGE_CRUISE_ALT_XPATH,
+ SU2_TARGET_CL_XPATH,
+ SU2_FIXED_CL_XPATH,
+ GEOM_XPATH,
+ SU2_BC_WALL_XPATH,
+ SU2_BC_FARFIELD_XPATH,
+ AEROPERFORMANCE_XPATH,
)
-from ceasiompy.utils.moduleinterfaces import CPACSInOut
-
-# ===== Module Status =====
-# True if the module is active
-# False if the module is disabled (not working or not ready)
-module_status = True
-
-# ===== Results directory path =====
-
-RESULTS_DIR = Path("Results", "SU2")
-# ===== CPACS inputs and outputs =====
+# ==============================================================================
+# VARIABLE
+# ==============================================================================
cpacs_inout = CPACSInOut()
-# ----- Input -----
+# ==============================================================================
+# GUI INPUTS
+# ==============================================================================
cpacs_inout.add_input(
var_name="aeromap_uid",
var_type=list,
- default_value=None,
+ default_value=st.session_state.cpacs.get_aeromap_uid_list(),
unit=None,
descr="Name of the aero map to calculate",
xpath=SU2_AEROMAP_UID_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="__AEROMAP_SELECTION",
gui_group="Aeromap settings",
)
-# cpacs_inout.add_input(
-# var_name="mesh_upload",
-# var_type=str,
-# default_value="your path",
-# unit=None,
-# descr="Name of the mesh to upload",
-# xpath=SU2MESH_XPATH,
-# gui=True,
-# gui_name="Path of the mesh",
-# gui_group="Mesh upload",
-# )
-
cpacs_inout.add_input(
- var_name="ref_len",
- var_type=float,
- default_value=None,
- unit="m",
- descr="Reference length of the aircraft",
- xpath=REF_XPATH + "/length",
- gui=False,
- gui_name=None,
- gui_group=None,
-)
-
-cpacs_inout.add_input(
- var_name="ref_area",
- var_type=float,
- default_value=None,
- unit="m^2",
- descr="Reference area of the aircraft",
- xpath=REF_XPATH + "/area",
- gui=False,
- gui_name=None,
- gui_group=None,
-)
-
-for direction in ["x", "y", "z"]:
- cpacs_inout.add_input(
- var_name=f"ref_ori_moment_{direction}",
- var_type=float,
- default_value=0.0,
- unit="m",
- descr=f"Fuselage scaling on {direction} axis",
- xpath=REF_XPATH + f"/point/{direction}",
- gui=False,
- gui_name=None,
- gui_group=None,
- )
-
-cpacs_inout.add_input(
- var_name="cruise_mach",
- var_type=float,
- default_value=0.78,
- unit="1",
- descr="Aircraft cruise Mach number",
- xpath=RANGE_XPATH + "/cruiseMach",
- gui=False,
- gui_name="Cruise Mach",
- gui_group="If fixed CL",
-)
-
-cpacs_inout.add_input(
- var_name="cruise_alt",
- var_type=float,
- default_value=120000.0,
- unit="m",
- descr="Aircraft cruise altitude",
- xpath=RANGE_XPATH + "/cruiseAltitude",
- gui=False,
- gui_name="Cruise Altitude",
- gui_group="If fixed CL",
-)
-
-cpacs_inout.add_input(
- var_name="target_cl",
- var_type=float,
- default_value=1.0,
- unit="1",
- descr="Value of CL to achieve to have a level flight with the given conditions",
- xpath=SU2_TARGET_CL_XPATH,
- gui=False,
- gui_name=None,
- gui_group=None,
-)
-
-cpacs_inout.add_input(
- var_name="fixed_cl",
- var_type=str,
- default_value="NO",
- unit="-",
- descr="FIXED_CL_MODE parameter for SU2",
- xpath=SU2_FIXED_CL_XPATH,
- gui=False,
- gui_name=None,
- gui_group=None,
+ var_name="mesh_choice",
+ var_type="DynamicChoice",
+ default_value=["Other", "Path", "db"],
+ unit=None,
+ descr="Choose from where to upload the mesh",
+ xpath=USED_SU2_MESH_XPATH,
+ gui=include_gui,
+ gui_name="Choose mesh",
+ gui_group="Data Settings",
)
cpacs_inout.add_input(
var_name="damping_der",
var_type=bool,
default_value=False,
- unit="1",
+ unit=None,
descr="To check if damping derivatives should be calculated or not",
xpath=SU2_DAMPING_DER_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Damping Derivatives",
- gui_group="Aeromap Options",
+ gui_group="Damping Derivative Option",
)
cpacs_inout.add_input(
var_name="rotation_rate",
var_type=float,
default_value=1.0,
- unit="rad/s",
+ unit="[rad/s]",
descr="Rotation rate use to calculate damping derivatives",
xpath=SU2_ROTATION_RATE_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Rotation Rate",
- gui_group="Aeromap Options",
+ gui_group="Rotation Rates Setting",
)
cpacs_inout.add_input(
var_name="control_surf",
var_type=bool,
default_value=False,
- unit="1",
+ unit=None,
descr="To check if control surfaces deflections should be calculated or not",
- xpath=SU2_CONTROL_SURF_XPATH,
- gui=True,
+ xpath=SU2_CONTROL_SURF_BOOL_XPATH,
+ gui=include_gui,
gui_name="Control Surfaces",
+ gui_group="Control Surface Option",
+)
+
+cpacs_inout.add_input(
+ var_name="ctrl_surf_deflection",
+ var_type="multiselect",
+ default_value=[0.0],
+ unit="[deg]",
+ descr="Rotation of control surface",
+ xpath=SU2_CONTROL_SURF_ANGLE_XPATH,
+ gui=include_gui,
+ gui_name="Control surface angle",
gui_group="Aeromap Options",
)
@@ -196,10 +147,10 @@
var_name="nb_proc",
var_type=int,
default_value=get_reasonable_nb_cpu(),
- unit="1",
+ unit=None,
descr="Number of proc to use to run SU2",
xpath=SU2_NB_CPU_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Nb of processor",
gui_group="CPU",
)
@@ -207,23 +158,23 @@
cpacs_inout.add_input(
var_name="RANS calculation",
var_type=list,
- default_value=["Euler", "RANS"],
- unit="1",
- descr="Running an Euler or a RANS calculation",
+ default_value=TEMPLATE_TYPE,
+ unit=None,
+ descr="Running EULER or RANS calculation",
xpath=SU2_CONFIG_RANS_XPATH,
- gui=True,
- gui_name="Euler or RANS simulation",
+ gui=include_gui,
+ gui_name="EULER or RANS simulation",
gui_group="SU2 Parameters",
)
cpacs_inout.add_input(
var_name="max_iter",
var_type=int,
- default_value=200,
- unit="1",
+ default_value=1,
+ unit=None,
descr="Maximum number of iterations performed by SU2",
xpath=SU2_MAX_ITER_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Maximum iterations",
gui_group="SU2 Parameters",
)
@@ -232,22 +183,22 @@
var_name="cfl_nb",
var_type=float,
default_value=1.0,
- unit="1",
+ unit=None,
descr="CFL Number, Courant–Friedrichs–Lewy condition",
xpath=SU2_CFL_NB_XPATH,
- gui=False,
+ gui=include_gui,
gui_name="CFL Number",
gui_group="SU2 Parameters",
)
cpacs_inout.add_input(
var_name="cfl_adapt",
- var_type=bool,
- default_value=False,
- unit="1",
+ var_type=list,
+ default_value=NO_YES_LIST,
+ unit=None,
descr="CFL Adaptation",
xpath=SU2_CFL_ADAPT_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="CFL Adaptation",
gui_group="SU2 Parameters",
)
@@ -256,10 +207,10 @@
var_name="cfl_adapt_param_factor_down",
var_type=float,
default_value=0.5,
- unit="1",
+ unit=None,
descr="CFL Adaptation Factor Down",
xpath=SU2_CFL_ADAPT_PARAM_DOWN_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="CFL Adaptation Factor Down",
gui_group="SU2 Parameters",
)
@@ -268,10 +219,10 @@
var_name="cfl_adapt_param_factor_up",
var_type=float,
default_value=1.5,
- unit="1",
+ unit=None,
descr="CFL Adaptation Factor Up",
xpath=SU2_CFL_ADAPT_PARAM_UP_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="CFL Adaptation Factor Up",
gui_group="SU2 Parameters",
)
@@ -280,10 +231,10 @@
var_name="cfl_adapt_param_min",
var_type=float,
default_value=0.5,
- unit="1",
+ unit=None,
descr="CFL Minimum Value",
xpath=SU2_CFL_MIN_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="CFL Min Value",
gui_group="SU2 Parameters",
)
@@ -292,10 +243,10 @@
var_name="cfl_adapt_param_max",
var_type=float,
default_value=100,
- unit="1",
+ unit=None,
descr="CFL Maximum Value",
xpath=SU2_CFL_MAX_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="CFL Max Value",
gui_group="SU2 Parameters",
)
@@ -304,34 +255,22 @@
var_name="mg_level",
var_type=int,
default_value=3,
- unit="3",
+ unit=None,
descr="Multi-grid level (0 = no multigrid)",
xpath=SU2_MG_LEVEL_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Multigrid Level",
gui_group="SU2 Parameters",
)
-cpacs_inout.add_input(
- var_name="su2_mesh_path",
- var_type="pathtype",
- default_value="-",
- unit="1",
- descr="Absolute path of the SU2 mesh",
- xpath=SU2MESH_XPATH,
- gui=True,
- gui_name="SU2 Mesh",
- gui_group="Inputs",
-)
-
cpacs_inout.add_input(
var_name="update_wetted_area",
var_type=bool,
default_value=True,
- unit="1",
+ unit=None,
descr="Option to update the wetted area from the latest SU2 result.",
xpath=SU2_UPDATE_WETTED_AREA_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Update Wetted Area",
gui_group="Results",
)
@@ -340,10 +279,10 @@
var_name="check_extract_loads",
var_type=bool,
default_value=False,
- unit="1",
+ unit=None,
descr="Option to extract loads (forces in each point) from results",
xpath=SU2_EXTRACT_LOAD_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Extract loads",
gui_group="Results",
)
@@ -354,10 +293,10 @@
var_name="include_actuator_disk",
var_type=bool,
default_value=False,
- unit="1",
+ unit=None,
descr="To check if actuator disk(s) should be included in the SU2 calculation",
xpath=SU2_ACTUATOR_DISK_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Include actuator disk(s)",
gui_group="Actuator disk",
)
@@ -368,8 +307,8 @@
default_value=3000,
unit="N",
descr="Aircraft thrust",
- xpath=PROP_XPATH + "/propeller/thrust",
- gui=True,
+ xpath=PROPELLER_THRUST_XPATH,
+ gui=include_gui,
gui_name="Thrust",
gui_group="Actuator disk",
)
@@ -381,7 +320,7 @@
# unit="1/s",
# descr="Propeller rotational velocity",
# xpath=PROP_XPATH + "/propeller/rotational_velocity",
-# gui=True,
+# gui=include_gui,
# gui_name="Rotational velocity setting",
# gui_group="Actuator disk",
# )
@@ -392,8 +331,8 @@
default_value=False,
unit=None,
descr="Enable or disable the tip loss correction of Prandtl",
- xpath=PROP_XPATH + "/propeller/blade/loss",
- gui=True,
+ xpath=PROPELLER_BLADE_LOSS_XPATH,
+ gui=include_gui,
gui_name="Tip loss correction",
gui_group="Actuator disk",
)
@@ -405,13 +344,122 @@
# unit=None,
# descr="Number of propeller blades",
# xpath=PROP_XPATH + "/propeller/bladeNumber",
-# gui=True,
+# gui=include_gui,
# gui_name="Propeller blades numbers",
# gui_group="Actuator disk",
# )
+# Dynamic Stability Settings
+cpacs_inout.add_input(
+ var_name="dot_derivatives",
+ var_type=bool,
+ default_value=False,
+ unit=None,
+ descr="Computing dot derivatives",
+ xpath=SU2_DYNAMICDERIVATIVES_BOOL_XPATH,
+ gui=include_gui,
+ gui_name="Compute derivatives",
+ gui_group="Dynamic Stability Settings",
+)
-# ----- Output -----
+cpacs_inout.add_input(
+ var_name="time_steps",
+ var_type=int,
+ default_value=20,
+ unit=None,
+ descr="Size of time vector i.e. t = 2pi * (0, 1/n-1, ..., n-2/n-1)",
+ xpath=SU2_DYNAMICDERIVATIVES_TIMESIZE_XPATH,
+ gui=include_gui,
+ gui_name="Time Size (n > 1)",
+ gui_group="Dynamic Stability Settings",
+)
+
+cpacs_inout.add_input(
+ var_name="amplitude",
+ var_type=float,
+ default_value=1.0,
+ unit="[deg]",
+ descr="Oscillation: a * sin(w t) and a > 0",
+ xpath=SU2_DYNAMICDERIVATIVES_AMPLITUDE_XPATH,
+ gui=include_gui,
+ gui_name="Oscillation's amplitude (a): ",
+ gui_group="Dynamic Stability Settings",
+)
+
+cpacs_inout.add_input(
+ var_name="angular_frequency",
+ var_type=float,
+ default_value=0.087,
+ unit="[rad/s]",
+ descr="Oscillation: a * sin(w t) and w > 0",
+ xpath=SU2_DYNAMICDERIVATIVES_FREQUENCY_XPATH,
+ gui=include_gui,
+ gui_name="Oscillation's angular frequency (w)",
+ gui_group="Dynamic Stability Settings",
+)
+
+cpacs_inout.add_input(
+ var_name="inner_iter",
+ var_type=int,
+ default_value=10,
+ unit=None,
+ descr="Per time step, the maximum number of iterations the solver will use.",
+ xpath=SU2_DYNAMICDERIVATIVES_INNERITER_XPATH,
+ gui=include_gui,
+ gui_name="Maximum number of inner iterations",
+ gui_group="Dynamic Stability Settings",
+)
+cpacs_inout.add_input(
+ var_name="cruise_mach",
+ var_type=float,
+ default_value=0.78,
+ unit=None,
+ descr="Aircraft cruise Mach number",
+ xpath=RANGE_CRUISE_MACH_XPATH,
+ gui=include_gui,
+ gui_name="Cruise Mach",
+ gui_group="If fixed CL",
+)
+
+cpacs_inout.add_input(
+ var_name="cruise_alt",
+ var_type=float,
+ default_value=120000.0,
+ unit="m",
+ descr="Aircraft cruise altitude",
+ xpath=RANGE_CRUISE_ALT_XPATH,
+ gui=include_gui,
+ gui_name="Cruise Altitude",
+ gui_group="If fixed CL",
+)
+
+cpacs_inout.add_input(
+ var_name="target_cl",
+ var_type=float,
+ default_value=1.0,
+ unit=None,
+ descr="Value of CL to achieve to have a level flight with the given conditions",
+ xpath=SU2_TARGET_CL_XPATH,
+ gui=include_gui,
+ gui_name="Target CL value",
+ gui_group="If fixed CL",
+)
+
+cpacs_inout.add_input(
+ var_name="fixed_cl",
+ var_type=list,
+ default_value=NO_YES_LIST,
+ unit=None,
+ descr="FIXED_CL_MODE parameter for SU2",
+ xpath=SU2_FIXED_CL_XPATH,
+ gui=include_gui,
+ gui_name="Fixed CL value",
+ gui_group="If fixed CL",
+)
+
+# ==============================================================================
+# GUI OUTPUTS
+# ==============================================================================
cpacs_inout.add_output(
var_name="wetted_area",
@@ -426,7 +474,7 @@
var_name="bc_wall_list",
var_type=list,
default_value=None,
- unit="1",
+ unit=None,
descr="Wall boundary conditions found in the SU2 mesh",
xpath=SU2_BC_WALL_XPATH,
)
@@ -435,7 +483,7 @@
var_name="bc_farfield_list",
var_type=list,
default_value=None,
- unit="1",
+ unit=None,
descr="Farfield boundary conditions found in the SU2 mesh (for off engines)",
xpath=SU2_BC_FARFIELD_XPATH,
)
@@ -448,3 +496,11 @@
descr="aeroMap with aero coefficients calculated by SU2",
xpath=AEROPERFORMANCE_XPATH + "/aeroMap/aeroPerformanceMap",
)
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/SU2Run/files/config_template_euler.cfg b/ceasiompy/SU2Run/files/cfg_tpl_euler.cfg
similarity index 98%
rename from ceasiompy/SU2Run/files/config_template_euler.cfg
rename to ceasiompy/SU2Run/files/cfg_tpl_euler.cfg
index d4b35df00..98f42cb15 100644
--- a/ceasiompy/SU2Run/files/config_template_euler.cfg
+++ b/ceasiompy/SU2Run/files/cfg_tpl_euler.cfg
@@ -44,7 +44,7 @@ FREESTREAM_TEMPERATURE= 288.15
% ---------------------- REFERENCE VALUE DEFINITION ---------------------------%
%
% Reference origin for moment computation
-REF_ORIGIN_MOMENT_X = 0.25
+REF_ORIGIN_MOMENT_X = 0.00
REF_ORIGIN_MOMENT_Y = 0.00
REF_ORIGIN_MOMENT_Z = 0.00
%
@@ -210,7 +210,7 @@ CONV_RESIDUAL_MINVAL= -12
CONV_STARTITER= 25
%
% Number of elements to apply the criteria
-CONV_CAUCHY_ELEMS= 100
+CONV_CAUCHY_ELEMS= 1000
%
% Epsilon to control the series convergence
CONV_CAUCHY_EPS= 1E-10
@@ -230,7 +230,7 @@ SOLUTION_FILENAME= solution_flow.dat
% Restart adjoint input file
SOLUTION_ADJ_FILENAME= solution_adj.dat
%
-% Mesh input file format (SU2)
+% Mesh input file format (SU2, CGNS)
MESH_FORMAT= SU2
%
TABULAR_FORMAT= CSV
@@ -266,7 +266,7 @@ OUTPUT_WRT_FREQ= 100
% Screen output fields
SCREEN_OUTPUT = (INNER_ITER, RMS_DENSITY, RMS_ENERGY, LIFT, DRAG)
%
-OUTPUT_FILES= (RESTART_ASCII, CGNS, SURFACE_CGNS)
+OUTPUT_FILES= (RESTART_ASCII, SU2, SURFACE_SU2)
% ----------------------- DESIGN VARIABLE PARAMETERS --------------------------%
%
% Kind of deformation (TRANSLATION, ROTATION, SCALE,
@@ -320,7 +320,7 @@ DEFORM_CONSOLE_OUTPUT= NO
% Minimum residual criteria for the linear solver convergence of grid deformation
DEFORM_LINEAR_SOLVER_ERROR= 1E-14
%
-% Type of element stiffness imposed for FEA mesh deformation (INVERSE_VOLUME,
+% Type of element stiffness imposed for FEA mesh deformation (INVERSE_VOLUME,
% WALL_DISTANCE, CONSTANT_STIFFNESS)
DEFORM_STIFFNESS_TYPE= INVERSE_VOLUME
@@ -348,7 +348,7 @@ FFD_CONTINUITY= 2ND_DERIVATIVE
% --------------------- OPTIMAL SHAPE DESIGN DEFINITION -----------------------%
%
% Available flow based objective functions or constraint functions
-% DRAG, LIFT, SIDEFORCE, EFFICIENCY, BUFFET,
+% DRAG, LIFT, SIDEFORCE, EFFICIENCY, BUFFET,
% FORCE_X, FORCE_Y, FORCE_Z,
% MOMENT_X, MOMENT_Y, MOMENT_Z,
% THRUST, TORQUE, FIGURE_OF_MERIT,
@@ -393,10 +393,10 @@ OPT_OBJECTIVE= DRAG * 1.0
%
% Optimization constraint functions with scaling factors, separated by semicolons
% ex= (Objective = Value ) * Scale, use '>','<','='
-OPT_CONSTRAINT= ( LIFT > 0.282557 ) * 1.0
-%
+OPT_CONSTRAINT= ( DRAG > 0.0 ) * 1.0
+
% Maximum number of iterations
-OPT_ITERATIONS= 100
+OPT_ITERATIONS= 1000
%
% Requested accuracy
OPT_ACCURACY= 1E-6
diff --git a/ceasiompy/SU2Run/files/config_template_rans.cfg b/ceasiompy/SU2Run/files/cfg_tpl_rans.cfg
similarity index 99%
rename from ceasiompy/SU2Run/files/config_template_rans.cfg
rename to ceasiompy/SU2Run/files/cfg_tpl_rans.cfg
index 1af0ceee0..3b6dc6719 100644
--- a/ceasiompy/SU2Run/files/config_template_rans.cfg
+++ b/ceasiompy/SU2Run/files/cfg_tpl_rans.cfg
@@ -88,7 +88,7 @@ ITER = 5000
% FORCE_X, FORCE_Y, FORCE_Z, THRUST,
% TORQUE, TOTAL_HEATFLUX,
% MAXIMUM_HEATFLUX, INVERSE_DESIGN_PRESSURE,
-% INVERSE_DESIGN_HEATFLUX, SURFACE_TOTAL_PRESSURE,
+% INVERSE_DESIGN_HEATFLUX, SURFACE_TOTAL_PRESSURE,
% SURFACE_MASSFLOW, SURFACE_STATIC_PRESSURE, SURFACE_MACH)
% For a weighted sum of objectives: separate by commas, add OBJECTIVE_WEIGHT and MARKER_MONITORING in matching order.
OBJECTIVE_FUNCTION= DRAG
diff --git a/ceasiompy/SU2Run/func/__init__.py b/ceasiompy/SU2Run/func/__init__.py
index e69de29bb..79cc07eac 100644
--- a/ceasiompy/SU2Run/func/__init__.py
+++ b/ceasiompy/SU2Run/func/__init__.py
@@ -0,0 +1,19 @@
+
+# utils.py
+AERO_COEFFICIENTS = {
+ "Total CL:": "cl",
+ "Total CD:": "cd",
+ "Total CSF:": "cs",
+ "Total CMx:": "cmd",
+ "Total CMy:": "cms",
+ "Total CMz:": "cml",
+ "Free-stream velocity": "velocity"
+}
+SU2_FORCES_MOM = {
+ "Total CFx:": "cfx",
+ "Total CFy:": "cfy",
+ "Total CFz:": "cfz",
+ "Total CMx:": "cmx",
+ "Total CMy:": "cmy",
+ "Total CMz:": "cmz",
+}
diff --git a/ceasiompy/SU2Run/func/su2actuatordiskfile.py b/ceasiompy/SU2Run/func/actuatordiskfile.py
similarity index 52%
rename from ceasiompy/SU2Run/func/su2actuatordiskfile.py
rename to ceasiompy/SU2Run/func/actuatordiskfile.py
index cbb1a76cc..de38859c8 100644
--- a/ceasiompy/SU2Run/func/su2actuatordiskfile.py
+++ b/ceasiompy/SU2Run/func/actuatordiskfile.py
@@ -5,16 +5,14 @@
Functions to generate the actuator disk file for SU2.
-Source: https://github.com/su2code/SU2/blob/master/TestCases/rans/
- actuatordisk_variable_load/ActuatorDisk.dat
+Source:
+ https://github.com/su2code/SU2/blob/master/TestCases/rans/actuatordisk_variable_load/ActuatorDisk.dat
-Python version: >=3.8
| Author : Aidan Jungo and Giacomo Benedetti
| Creation: 2022-12-05
-
-TODO:
-
+| Modified: Leon Deligny
+| Date: 24-Feb-2025
"""
@@ -23,100 +21,124 @@
# =================================================================================================
import math
-from pathlib import Path
-import matplotlib.pyplot as plt
import numpy as np
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.ceasiompyutils import get_results_directory
-from markdownpy.markdownpy import MarkdownDoc
-log = get_logger()
+from ceasiompy.utils.ceasiompyutils import get_results_directory
+from pathlib import Path
+from numpy import ndarray
+from markdownpy.markdownpy import MarkdownDoc
-# =================================================================================================
-# CLASSES
-# =================================================================================================
+from typing import (
+ Callable,
+ Tuple,
+ TextIO,
+)
+from ceasiompy import log
# =================================================================================================
# FUNCTIONS
# =================================================================================================
-def get_radial_stations(radius, hub_radius, number_of_stations=40):
- """Function to adimensionalize the radius and remove values smaller than hub radius.
+def get_radial_stations(
+ radius: float,
+ hub_radius: float,
+ number_of_stations: int = 40
+) -> ndarray:
+ """
+ Non-dimensionalize the radius and remove values smaller than hub radius.
Args:
- radius (float): Propeller radius [m]
- hub_radius (float): Hub radius [m]
- number_of_stations (int): Number of radial station, 40 by default
+ radius (float): Propeller radius [m].
+ hub_radius (float): Hub radius [m].
+ number_of_stations (int = 40): Number of radial station.
Returns:
- radial_stations (np.array): adimensional radius along the blade. Multiply by radius to
- get the real radius value
+ radial_stations (ndarray): Non-dimensional radius along the blade.
+
"""
if hub_radius >= radius:
- raise ValueError("hub radius should be smaller than radius")
+ raise ValueError("hub radius should be smaller than radius.")
radial_stations = np.linspace(0, 1, number_of_stations + 1)[1:]
i_hub = (radial_stations >= hub_radius / radius).argmax()
+
return radial_stations[i_hub:]
-def get_advanced_ratio(free_stream_velocity, rotational_velocity, radius):
- """Function to calculate advanced ratio, that is the ratio between velocity
- and rotational velocity, taking in account of the diameter of the propeller
+def get_advanced_ratio(
+ free_stream_velocity: float,
+ rotational_velocity: float,
+ radius: float,
+) -> float:
+ """
+ Calculate advanced ratio (ratio between velocity and rotational velocity),
+ taking in account of the diameter of the propeller.
Args:
- free_stream_velocity (float): Free stream velocity [m/s]
- rotational_velocity (float): Propeller rotational velocity [1/s]
- radius (float): Propeller radius [m]
+ free_stream_velocity (float): Free stream velocity [m/s].
+ rotational_velocity (float): Propeller rotational velocity [1/s].
+ radius (float): Propeller radius [m].
+
+ Returns:
+ (float): Advanced ratio.
+
"""
if rotational_velocity <= 0:
- raise ValueError("Rotational velocity must be positive!")
+ raise ValueError("Rotational velocity must be positive !")
return free_stream_velocity / (rotational_velocity * (2 * radius))
-def axial_interference_function(lagrangian_multiplier, non_dimensional_radius):
- """Function to obtain the array with the different values of axial interference factor at
- every radius
+def axial_interference_function(lag_mult: float, non_dim_radius: float) -> float:
+ """
+ Obtains the array with the different values of
+ axial interference factor at every radius.
+ This vector is used to estimate the losses
+ due to a pre-rotation of the fluid.
Args:
- lagrangian_multiplier (float): lagrangian multiplier, it is used to calculate
- interference factor
- non_dimensional_radius (float): radius adimentionalization made using advanced ratio
+ lag_mult (float): Lagrangian multiplier.
+ non_dim_radius (float): radius adimentionalization made using advanced ratio.
Returns:
- axial_interference_factor (np.array): axial interference factor calculated ad every radius,
- it is used to estimate the losses due to a
- pre-rotation of the fluid
+ axial_interference_factor (float): Calculated at every radius.
+
"""
- axial_interference_factor = (lagrangian_multiplier * non_dimensional_radius**2) / (
- non_dimensional_radius**2 + (1 + lagrangian_multiplier) ** 2
+ axial_interference_factor = (lag_mult * non_dim_radius**2) / (
+ non_dim_radius**2 + (1 + lag_mult) ** 2
)
return axial_interference_factor
def get_prandtl_correction_values(
- radial_stations, prandtl_correction, blades_number, omega, radius, free_stream_velocity
-):
- """Function to correct the values of thrust and power coefficients near the tip, based on
- Prandtl formulation
+ radial_stations: ndarray,
+ prandtl_correction: bool,
+ blades_number: int,
+ omega: float,
+ radius: float,
+ free_stream_velocity: float,
+) -> ndarray:
+ """
+ Correct the values of thrust and power coefficients near the tip,
+ based on Prandtl formulation.
Args:
- prandtl (bool): bool value thanks to which is possible
- to activate or deactivate the correction
- blades_number (int): number of blades of the propeller
- radial_stations (np.array): adimensional radius along the blade. Multiply by radius to
- get the real radius value
- omega (float): rotational velocity multiplied for 2 and pi
- radius (float): radius of the propeller
- free_stream_velocity (float): free stream velocity
+ radial_stations (ndarray): Non-dimensionalized radius along the blade.
+ prandtl_correction (bool): Activate (True) the correction.
+ blades_number (int): Number of blades of the propeller.
+ omega (float): Rotational velocity multiplied for 2 and pi.
+ radius (float): Radius of the propeller.
+ free_stream_velocity (float): Free stream velocity.
+ Returns:
+ (ndarray): Prandtl correction values.
+
"""
if not prandtl_correction:
@@ -132,49 +154,62 @@ def get_prandtl_correction_values(
)
-def get_error(radial_stations_spacing, dCt, total_thrust_coefficient):
- """Function to get the error between calculated total thrust coefficient and the input one
+def get_error(radial_stations_spacing: float, dCt: ndarray,
+ total_thrust_coefficient: float) -> float:
+ """
+ Computes error between calculated and input thrust coefficient.
Args:
radial_stations_spacing (float): spacing between r and r+dr
- dCt (np.array): local thrust coefficient
+ dCt (ndarray): local thrust coefficient
total_thrust_coefficient (float): integration of local thrust coefficient
+ Returns:
+ (float): Difference between calculated and input thrust coefficient.
"""
+ # TODO: Why not return euclidean norm of error ?
return np.sum(radial_stations_spacing * dCt) - total_thrust_coefficient
def get_corrected_axial_factor(
- vectorized_axial_interf_f,
- lagrange_multiplier,
- prandtl_correction_values,
- non_dimensional_radius,
-):
- """Function which correct axial interference factor at every radius with Prandtl correction
+ vectorized_axial_interf_f: Callable[[ndarray, ndarray], ndarray],
+ lag_mult: float,
+ prandtl_correction_values: ndarray,
+ non_dimensional_radius: ndarray,
+) -> ndarray:
+ """
+ Correct axial interference factor at every radius with Prandtl correction.
Args:
- lagrangian_multiplier (float): lagrangian multiplier, it is used to calculate
- interference factor
- non_dimensional_radius (float): radius adimentionalization made using advanced ratio
+ vectorized_axial_interf_f (Callable[[float, float], float]):
+ Function to calculate axial interference factor.
+ lag_mult (float): Lagrangian multiplier.
+ prandtl_correction_values (ndarray): Prandtl correction values.
+ non_dimensional_radius (float): Made using advanced ratio.
"""
return vectorized_axial_interf_f(
- lagrange_multiplier * prandtl_correction_values,
+ lag_mult * prandtl_correction_values,
non_dimensional_radius,
)
-def calculate_radial_thrust_coefs(radial_stations, advanced_ratio, opt_axial_interf_factor):
- """Function to calculate thrust coefficient distribution along the radius
+def calculate_radial_thrust_coefs(
+ radial_stations: ndarray,
+ advanced_ratio: float,
+ opt_axial_interf_factor: ndarray,
+):
+ """
+ alculate thrust coefficient distribution along the radius.
Args:
- radial_stations (np.array): adimensional radius along the blade. Multiply by radius to
- get the real radius value
- advanced_ratio (float): ratio between velocity and rotational velocity
- multiplied by diameter
- opt_axial_interf_factor (np.array): Optimal axial interference factor
+ radial_stations (ndarray): Non-dimensionalized radius along the blade.
+ advanced_ratio (float):
+ Ratio between velocity
+ and rotational velocity multiplied by diameter.
+ opt_axial_interf_factor (ndarray): Optimal axial interference factor.
"""
@@ -187,131 +222,39 @@ def calculate_radial_thrust_coefs(radial_stations, advanced_ratio, opt_axial_int
)
-def save_plots(
- radial_stations,
- radial_thrust_coefs,
- radial_power_coefs,
- non_dimensional_radius,
- optimal_axial_interference_factor,
- optimal_rotational_interference_factor,
- prandtl_correction_values,
- case_dir_path,
- propeller_uid,
-):
- """Function to save plot in result folder
-
- Args:
- radial_stations (np.array): adimensional radius along the blade. Multiply by radius to
- get the real radius value
- radial_thrust_coefs (np.array): radial distribution of thrust coefficient
- radial_power_coefs (np.array): radial distribution of power coefficient
- non_dimensional_radius (float): radius adimentionalization made using advanced ratio
- optimal_axial_interference_factor (np.array): optimal axial interference factor
- optimal_rotational_interference_factor (np.array): optimal rotational interference factor
- prandtl_correction_values (np.array): values of Prandtl correction
- case_dir_path (Path): Path object of the case directory
- propeller_uid (str): Uid of the current propeller
- """
-
- current_dir = Path(case_dir_path, propeller_uid)
- current_dir.mkdir()
- interference_plot_path = Path(current_dir, "interference.png")
- ct_cp_distr_plot_path = Path(current_dir, "radial_thrust_and_power_coefficient.png")
- prandtl_correction_plot_path = Path(current_dir, "prandtl_correction.png")
-
- f1 = plt.figure(1)
- plt.plot(
- radial_stations,
- radial_thrust_coefs,
- "r",
- markersize=4,
- label="$\\frac{dCT}{d\overline{r}}$",
- )
- plt.plot(
- radial_stations,
- radial_power_coefs,
- "k",
- markersize=4,
- label="$\\frac{dCP}{d\overline{r}}$",
- )
- plt.grid(True)
- plt.legend()
- plt.xlabel("$\overline{r}$")
- plt.ylabel("$dC_t$, $dC_p$")
- plt.title("Load Distribution")
-
- f1.savefig(ct_cp_distr_plot_path)
- plt.clf()
-
- f2 = plt.figure(2)
- plt.plot(
- non_dimensional_radius,
- optimal_axial_interference_factor,
- "r",
- markersize=4,
- label="$a$",
- )
- plt.plot(
- non_dimensional_radius,
- optimal_rotational_interference_factor,
- "k",
- markersize=4,
- label="$a^1$",
- )
- plt.grid(True)
- plt.legend(numpoints=3)
- plt.xlabel("$\\frac{2\pi*r}{J}$")
- plt.ylabel("$a$, $a^1$")
- plt.title("Interference Factors")
-
- f2.savefig(interference_plot_path)
- plt.clf()
-
- f3 = plt.figure(3)
- plt.plot(radial_stations, prandtl_correction_values, "k", markersize=4)
- plt.grid(True)
- plt.xlabel("$\overline{r}$")
- plt.ylabel("$F(\overline{r})$")
- plt.title("Tip Loss Prandtl Correction Function")
- f3.savefig(prandtl_correction_plot_path)
- plt.clf()
-
- log.info(f"A plot have been saved at {ct_cp_distr_plot_path}")
- log.info(f"A plot have been saved at {interference_plot_path}")
- log.info(f"A plot have been saved at {prandtl_correction_plot_path}")
-
-
def check_input_output_values(
- radial_stations_spacing,
- radial_power_coefs,
- radial_thrust_coefs,
- free_stream_velocity,
- advanced_ratio,
- radial_stations,
- radius,
- rotational_velocity,
-):
- """Function to control input and output values, these values will be written in markdown file
+ radial_stations_spacing: float,
+ radial_power_coefs: ndarray,
+ radial_thrust_coefs: ndarray,
+ free_stream_velocity: float,
+ advanced_ratio: float,
+ radial_stations: ndarray,
+ radius: float,
+ rotational_velocity: int,
+) -> Tuple[float, float, float, float, float]:
+ """
+ Control input and output values, these values will be written in markdown file.
Args:
- radial_stations_spacing (float): spacing between r and r+dr
- radial_thrust_coefs (np.array): radial distribution of thrust coefficient
- radial_power_coefs (np.array): radial distribution of power coefficient
- free_stream_velocity (float): Cruise velocity [m/s]
- get the real radius value
- advanced_ratio (float): ratio between velocity and rotational velocity
- multiplied by diameter
- radial_stations (np.array): adimensional radius along the blade. Multiply by radius to
- radius (float): Blade radius [m]
- rotational_velocity (int): Blade velocity rotation [1/s]
+ radial_stations_spacing (float): Spacing between r and r+dr.
+ radial_thrust_coefs (ndarray): Radial distribution of thrust coefficient.
+ radial_power_coefs (ndarray): Radial distribution of power coefficient.
+ free_stream_velocity (float): Cruise velocity [m/s] get the real radius value.
+ advanced_ratio (float):
+ Ratio between velocity
+ and rotational velocity multiplied by diameter.
+ radial_stations (ndarray): Non-dimensionalized radius along the blade.
+ radius (float): Blade radius [m].
+ rotational_velocity (int): Blade velocity rotation [1/s].
Returns:
- total_power_coefficient (float): thrust coefficient at every radius [-]
- optimal_total_thrust_coefficient (float): power coefficient at every radius [-]
- delta_pressure (float): adimensional radius [-]
- thrust_density_ratio (float): ratio between thrust and density [N m^3/kg]
- computed_total_thrust_coefficient (float): thrust coefficient calculated in the program [-]
- eta (float): efficiency of the propeller [-]
+ total_power_coefficient (float): Thrust coefficient at every radius [-].
+ optimal_total_thrust_coefficient (float): Power coefficient at every radius [-].
+ delta_pressure (float): Non-dimensionalized radius [-].
+ thrust_density_ratio (float): Ratio between thrust and density [N m^3/kg].
+ computed_total_thrust_coefficient (float):
+ Thrust coefficient calculated in the program [-].
+ eta (float): Efficiency of the propeller [-].
"""
@@ -326,7 +269,8 @@ def check_input_output_values(
# Computation of the thrust over density using the static pressure jump distribution
thrust_density_ratio = np.sum(
- 2 * math.pi * radial_stations * radius**2 * radial_stations_spacing * delta_pressure
+ 2 * math.pi * radial_stations * radius**2
+ * radial_stations_spacing * delta_pressure
)
# Computation of the thrust coefficient using thrust_density_ratio
@@ -335,7 +279,9 @@ def check_input_output_values(
)
# Computation of the efficiency.
- eta = advanced_ratio * (optimal_total_thrust_coefficient / total_power_coefficient)
+ eta = advanced_ratio * (
+ optimal_total_thrust_coefficient / total_power_coefficient
+ )
return (
total_power_coefficient,
@@ -347,31 +293,33 @@ def check_input_output_values(
def thrust_calculator(
- radial_stations,
- total_thrust_coefficient,
- radius,
- free_stream_velocity,
- prandtl_correction,
- blades_number,
- rotational_velocity,
-):
- """Performing of a calculation to obtain thrust and power coefficients distribution
+ radial_stations: ndarray,
+ total_thrust_coefficient: float,
+ radius: float,
+ free_stream_velocity: float,
+ prandtl_correction: bool,
+ blades_number: int,
+ rotational_velocity: int,
+) -> Tuple[float, float, float, float, float, float]:
+ """
+ Performing of a calculation to obtain thrust
+ and power coefficients distribution.
Args:
- radial_stations (np.array): adimensional radius along the blade. Multiply by radius to
- get the real radius value
- total_thrust_coefficient (float): Total thrust coefficient[-]
- radius (float): Blade radius [m]
- free_stream_velocity (float): Cruise velocity [m/s]
- prandtl_correction (bool): Correction for tip losses
- blades_number (int): Blades propeller number[-]
- rotational_velocity (int): Blade velocity rotation [1/s]
+ radial_stations (ndarray): Non-dimensionalized radius along the blade.
+ total_thrust_coefficient (float):
+ Total thrust coefficient [TODO: Find unit].
+ radius (float): Blade radius [m].
+ free_stream_velocity (float): Cruise velocity [m/s].
+ prandtl_correction (bool): Correction for tip losses.
+ blades_number (int): Blades propeller number.
+ rotational_velocity (int): Blade velocity rotation [1/s].
Returns:
- dCt_optimal (float): thrust coefficient at every radius [-]
- dCp (float): power coefficient at every radius [-]
- r (float): adimensional radius [-]
+ TODO: Documentate return of thrust_calculator.
+
"""
+ # TODO: Improve this function...
results_dir = get_results_directory("SU2Run")
md = MarkdownDoc(Path(results_dir, "su2actuatordisk.md"))
@@ -386,7 +334,12 @@ def thrust_calculator(
vectorized_axial_interf_f = np.vectorize(axial_interference_function)
prandtl_correction_values = get_prandtl_correction_values(
- radial_stations, prandtl_correction, blades_number, omega, radius, free_stream_velocity
+ radial_stations,
+ prandtl_correction,
+ blades_number,
+ omega,
+ radius,
+ free_stream_velocity
)
log.info(f"Prandtl correction= {prandtl_correction}")
@@ -434,7 +387,7 @@ def thrust_calculator(
# Compute the error with respect to the thrust coefficient given in input
initial_error = get_error(radial_stations_spacing, dCt_0, total_thrust_coefficient)
- log.info("Start of error calculation")
+ log.info("Start of error calculation.")
# Computation of the second try Lagrange multiplicator
last_lagrange_multiplier = first_lagrange_multiplier + 0.1
@@ -464,7 +417,8 @@ def thrust_calculator(
iteration += 1
# Computation of the new Lagrange multiplicator value based on the false position method
new_lagrange_multiplier = (
- last_lagrange_multiplier * initial_error - first_lagrange_multiplier * old_error
+ last_lagrange_multiplier * initial_error
+ - first_lagrange_multiplier * old_error
) / (initial_error - old_error)
# Computation of the new axial interference factor distribution
@@ -490,7 +444,7 @@ def thrust_calculator(
first_lagrange_multiplier = last_lagrange_multiplier
last_lagrange_multiplier = new_lagrange_multiplier
- log.info("Error has been estimated")
+ log.info("Error has been estimated.")
# Calculate radial Thrust coefficient at each stations
optimal_axial_interference_factor = get_corrected_axial_factor(
@@ -514,8 +468,11 @@ def thrust_calculator(
+ (1 + new_lagrange_multiplier * prandtl_correction_values) ** 2
)
)
-
- radial_power_coefs = (radius * 4 * np.pi / (rotational_velocity**3 * (2 * radius) ** 5)) * (
+ # ???
+ radial_power_coefs = (
+ radius * 4 * np.pi
+ / (rotational_velocity**3 * (2 * radius) ** 5)
+ ) * (
free_stream_velocity**3
* (1 + optimal_axial_interference_factor) ** 2
* optimal_axial_interference_factor
@@ -528,7 +485,7 @@ def thrust_calculator(
* (radial_stations * radius) ** 3
)
- log.info("Radial thrust and power coefficients have been estimated")
+ log.info("Finished estimating Radial thrust and power coefficients.")
(
total_power_coefficient,
@@ -536,6 +493,7 @@ def thrust_calculator(
thrust_density_ratio,
computed_total_thrust_coefficient,
eta,
+
) = check_input_output_values(
radial_stations_spacing,
radial_power_coefs,
@@ -565,11 +523,13 @@ def thrust_calculator(
)
-def write_header(file):
+def write_header(file: TextIO) -> TextIO:
"""Write the header of the actuator disk file.
Args:
- file (file): File in which the header will be written.
+ file (file): Input file in which the header will be written.
+ Returns:
+ file (file): Output file in which the header is written.
"""
@@ -593,42 +553,47 @@ def write_header(file):
]
file.writelines(header_lines)
-
log.info("ActuatorDisk.dat header has been written")
return file
def write_actuator_disk_data(
- file,
- inlet_marker,
- outlet_marker,
- center,
- axis,
- radius,
- advanced_ratio,
- radial_stations,
- radial_thrust_coefs,
- radial_power_coefs,
-):
- """Function to create a ActuatorDisk.dat file which is used to when rotor/propeller are define
- in the CPACS file.
+ file: TextIO,
+ inlet_marker: str,
+ outlet_marker: str,
+ center: Tuple,
+ axis: Tuple,
+ radius: float,
+ advanced_ratio: float,
+ radial_stations: ndarray,
+ radial_thrust_coefs: ndarray,
+ radial_power_coefs: ndarray,
+) -> None:
+ """
+ Updates file with rotor or propeller definitions from CPACS file.
Args:
- file (file): File to write the actuator disk file
- inlet_marker (str): Marker of the inlet of the actuator disk
- outlet_marker (str): Marker of the outlet of the actuator disk
- center (tuple): Center of the actuator disk (x,y,z)
- axis (tuple): Axis of the actuator disk (x,y,z)
- radius (float): Radius of the actuator disk
- advanced_ratio (float): Advance ratio
- radial_stations (np.array): adimensional radius along the blade. Multiply by radius to
- get the real radius value
- radial_thrust_coefs (np.array): Thrust coefficient at each radial_stations
- radial_power_coefs (np.array): Power coefficient at each radial_stations
+ file (file): Input file to update.
+ inlet_marker (str): Marker of the inlet of the actuator disk.
+ outlet_marker (str): Marker of the outlet of the actuator disk.
+ center (tuple): Center of the actuator disk (x,y,z).
+ axis (tuple): Axis of the actuator disk (x,y,z).
+ radius (float): Radius of the actuator disk.
+ advanced_ratio (float): Advance ratio.
+ radial_stations (ndarray): Non-dimensionalized radius along the blade.
+ radial_thrust_coefs (ndarray): Thrust coefficient at each radial_stations.
+ radial_power_coefs (ndarray): Power coefficient at each radial_stations.
+
+ Returns:
+ file (TextIO): Output file where the actuator disk file is written.
+
"""
- total_thrust_coefficient = sum(radial_thrust_coefs * (radial_stations[1] - radial_stations[0]))
+ total_thrust_coefficient = sum(
+ radial_thrust_coefs * (radial_stations[1] - radial_stations[0])
+ )
+
file.write(f"# Total thurst coefficient= {total_thrust_coefficient:.5f}\n")
file.write(f"MARKER_ACTDISK= {inlet_marker} {outlet_marker}\n")
@@ -644,15 +609,12 @@ def write_actuator_disk_data(
file.write("\n")
- log.info("ActuatorDisk.dat file generated!")
-
- return file
-
+ log.info("ActuatorDisk.dat file generated.")
# =================================================================================================
# MAIN
# =================================================================================================
-if __name__ == "__main__":
- print("Nothing to execute!")
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/SU2Run/func/config.py b/ceasiompy/SU2Run/func/config.py
new file mode 100644
index 000000000..5a27b3563
--- /dev/null
+++ b/ceasiompy/SU2Run/func/config.py
@@ -0,0 +1,796 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Function generate or modify SU2 configuration files
+
+
+| Author: Aidan Jungo
+| Creation: 2020-02-24
+| Modified: Leon Deligny
+| Date: 24-Feb-2025
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+import numpy as np
+
+from shutil import copyfile
+from ceasiompy.SU2Run.func.plot import save_plots
+from ceasiompy.CPACS2GMSH.func.mesh_sizing import wings_size
+from ceasiompy.utils.geometryfunctions import get_main_wing_le
+from ceasiompy.SU2Run.func.dotderivatives import load_parameters
+
+from ceasiompy.utils.ceasiompyutils import (
+ bool_,
+ get_aeromap_conditions,
+)
+from cpacspy.cpacsfunctions import (
+ get_value,
+ open_tigl,
+ create_branch,
+ get_value_or_default,
+)
+from ceasiompy.SU2Run.func.actuatordiskfile import (
+ write_header,
+ thrust_calculator,
+ get_advanced_ratio,
+ get_radial_stations,
+ write_actuator_disk_data,
+)
+from ceasiompy.SU2Run.func.utils import (
+ su2_format,
+ validate_file,
+ get_su2_cfg_tpl,
+ get_mesh_markers,
+ su2_mesh_list_from_db,
+ check_control_surface,
+ add_damping_derivatives,
+ get_surface_pitching_omega,
+)
+
+from pathlib import Path
+from ambiance import Atmosphere
+from cpacspy.cpacspy import CPACS
+from tixi3.tixi3wrapper import Tixi3
+from cpacspy.rotorcraft import Rotorcraft
+from ceasiompy.utils.configfiles import ConfigFile
+from typing import (
+ Dict,
+ List,
+ Tuple,
+)
+
+from ceasiompy import log
+from ceasiompy.SU2Run import CONTROL_SURFACE_LIST
+
+from ceasiompy.utils.commonnames import (
+ CONFIG_CFD_NAME,
+ ACTUATOR_DISK_FILE_NAME,
+ SU2_FORCES_BREAKDOWN_NAME,
+ ACTUATOR_DISK_INLET_SUFFIX,
+ ACTUATOR_DISK_OUTLET_SUFFIX,
+
+)
+from ceasiompy.utils.commonxpath import (
+ SU2MESH_XPATH,
+ USED_SU2_MESH_XPATH,
+ SU2_CFL_NB_XPATH,
+ ENGINE_TYPE_XPATH,
+ SU2_BC_WALL_XPATH,
+ SU2_CFL_MAX_XPATH,
+ SU2_CFL_MIN_XPATH,
+ SU2_FIXED_CL_XPATH,
+ SU2_MAX_ITER_XPATH,
+ SU2_MG_LEVEL_XPATH,
+ SU2_CFL_ADAPT_XPATH,
+ SU2_TARGET_CL_XPATH,
+ SU2_DAMPING_DER_XPATH,
+ SU2_AEROMAP_UID_XPATH,
+ SU2_BC_FARFIELD_XPATH,
+ PROPELLER_THRUST_XPATH,
+ SU2_ACTUATOR_DISK_XPATH,
+ SU2_ROTATION_RATE_XPATH,
+ PROPELLER_BLADE_LOSS_XPATH,
+ SU2_CFL_ADAPT_PARAM_UP_XPATH,
+ SU2_CFL_ADAPT_PARAM_DOWN_XPATH,
+ ENGINE_BC_PRESSUREOUTLET_XPATH,
+ ENGINE_BC_TEMPERATUREOUTLET_XPATH,
+ SU2_DYNAMICDERIVATIVES_INNERITER_XPATH,
+)
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def add_actuator_disk(
+ cfg: ConfigFile,
+ tixi: Tixi3,
+ case_dir_path: Path,
+ actuator_disk_file: Path,
+ mesh_markers: Dict,
+ alt: float,
+ mach: float
+) -> None:
+ """
+ Add actuator disk parameter to the config file.
+
+ Args:
+ cfg (ConfigFile): ConfigFile object.
+ cpacs (CPACS): CPACS object from cpacspy library.
+ case_dir_path (Path): Path object to the current case directory.
+ actuator_disk_file (Path): Path to the actuator disk file.
+ mesh_markers (dict): Dictionary containing all the mesh markers.
+ alt (float): Altitude in meters.
+ mach (float): Mach number.
+
+ """
+
+ # Access markers
+ ad_inlet_marker = sorted(mesh_markers["actuator_disk_inlet"])
+ ad_outlet_marker = sorted(mesh_markers["actuator_disk_outlet"])
+
+ if "None" in ad_inlet_marker or "None" in ad_outlet_marker:
+ # No markers to add so return cfg
+ return cfg
+
+ if len(ad_inlet_marker) != len(ad_outlet_marker):
+ raise ValueError(
+ "The number of inlet and outlet markers of the actuator disk must be the same."
+ )
+
+ # Get rotorcraft configuration (propeller)
+ try:
+ tigl_rotor = open_tigl(tixi, rotorcraft=True)
+ rotorcraft = Rotorcraft(tixi, tigl_rotor)
+ rotorcraft_config = rotorcraft.configuration
+ except AttributeError:
+ raise ValueError(
+ "The actuator disk is defined but no rotorcraft "
+ "configuration is defined in the CPACS file."
+ )
+
+ rotor_uid_pos = {}
+ for i in range(1, rotorcraft_config.get_rotor_count() + 1):
+ rotor = rotorcraft_config.get_rotor(i)
+
+ rotor_uid = rotor.get_uid()
+ pos_x = rotor.get_translation().x
+ pos_y = rotor.get_translation().y
+ pos_z = rotor.get_translation().z
+ radius = rotor.get_radius()
+ hub_radius = 0.0 # TODO: get correctly from CPACS
+
+ rotor_xpath = tixi.uIDGetXPath(rotor_uid)
+
+ number_of_blades_xpath = (
+ rotor_xpath + "/rotorHub/rotorBladeAttachments/rotorBladeAttachment/numberOfBlades"
+ )
+ number_of_blades = get_value_or_default(tixi, number_of_blades_xpath, 3)
+
+ # TODO: this is the nominal speed, how to get a speed which correspond to
+ # each flight condition
+ rotational_velocity_xpath = rotor_xpath + "/nominalRotationsPerMinute"
+ rotational_velocity = (
+ get_value_or_default(tixi, rotational_velocity_xpath, 3000) / 60.0
+ )
+
+ rotor_uid_pos[rotor_uid] = (
+ pos_x,
+ pos_y,
+ pos_z,
+ radius,
+ hub_radius,
+ number_of_blades,
+ rotational_velocity,
+ )
+
+ cfg["ACTDISK_DOUBLE_SURFACE"] = "YES"
+ cfg["ACTDISK_TYPE"] = "VARIABLE_LOAD"
+ cfg["ACTDISK_FILENAME"] = ACTUATOR_DISK_FILE_NAME
+ cfg["MGLEVEL"] = 0 # Calculation diverges if multigrid is used with a disk actuator
+
+ actdisk_markers = []
+
+ f = open(actuator_disk_file, "w")
+ f = write_header(f)
+
+ for maker_inlet, marker_outlet in zip(ad_inlet_marker, ad_outlet_marker):
+ inlet_uid = maker_inlet.split(ACTUATOR_DISK_INLET_SUFFIX)[0]
+ outlet_uid = marker_outlet.split(ACTUATOR_DISK_OUTLET_SUFFIX)[0]
+
+ if inlet_uid != outlet_uid:
+ raise ValueError("The inlet and outlet markers of the actuator disk must be the same.")
+
+ if "_mirrored" in maker_inlet:
+ uid = inlet_uid.split("_mirrored")[0]
+ sym = -1
+ else:
+ uid = inlet_uid
+ sym = 1
+
+ center = []
+ center.append(round(rotor_uid_pos[uid][0], 5))
+ center.append(round(sym * rotor_uid_pos[uid][1], 5))
+ center.append(round(rotor_uid_pos[uid][2], 5))
+
+ axis = (1.0, 0.0, 0.0) # TODO: get the axis by applying the rotation matrix
+ radius = round(rotor_uid_pos[uid][3], 5)
+ hub_radius = round(rotor_uid_pos[uid][4], 5)
+ number_of_blades = round(rotor_uid_pos[uid][5], 5)
+ rotational_velocity = round(rotor_uid_pos[uid][6], 5)
+
+ actdisk_markers.extend([
+ maker_inlet, marker_outlet,
+ str(center[0]), str(center[1]), str(center[2]),
+ str(center[0]), str(center[1]), str(center[2]),
+ ])
+
+ Atm = Atmosphere(alt)
+ free_stream_velocity = mach * Atm.speed_of_sound[0]
+
+ radial_stations = get_radial_stations(radius, hub_radius)
+ advanced_ratio = get_advanced_ratio(free_stream_velocity, rotational_velocity, radius)
+
+ prandtl_correction = bool_(get_value(tixi, PROPELLER_BLADE_LOSS_XPATH))
+
+ thrust = get_value(tixi, PROPELLER_THRUST_XPATH)
+ total_thrust_coefficient = float(
+ thrust / (Atm.density * rotational_velocity**2 * (radius * 2) ** 4)
+ )
+
+ (
+ radial_thrust_coefs,
+ radial_power_coefs,
+ non_dimensional_radius,
+ optimal_axial_interference_factor,
+ optimal_rotational_interference_factor,
+ prandtl_correction_values,
+
+ ) = thrust_calculator(
+ radial_stations,
+ total_thrust_coefficient,
+ radius,
+ free_stream_velocity,
+ prandtl_correction,
+ number_of_blades,
+ rotational_velocity,
+ )
+
+ save_plots(
+ radial_stations=radial_stations,
+ radial_thrust_coefs=radial_thrust_coefs,
+ radial_power_coefs=radial_power_coefs,
+ non_dimensional_radius=non_dimensional_radius,
+ optimal_axial_interference_factor=optimal_axial_interference_factor,
+ optimal_rotational_interference_factor=optimal_rotational_interference_factor,
+ prandtl_correction_values=prandtl_correction_values,
+ case_dir_path=case_dir_path,
+ propeller_uid=inlet_uid,
+ )
+
+ write_actuator_disk_data(
+ file=f,
+ inlet_marker=maker_inlet,
+ outlet_marker=marker_outlet,
+ center=center,
+ axis=axis,
+ radius=radius,
+ advanced_ratio=advanced_ratio,
+ radial_stations=radial_stations,
+ radial_thrust_coefs=radial_thrust_coefs,
+ radial_power_coefs=radial_power_coefs,
+ )
+
+ cfg["MARKER_ACTDISK"] = su2_format(", ".join(actdisk_markers))
+ f.close()
+
+
+def add_thermodata(
+ cfg: ConfigFile,
+ tixi: Tixi3,
+ alt: float,
+ case_nb: int,
+ alt_list: List,
+) -> None:
+ if tixi.checkElement(ENGINE_TYPE_XPATH):
+ log.info("Adding engine BC to the SU2 config file.")
+ engine_type = get_value(tixi, ENGINE_TYPE_XPATH)
+ log.info(f"Engine type {engine_type}.")
+
+ alt = alt_list[case_nb]
+ Atm = Atmosphere(alt)
+ tot_temp_in = Atm.temperature[0]
+ tot_pressure_in = Atm.pressure[0]
+
+ if len(alt_list) > 1:
+ tot_temp_out = get_value(tixi, ENGINE_BC_TEMPERATUREOUTLET_XPATH).split(";")
+ tot_pressure_out = get_value(tixi, ENGINE_BC_PRESSUREOUTLET_XPATH).split(";")
+ tot_temp_out = tot_temp_out[case_nb]
+ tot_pressure_out = tot_pressure_out[case_nb]
+ else:
+ tot_temp_out = get_value(tixi, ENGINE_BC_TEMPERATUREOUTLET_XPATH)
+ tot_pressure_out = get_value(tixi, ENGINE_BC_PRESSUREOUTLET_XPATH)
+ cfg["INLET_TYPE"] = "TOTAL_CONDITIONS"
+ cfg["MARKER_INLET"] = su2_format(
+ f"INLET_ENGINE, {tot_temp_in}, {tot_pressure_in}, {1},{0},{0}, "
+ f"OUTLET_ENGINE, {tot_temp_out}, {tot_pressure_out}, {1},{0},{0}"
+ )
+ else:
+ log.warning(f"No engines found at xPath {ENGINE_TYPE_XPATH}.")
+
+
+def add_reynolds_number(alt: float, mach: float, cfg: ConfigFile, cpacs_path: Path) -> None:
+ """
+ In case of RANS simulation, compute and add Reynolds number to configuration file cfg.
+
+ Args:
+ alt (float): Altitude.
+ mach (float): Mach number.
+ cfg (ConfigFile): Configuration file.
+ cpacs_path (Path): CPACS path.
+
+ """
+
+ Atm = Atmosphere(alt)
+
+ # Get speed from Mach Number
+ speed = mach * Atm.speed_of_sound[0]
+
+ ref_chord = wings_size(cpacs_path)[0] / 0.15
+ log.info(f"Reference chord is {ref_chord}.")
+
+ # Reynolds number based on the mean chord
+ reynolds = int(ref_chord * speed / Atm.kinematic_viscosity[0])
+ cfg["REYNOLDS_NUMBER"] = reynolds
+ log.info(f"Reynolds number is {reynolds}.")
+
+
+def add_case_data(
+ tixi: Tixi3,
+ wkdir: Path,
+ cfg: ConfigFile,
+ cpacs_path: Path,
+ rans: bool,
+ mesh_markers: Dict,
+ case_dir_name: str,
+ mach: float,
+ alt: float,
+ case_nb: int,
+ alt_list: List,
+ ctrlsurf: str,
+) -> None:
+ """
+ Adds case-specific data to the SU2 configuration file and sets up the case directory.
+
+ Args:
+ cpacs (CPACS): CPACS object containing the CPACS data.
+ wkdir (Path): Working directory path.
+ cfg (ConfigFile): SU2 configuration file object.
+ cpacs_path (Path): Path to the CPACS file.
+ rans (bool): Flag indicating if RANS (Reynolds-Averaged Navier-Stokes) is used.
+ mesh_markers (Dict): Dictionary containing mesh markers.
+ case_dir_name (str): Name of the case directory.
+ mach (float): Mach number for the case.
+ alt (float): Altitude for the case.
+ case_nb (int): Case number.
+ alt_list (List): List of altitudes.
+
+ """
+
+ add_thermodata(cfg, tixi, alt, case_nb, alt_list)
+
+ if rans:
+ add_reynolds_number(alt, mach, cfg, cpacs_path)
+
+ case_dir_path = Path(wkdir, case_dir_name)
+ if not case_dir_path.exists():
+ case_dir_path.mkdir()
+
+ if bool_(get_value(tixi, SU2_ACTUATOR_DISK_XPATH)):
+ actuator_disk_file = Path(wkdir, ACTUATOR_DISK_FILE_NAME)
+ add_actuator_disk(
+ cfg=cfg,
+ tixi=tixi,
+ case_dir_path=case_dir_path,
+ actuator_disk_file=actuator_disk_file,
+ mesh_markers=mesh_markers,
+ alt=alt,
+ mach=mach,
+ )
+
+ if actuator_disk_file.exists():
+ case_actuator_disk_file = Path(case_dir_path, ACTUATOR_DISK_FILE_NAME)
+ copyfile(actuator_disk_file, case_actuator_disk_file)
+
+ bc_wall_str = su2_format(
+ ",".join(mesh_markers["wall"] + mesh_markers["actuator_disk_inlet"]
+ + mesh_markers["actuator_disk_outlet"])
+ )
+
+ cfg["MARKER_PLOTTING"] = bc_wall_str
+ cfg["MARKER_MONITORING"] = bc_wall_str
+
+ if bool_(get_value(tixi, SU2_DAMPING_DER_XPATH)):
+ rotation_rate = get_value(tixi, SU2_ROTATION_RATE_XPATH)
+ add_damping_derivatives(cfg, wkdir, case_dir_name, rotation_rate)
+
+ ctrlsurf_case_dir_path = Path(case_dir_path, ctrlsurf)
+ if not ctrlsurf_case_dir_path.exists():
+ ctrlsurf_case_dir_path.mkdir()
+
+ config_output_path = Path(ctrlsurf_case_dir_path, CONFIG_CFD_NAME)
+ cfg.write_file(config_output_path, overwrite=True)
+
+
+def configure_unsteady_simulation(
+ cfg: ConfigFile,
+ tixi: Tixi3,
+ oscillation_type: str,
+ mesh_markers: Dict,
+ markers_len: int,
+) -> None:
+ # Center of gravity
+ x1, _, z1, c = get_main_wing_le(tixi)
+ x_cg = x1 + (c / 4)
+
+ # Load parameters
+ a, omega, n, _ = load_parameters(tixi)
+
+ time_step = (2 / (n - 1)) * np.pi / omega
+
+ cfg["TIME_DOMAIN"] = "YES"
+ cfg["TIME_MARCHING"] = "DUAL_TIME_STEPPING-2ND_ORDER"
+ cfg["TIME_STEP"] = str(time_step)
+ cfg["MAX_TIME"] = str(2 * np.pi / omega)
+
+ inner_iter = get_value(tixi, SU2_DYNAMICDERIVATIVES_INNERITER_XPATH)
+ cfg["INNER_ITER"] = str(inner_iter)
+ cfg["UNST_ADJOINT_ITER"] = "0"
+ cfg["OUTPUT_WRT_FREQ"] = "1"
+
+ cfg["TIME_ITER"] = str(2 * np.pi / omega)
+ cfg["CONV_RESIDUAL_MINVAL"] = "-8"
+ cfg["CONV_STARTITER"] = "0"
+ cfg["CONV_CAUCHY_ELEMS"] = "2"
+ cfg["CONV_CAUCHY_EPS"] = "1E-10"
+
+ surf_omega = get_surface_pitching_omega(oscillation_type, omega)
+ cfg["SURFACE_MOVEMENT"] = su2_format(("DEFORMING " * markers_len))
+ cfg["MARKER_MOVING"] = su2_format(",".join(mesh_markers["wall"]))
+ cfg["SURFACE_MOTION_ORIGIN"] = su2_format((f"{x_cg} 0.0 {z1} " * markers_len))
+ cfg["SURFACE_PITCHING_OMEGA"] = su2_format((surf_omega * markers_len))
+ cfg["SURFACE_PITCHING_AMPL"] = su2_format((f"{a} {a} {a} " * markers_len))
+
+
+def configure_freestream(
+ cfg: ConfigFile,
+ alt: float,
+ mach: float,
+ aoa: float,
+ aos: float,
+) -> None:
+ Atm = Atmosphere(alt)
+
+ cfg["MACH_NUMBER"] = mach
+ cfg["AOA"] = aoa
+ cfg["SIDESLIP_ANGLE"] = aos
+ cfg["FREESTREAM_PRESSURE"] = Atm.pressure[0]
+ cfg["FREESTREAM_TEMPERATURE"] = Atm.temperature[0]
+
+
+def configure_cfd_environment(
+ cpacs: CPACS,
+ wkdir: Path,
+ cfg: ConfigFile,
+ su2_mesh_path: Path,
+ rans: bool,
+ dyn_stab: bool,
+ mesh_markers: Dict,
+ ctrlsurf: str,
+) -> None:
+ """
+ Configure cfd environment for SU2 simulation.
+
+ Args:
+ cpacs (CPACS): CPACS file.
+ wkdir (Path): Working directory (SU2).
+ cfg (ConfigFile): Configuration file to modify.
+ su2_mesh (Path): Path to the SU2 mesh file.
+ rans (bool): True if RANS simulation.
+ mesh_markers (Dict): Dictionary of the mesh markers found in the SU2 mesh file.
+
+ """
+ tixi = cpacs.tixi
+ cpacs_path = cpacs.cpacs_file
+
+ alt_list, mach_list, aoa_list, aos_list = get_aeromap_conditions(cpacs, SU2_AEROMAP_UID_XPATH)
+
+ cfg["MARKER_MOVING"] = su2_format("NONE")
+ cfg["MESH_FILENAME"] = str(su2_mesh_path)
+
+ mach_len = len(mach_list)
+
+ # Parameters which will vary for the different cases (alt, mach, aoa, aos)
+ for case_nb in range(mach_len):
+ # Configure aeromaps cases
+ alt, mach = alt_list[case_nb], mach_list[case_nb]
+ aoa, aos = (0.0, 0.0) if dyn_stab else (aoa_list[case_nb], aos_list[case_nb])
+ configure_freestream(cfg, alt, mach, aoa, aos)
+
+ # Oscillatory case
+ if dyn_stab:
+ markers_len = len(mesh_markers["wall"])
+
+ for oscillation_type in ["alpha", "beta"]:
+ configure_unsteady_simulation(
+ cfg, tixi, oscillation_type, mesh_markers, markers_len
+ )
+
+ case_dir_name = (
+ f"Case{str(case_nb + mach_len).zfill(2)}"
+ f"_alt{round(alt, 2)}"
+ f"_mach{round(mach, 2)}"
+ f"_angle{oscillation_type}_dynstab"
+ )
+
+ add_case_data(
+ tixi=tixi,
+ wkdir=wkdir,
+ cfg=cfg,
+ cpacs_path=cpacs_path,
+ rans=rans,
+ mesh_markers=mesh_markers,
+ case_dir_name=case_dir_name,
+ mach=mach,
+ alt=alt,
+ case_nb=case_nb,
+ alt_list=alt_list,
+ ctrlsurf=ctrlsurf,
+ )
+
+ # Stationary case
+ else:
+ case_dir_name = (
+ f"Case{str(case_nb).zfill(2)}_alt{alt}_mach{round(mach, 2)}"
+ f"_aoa{round(aoa, 1)}_aos{round(aos, 1)}"
+ )
+
+ add_case_data(
+ tixi=tixi,
+ wkdir=wkdir,
+ cfg=cfg,
+ cpacs_path=cpacs_path,
+ rans=rans,
+ mesh_markers=mesh_markers,
+ case_dir_name=case_dir_name,
+ mach=mach,
+ alt=alt,
+ case_nb=case_nb,
+ alt_list=alt_list,
+ ctrlsurf=ctrlsurf,
+ )
+
+
+def define_markers(tixi: Tixi3, su2_mesh_path: Path) -> Dict:
+ """
+ Define markers in CPACS file.
+ We assume that among all imported meshes,
+ there will be the same markers.
+
+ Args:
+ tixi (Tixi3): Tixi handle of CPACS file.
+ su2_mesh_path (Path): Path to a mesh.
+
+ """
+ mesh_markers = get_mesh_markers(su2_mesh_path)
+ for key, value in mesh_markers.items():
+ mesh_markers[key] = [str(item).replace(" ", "") for item in value]
+
+ # Generate wall
+ create_branch(tixi, SU2_BC_WALL_XPATH)
+ bc_wall_str = ";".join(mesh_markers["wall"])
+ tixi.updateTextElement(SU2_BC_WALL_XPATH, bc_wall_str)
+
+ # Generate Farfield
+ create_branch(tixi, SU2_BC_FARFIELD_XPATH)
+ bc_farfiled_str = ";".join(
+ mesh_markers["engine_intake"]
+ + mesh_markers["engine_exhaust"]
+ )
+ tixi.updateTextElement(SU2_BC_FARFIELD_XPATH, bc_farfiled_str)
+
+ # Generate Actuator disk
+ create_branch(tixi, SU2_ACTUATOR_DISK_XPATH)
+ bc_actuator_disk_str = ";".join(
+ mesh_markers["actuator_disk_inlet"]
+ + mesh_markers["actuator_disk_outlet"]
+ )
+ tixi.updateTextElement(SU2_ACTUATOR_DISK_XPATH, bc_actuator_disk_str)
+
+ return mesh_markers
+
+
+def load_su2_mesh_paths(tixi: Tixi3, results_dir: Path) -> Tuple[List[Path], List[Path]]:
+ """
+ Retrieve su2 mesh file data and paths.
+ """
+
+ # Using CPACS2Gmsh
+ if tixi.getTextElement(USED_SU2_MESH_XPATH + "type") == "Other":
+ log.info("Using mesh files from CPACS2Gmsh")
+ tixi_su2_mesh_paths = tixi.getTextElement(SU2MESH_XPATH)
+ su2_mesh_paths = [Path(x) for x in str(tixi_su2_mesh_paths).split(';')]
+
+ # Using Specified mesh files from GUI
+ elif tixi.getTextElement(USED_SU2_MESH_XPATH + "type") == "Path":
+ log.info("Using specified mesh paths")
+ tixi_su2_mesh_paths = tixi.getTextElement(USED_SU2_MESH_XPATH)
+ su2_mesh_paths = [Path(x) for x in str(tixi_su2_mesh_paths).split(';')]
+
+ # Using ceasiompy.db
+ elif tixi.getTextElement(USED_SU2_MESH_XPATH + "type") == "db":
+ log.info("Using ceasiompy.db data")
+ su2_mesh_list = su2_mesh_list_from_db(tixi)
+
+ # Upload files to the working directory and update paths
+ su2_mesh_paths = []
+ for (su2_mesh, aircraft_name, deformation, angle) in su2_mesh_list:
+ su2_path = results_dir / f"{aircraft_name}_{deformation}_{angle}.su2"
+
+ with open(su2_path, 'w') as su2_file:
+ su2_file.write(su2_mesh.decode('utf-8'))
+
+ su2_mesh_paths.append(Path(su2_path))
+
+ if not tixi.checkElement(SU2MESH_XPATH):
+ create_branch(tixi, SU2MESH_XPATH)
+
+ log.info(f"su2_mesh_paths {su2_mesh_paths}")
+ # Update tixi element at SU2MESH_XPATH with new paths
+ tixi_su2_mesh_paths = ';'.join(str(su2_mesh_paths))
+ tixi.updateTextElement(SU2MESH_XPATH, tixi_su2_mesh_paths)
+
+ dynstab_su2_mesh_paths = [
+ mesh
+ for mesh in su2_mesh_paths
+ if not any(exclude in str(mesh) for exclude in CONTROL_SURFACE_LIST)
+ ]
+
+ if not su2_mesh_paths:
+ raise ValueError("List of su2 mesh paths is empty.")
+ if not dynstab_su2_mesh_paths:
+ raise ValueError("List of Dynamic Stability su2 mesh paths is empty.")
+
+ return su2_mesh_paths, dynstab_su2_mesh_paths
+
+
+def configure_mesh_format(cfg: ConfigFile, mesh_path: Path) -> None:
+ mesh_name = mesh_path.name
+ if mesh_name.endswith(".cgns"):
+ cfg["MESH_FORMAT"] = "CGNS"
+ return None
+ if not mesh_name.endswith(".su2"):
+ log.warning(
+ "Did not recognize the mesh format. "
+ "Using SU2 as default."
+ )
+ cfg["MESH_FORMAT"] = "SU2"
+
+
+def generate_su2_cfd_config(
+ cpacs: CPACS,
+ wkdir: Path,
+ su2_mesh_paths: List[Path],
+ mesh_markers: Dict,
+ dyn_stab: bool,
+ rans: bool
+) -> None:
+ """
+ Reads data in the CPACS file and generate configuration files
+ for one or multiple flight conditions (alt, mach, aoa, aos).
+
+ Source:
+ * SU2 config template:
+ https://github.com/su2code/SU2/blob/master/config_template.cfg
+
+ """
+ tixi = cpacs.tixi
+
+ for su2_mesh_path in su2_mesh_paths:
+
+ ctrlsurf = check_control_surface(str(su2_mesh_path))
+ log.info(f"Using SU2 mesh at path: {su2_mesh_path}.")
+
+ validate_file(su2_mesh_path, "SU2 mesh")
+
+ fixed_cl = get_value(tixi, SU2_FIXED_CL_XPATH)
+ target_cl = get_value(tixi, SU2_TARGET_CL_XPATH)
+
+ tpl_type = "RANS" if rans else "EULER"
+ cfg = ConfigFile(get_su2_cfg_tpl(tpl_type))
+ configure_mesh_format(cfg, su2_mesh_path)
+
+ if dyn_stab and 'ITER' in cfg.data:
+ cfg.data.pop('ITER', None)
+
+ # General parameters
+ aircraft = cpacs.aircraft
+ cfg["RESTART_SOL"] = "NO"
+ cfg["REF_LENGTH"] = aircraft.ref_length
+ cfg["REF_AREA"] = aircraft.ref_area
+
+ # TODO: Careful as not center gravity.
+ cfg["REF_ORIGIN_MOMENT_X"] = aircraft.ref_point_x
+ cfg["REF_ORIGIN_MOMENT_Y"] = aircraft.ref_point_y
+ cfg["REF_ORIGIN_MOMENT_Z"] = aircraft.ref_point_z
+
+ # SU2 version 8.1.0.
+ cfg["MUSCL_FLOW"] = "NO"
+ cfg["MUSCL_ADJFLOW"] = "NO"
+ cfg["MGLEVEL"] = int(get_value(tixi, SU2_MG_LEVEL_XPATH))
+
+ # Settings
+ cfl_down = get_value(tixi, SU2_CFL_ADAPT_PARAM_DOWN_XPATH)
+ cfl_up = get_value(tixi, SU2_CFL_ADAPT_PARAM_UP_XPATH)
+ cfl_min = get_value(tixi, SU2_CFL_MIN_XPATH)
+ cfl_max = get_value(tixi, SU2_CFL_MAX_XPATH)
+
+ if not dyn_stab:
+ cfg["CFL_ADAPT"] = str(get_value(tixi, SU2_CFL_ADAPT_XPATH))
+ cfg["INNER_ITER"] = int(get_value(tixi, SU2_MAX_ITER_XPATH))
+ cfg["CFL_NUMBER"] = str(get_value(tixi, SU2_CFL_NB_XPATH))
+ cfg["CFL_ADAPT_PARAM"] = su2_format(f"{cfl_down}, {cfl_up}, {cfl_min}, {cfl_max}")
+
+ # Fixed CL mode (AOA will not be taken into account)
+ cfg["FIXED_CL_MODE"] = fixed_cl
+ cfg["TARGET_CL"] = target_cl
+ cfg["DCL_DALPHA"] = "0.1"
+ cfg["UPDATE_AOA_ITER_LIMIT"] = "50"
+ cfg["ITER_DCL_DALPHA"] = "80"
+
+ # Mesh Marker
+ bc_wall_str = su2_format(f"{','.join(mesh_markers['wall'])}")
+
+ cfg["MARKER_EULER"] = bc_wall_str
+ farfield_bc = (
+ mesh_markers["farfield"]
+ + mesh_markers["engine_intake"]
+ + mesh_markers["engine_exhaust"]
+ )
+ cfg["MARKER_FAR"] = su2_format(f"{','.join(farfield_bc)}")
+ cfg["MARKER_SYM"] = su2_format(f"{','.join(mesh_markers['symmetry'])}")
+ cfg["MARKER_PLOTTING"] = bc_wall_str
+ cfg["MARKER_MONITORING"] = bc_wall_str
+ cfg["DV_MARKER"] = bc_wall_str
+
+ # Output
+ cfg["WRT_FORCES_BREAKDOWN"] = "YES"
+ cfg["BREAKDOWN_FILENAME"] = SU2_FORCES_BREAKDOWN_NAME
+ cfg["OUTPUT_FILES"] = su2_format("RESTART, PARAVIEW, SURFACE_PARAVIEW")
+ cfg["HISTORY_OUTPUT"] = su2_format("INNER_ITER, RMS_RES, AERO_COEFF")
+
+ configure_cfd_environment(
+ cpacs=cpacs,
+ wkdir=wkdir,
+ cfg=cfg,
+ su2_mesh_path=su2_mesh_path,
+ rans=rans,
+ dyn_stab=dyn_stab,
+ mesh_markers=mesh_markers,
+ ctrlsurf=ctrlsurf,
+ )
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/SU2Run/func/dotderivatives.py b/ceasiompy/SU2Run/func/dotderivatives.py
new file mode 100644
index 000000000..4bb978993
--- /dev/null
+++ b/ceasiompy/SU2Run/func/dotderivatives.py
@@ -0,0 +1,152 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed for CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Computing derivatives for dynamic stability.
+
+
+| Author: Leon Deligny
+| Creation: 2025-Feb-24
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+import numpy as np
+
+from cpacspy.cpacsfunctions import get_value
+
+from typing import Tuple
+from numpy import ndarray
+from tixi3.tixi3wrapper import Tixi3
+
+from ceasiompy import log
+
+from ceasiompy.utils.commonxpath import (
+ SU2_DYNAMICDERIVATIVES_TIMESIZE_XPATH,
+ SU2_DYNAMICDERIVATIVES_AMPLITUDE_XPATH,
+ SU2_DYNAMICDERIVATIVES_FREQUENCY_XPATH,
+)
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def norm(vector: ndarray) -> float:
+ """
+ Returns Euclidean norm of a vector ndarray.
+
+ Args:
+ vector (ndarray): Input vector on which we take the norm.
+
+ Returns:
+ float: Norm of this vector.
+
+ """
+ return np.linalg.norm(vector)**2
+
+
+def load_parameters(tixi: Tixi3) -> Tuple[float, float, float, ndarray]:
+ """
+ Load parameters for derivatives computation.
+
+ Args:
+ tixi (Tixi3): Tixi handle of CPACS file.
+
+ Returns:
+ Tuple[float, float, float, ndarray]: Oscillation's parameters.
+
+ """
+
+ # Amplitude
+ a = get_value(tixi, SU2_DYNAMICDERIVATIVES_AMPLITUDE_XPATH)
+
+ # Frequency
+ omega = get_value(tixi, SU2_DYNAMICDERIVATIVES_FREQUENCY_XPATH)
+
+ # Time
+ n = int(get_value(tixi, SU2_DYNAMICDERIVATIVES_TIMESIZE_XPATH))
+
+ # Ensure n is odd
+ if n % 2 == 0:
+ n += 1
+
+ # Create time vector t
+ t = np.linspace(0, 2 * np.pi * (n) / (omega * (n + 1)), n + 1)
+
+ return a, omega, n, t
+
+
+def compute_derivatives(
+ a: float,
+ omega: float,
+ t: ndarray,
+ f_time: ndarray,
+ f_static: ndarray,
+) -> Tuple[float, float]:
+ """
+ Compute derivatives through SU2 results.
+
+ Source:
+ https://www.overleaf.com/read/xgnbfvjhvbxp#809b6e
+
+ Args:
+ a (float): Amplitude (alpha_0).
+ omega (float): Angular frequency (w).
+ t (ndarray): Time vector (t_1, ..., t_n).
+ f_time (ndarray): Forces/moments on dynamic object (F(alpha, alpha_dot)(t)).
+ f_static (ndarray): Forces/moments on static object (F(0, 0)).
+
+ Returns:
+ Angle and Angle_dot derivative (alpha, alpha_dot derivatives).
+
+ """
+
+ log.info("t shape: %s", t.shape)
+
+ # Define the variables
+ cwt = np.cos(omega * t)
+ swt = np.sin(omega * t)
+
+ cwt_swt = np.dot(swt, cwt)
+
+ n_cwt = norm(cwt)
+ n_swt = norm(swt)
+
+ # Define the matrix M (corresponds to M^-1 in latex).
+ M = np.array([
+ [n_cwt, -cwt_swt],
+ [-cwt_swt, n_swt]
+ ])
+
+ det_M = n_cwt * n_swt - (cwt_swt ** 2)
+
+ # Differences
+ f = f_time - f_static
+ log.info("f shape: %s", f.shape)
+
+ # Define the vector
+ vector = np.array([
+ np.dot(swt, f),
+ np.dot(cwt, f)
+ ])
+
+ log.info("vector shape: %s", vector.shape)
+
+ x_min, y_min = (det_M / a) * np.dot(M, vector)
+
+ log.info("x_min shape: %s, y_min shape: %s", x_min.shape, y_min.shape)
+
+ return x_min, y_min
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/SU2Run/func/extractloads.py b/ceasiompy/SU2Run/func/extractloads.py
index 7ba9674b7..7f2aa69a4 100644
--- a/ceasiompy/SU2Run/func/extractloads.py
+++ b/ceasiompy/SU2Run/func/extractloads.py
@@ -4,46 +4,35 @@
Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
based on a script from Jan-Niclas Walther (DLR)
-Small description of the script
-
-Python version: >=3.8
| Author: Aidan Jungo
| Creation: 2019-09-24
-TODO:
-
- *
-
"""
# =================================================================================================
# IMPORTS
# =================================================================================================
-from pathlib import Path
-
+import vtk
import numpy as np
import pandas as pd
-import vtk
-from ceasiompy.utils.ceasiomlogger import get_logger
+
+from ceasiompy import log
+from six import iteritems
+from vtk.util.numpy_support import numpy_to_vtk, vtk_to_numpy
+
+from pathlib import Path
+from ceasiompy.utils.configfiles import ConfigFile
+from scipy.sparse import csr_matrix
+from typing import Dict, List
+
from ceasiompy.utils.commonnames import (
CONFIG_CFD_NAME,
FORCE_FILE_NAME,
SURFACE_FLOW_FILE_NAME,
SURFACE_FLOW_FORCE_FILE_NAME,
)
-from ceasiompy.utils.configfiles import ConfigFile
-from scipy.sparse import csr_matrix
-from six import iteritems
-from vtk.util.numpy_support import numpy_to_vtk, vtk_to_numpy
-
-log = get_logger()
-
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
# =================================================================================================
@@ -51,18 +40,17 @@
# =================================================================================================
-def compute_point_normals(coord, cells):
- """Function the normal vectors
-
- Function 'compute_point_normals' computes normals at points weighted by the
- area of the surrounding cells on a triangular mesh.
+def compute_point_normals(coord: np.ndarray, cells: np.ndarray) -> np.ndarray:
+ """
+ Computes normals at points weighted by the area of the surrounding cells on a triangular mesh.
Args:
- coords (array): np.ndarray(n, k) List of n k-dimensional coordinate points
- cells (array): np.ndarray(m, 3) Triangular cell connectivity
+ coords (np.ndarray): (n, k)-dimensional coordinate points.
+ cells (np.ndarray): (m, 3) triangular cell connectivity.
Returns:
- point_nvecs (array): np.ndarray(n, k) List of k-dimensional normal vector at the n points
+ point_nvecs (np.ndarray): (n, k)-dimensional normal vector at the n points.
+
"""
cell_vecs = np.diff(coord[cells], axis=1)
@@ -76,33 +64,27 @@ def compute_point_normals(coord, cells):
return cell_sp.T.dot(cell_nvecs) / 3.0
-def compute_forces(vtu_file_path, force_file_path, config_dict):
- """Function to compute force of a VTU file
-
- Function 'compute_forces' computes surface forces at points for SU2 result
- files.
+def compute_forces(
+ vtu_file_path: str,
+ force_file_path: str,
+ config_dict: Dict,
+) -> vtk.vtkXMLUnstructuredGridReader:
+ """
+ Computes surface forces at points for SU2 result files.
+ Saves forces dataframe at force_file_path.
Args:
- vtu_file_path (str): Path of the VTU file
- force_file_path (str): Path to the results force file to write
- config_dict (dict): SU2 cfg file dictionary to dimensionalize
- non-dimensional output
+ vtu_file_path (str): Path of the VTU file.
+ force_file_path (str): Path to the results force file to write.
+ config_dict (dict): SU2 cfg file dictionary to dimensionalize non-dimensional output.
Returns:
- mesh (vtkhelpers object instance): Python instance of SU2 result file
- with added force and normal vectors
- """
+ mesh (vtk.vtkXMLUnstructuredGridReader): SU2 result file with force and normal vectors.
- # To read .vtk file
- # reader = vtk.vtkUnstructuredGridReader()
- # reader.SetFileName(vtu_file_path)
- # reader.SetReadAllNormals(1)
- # reader.SetReadAllScalars(1)
- # reader.SetReadAllTensors(1)
- # reader.SetReadAllVectors(1)
+ """
# To read .vtu file
- reader = vtk.vtkXMLUnstructuredGridReader() # test
+ reader = vtk.vtkXMLUnstructuredGridReader()
reader.SetFileName(vtu_file_path)
reader.Update()
@@ -112,18 +94,17 @@ def compute_forces(vtu_file_path, force_file_path, config_dict):
cells = vtk_to_numpy(mesh.GetCells().GetData()).reshape(-1, 4)[:, 1:]
point_nvecs = compute_point_normals(coord, cells)
- press = np.ascontiguousarray(
+ # Access pressure at each PointData
+ pressure = np.ascontiguousarray(
vtk_to_numpy(mesh.GetPointData().GetAbstractArray("Pressure"))
).astype(np.double)
- # TODO raine ERROR, now we need config_dict anyway
+ # TODO raise ERROR, now we need config_dict anyway
if config_dict is not None:
- press = dimensionalize_pressure(press, config_dict)
+ pressure = dimensionalize_pressure(pressure, config_dict)
- force = point_nvecs * press[:, None]
+ force = point_nvecs * pressure[:, None]
- # unit_norm = point_nvecs / np.linalg.norm(point_nvecs, axis=1, keepdims=True)
- # # had to chage that with the last version of numpy
unit_norm = point_nvecs / np.linalg.norm(point_nvecs)
for name, values in iteritems({"n": unit_norm, "f": force}):
@@ -138,7 +119,6 @@ def compute_forces(vtu_file_path, force_file_path, config_dict):
ids = range(len(coord))
su2_mesh_path = config_dict.get("MESH_FILENAME")
-
marker_dict = get_mesh_markers_ids(su2_mesh_path)
mesh_maker = []
@@ -171,19 +151,18 @@ def compute_forces(vtu_file_path, force_file_path, config_dict):
return mesh
-def dimensionalize_pressure(p, config_dict):
- """Function to dimensionalize pressure
-
- Function 'dimensionalize_pressure' retrurns the pressures values
- dimensionalize accorind to data from the SU2 configuration file
+def dimensionalize_pressure(p: List, config_dict: Dict) -> List:
+ """
+ Returns the pressures values dimensionalized
+ accoring to data from the SU2 configuration file.
Args:
- p (list): Pressure values
- config_dict (dict): SU2 cfg file dictionary to
- dimensionalize non-dimensional output
+ p (list): Pressure values.
+ config_dict (dict): SU2 cfg file dictionary to dimensionalize non-dimensional output.
Returns:
- p (list): New pressure values
+ p (list): Dimensionalized pressure values.
+
"""
ref_dim = config_dict.get("REF_DIMENSIONALIZATION", "DIMENSIONAL")
@@ -201,23 +180,18 @@ def dimensionalize_pressure(p, config_dict):
return (p * gamma * ma**2 - 1) * p_inf
-def write_updated_mesh(mesh, new_vtu_file_path):
- """Function to write the new VTU file
-
- Function 'write_updated_mesh' crete new VTU file with utdated value given
- by 'mesh' and save at 'new_vtu_file_path'
+def write_updated_mesh(mesh: vtk.vtkXMLUnstructuredGridWriter, new_vtu_file_path: str) -> None:
+ """
+ Create new VTU file with updated value given by mesh
+ and save at new_vtu_file_path.
Args:
- mesh (vtkhelpers object instance): Python instance of SU2 result file
- with added force and normal vectors
- new_vtu_file_path (str): New VTU file path
+ mesh (vtk.vtkXMLUnstructuredGridWriter):
+ Python instance of SU2 result file with added force and normal vectors.
+ new_vtu_file_path (str): New VTU file path.
"""
- # To write .vtk file
- # writer = vtk.vtkUnstructuredGridWriter()
- # writer.SetFileType(0)
-
# To write .vtu file
writer = vtk.vtkXMLUnstructuredGridWriter()
@@ -229,19 +203,19 @@ def write_updated_mesh(mesh, new_vtu_file_path):
writer.SetFileName(new_vtu_file_path)
writer.Update()
-
# TODO: maybe create some exteral function to cope with SU2Mesh, get coord, get marker ...
-def get_mesh_markers_ids(su2_mesh_path):
- """Function to get ids corresponding to each marker
- Function 'get_mesh_markers_ids' crete dictionary which contains for each
- mesh marker (keys) a list of ids belonging to this mesh marker
+
+def get_mesh_markers_ids(su2_mesh_path: str) -> Dict:
+ """
+ Create dictionary which contains for each mesh marker (keys)
+ a list of ids belonging to this mesh marker.
Args:
- su2_mesh_path (str): Path to the SU2 mesh file
+ su2_mesh_path (str): Path to the SU2 mesh file.
- Return:
- marker_dict (dict): Dictionary of marker and ids
+ Returns:
+ marker_dict (dict): Dictionary of marker and ids.
"""
@@ -265,9 +239,7 @@ def get_mesh_markers_ids(su2_mesh_path):
log.info("Mesh marker " + new_marker + " start at line: " + str(start_line_nb))
if line_nb > start_line_nb + 1:
- # print(line)
line_ids = line.split("\n")[0].split()
- # print(line_ids)
marker_dict[new_marker].append(int(line_ids[1]))
marker_dict[new_marker].append(int(line_ids[2]))
marker_dict[new_marker].append(int(line_ids[3]))
@@ -284,8 +256,9 @@ def get_mesh_markers_ids(su2_mesh_path):
return marker_dict
-def extract_loads(results_files_dir):
- """Function to extract loads from a SU2 resuts file.
+def extract_loads(results_files_dir: Path) -> None:
+ """
+ Extract loads from a SU2 resuts file.
Args:
results_files_dir (Path): Path to the directory where results from SU2 are saved.
@@ -298,17 +271,15 @@ def extract_loads(results_files_dir):
surface_flow_force_file_path = Path(results_files_dir, SURFACE_FLOW_FORCE_FILE_NAME)
force_file_path = Path(results_files_dir, FORCE_FILE_NAME)
+ # Update mesh
config_dict = ConfigFile(config_file_path).data
updated_mesh = compute_forces(surface_flow_file_path, force_file_path, config_dict)
write_updated_mesh(updated_mesh, surface_flow_force_file_path)
-
# =================================================================================================
# MAIN
# =================================================================================================
-if __name__ == "__main__":
+if __name__ == "__main__":
log.info("Nothing to execute!")
-
- # TODO: adapt to be use as stand alone
diff --git a/ceasiompy/SU2Run/func/plot.py b/ceasiompy/SU2Run/func/plot.py
new file mode 100644
index 000000000..84da62a5d
--- /dev/null
+++ b/ceasiompy/SU2Run/func/plot.py
@@ -0,0 +1,131 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed for CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Save plot in result folder of SU2 module.
+
+
+| Author: Leon Deligny
+| Creation: 2025-Feb-24
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+import os
+import matplotlib
+
+import numpy as np
+import matplotlib.pyplot as plt
+
+from pathlib import Path
+
+from ceasiompy import log
+
+# =================================================================================================
+# BACKEND SETTING
+# =================================================================================================
+
+if os.environ.get('DISPLAY', '') == '':
+ matplotlib.use('Agg')
+else:
+ matplotlib.use('TkAgg')
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def save_plots(
+ radial_stations: np.ndarray,
+ radial_thrust_coefs: np.ndarray,
+ radial_power_coefs: np.ndarray,
+ non_dimensional_radius: float,
+ optimal_axial_interference_factor: np.ndarray,
+ optimal_rotational_interference_factor: np.ndarray,
+ prandtl_correction_values: np.ndarray,
+ case_dir_path: Path,
+ propeller_uid: str,
+) -> None:
+ """
+ Save plot in result folder.
+ """
+
+ current_dir = Path(case_dir_path, propeller_uid)
+ current_dir.mkdir()
+ interference_plot_path = Path(current_dir, "interference.png")
+ ct_cp_distr_plot_path = Path(current_dir, "radial_thrust_and_power_coefficient.png")
+ prandtl_correction_plot_path = Path(current_dir, "prandtl_correction.png")
+
+ f1 = plt.figure(1)
+ plt.plot(
+ radial_stations,
+ radial_thrust_coefs,
+ "r",
+ markersize=4,
+ label=r"$\frac{dCT}{d\overline{r}}$",
+ )
+ plt.plot(
+ radial_stations,
+ radial_power_coefs,
+ "k",
+ markersize=4,
+ label=r"$\frac{dCP}{d\overline{r}}$",
+ )
+ plt.grid(True)
+ plt.legend()
+ plt.xlabel(r"$\overline{r}$")
+ plt.ylabel(r"$dC_t$, $dC_p$")
+ plt.title("Load Distribution")
+
+ f1.savefig(ct_cp_distr_plot_path)
+ plt.clf()
+
+ f2 = plt.figure(2)
+ plt.plot(
+ non_dimensional_radius,
+ optimal_axial_interference_factor,
+ "r",
+ markersize=4,
+ label=r"$a$",
+ )
+ plt.plot(
+ non_dimensional_radius,
+ optimal_rotational_interference_factor,
+ "k",
+ markersize=4,
+ label=r"$a^1$",
+ )
+ plt.grid(True)
+ plt.legend(numpoints=3)
+ plt.xlabel(r"$\frac{2\pi*r}{J}$")
+ plt.ylabel(r"$a$, $a^1$")
+ plt.title("Interference Factors")
+
+ f2.savefig(interference_plot_path)
+ plt.clf()
+
+ f3 = plt.figure(3)
+ plt.plot(radial_stations, prandtl_correction_values, "k", markersize=4)
+ plt.grid(True)
+ plt.xlabel(r"$\overline{r}$")
+ plt.ylabel(r"$F(\overline{r})$")
+ plt.title("Tip Loss Prandtl Correction Function")
+ f3.savefig(prandtl_correction_plot_path)
+ plt.clf()
+
+ plot_msg = "A plot have been saved at "
+ log.info(f"{plot_msg} {ct_cp_distr_plot_path}.")
+ log.info(f"{plot_msg} {interference_plot_path}.")
+ log.info(f"{plot_msg} {prandtl_correction_plot_path}.")
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/SU2Run/func/results.py b/ceasiompy/SU2Run/func/results.py
new file mode 100644
index 000000000..e3abc76f8
--- /dev/null
+++ b/ceasiompy/SU2Run/func/results.py
@@ -0,0 +1,460 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Extract results from SU2 calculations and save them in a CPACS file.
+
+
+| Author: Aidan Jungo
+| Creation: 2019-10-02
+
+TODO:
+ * Saving for Control surface deflections
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+import os
+import itertools
+import pyvista as pv
+import numpy as np
+
+from ceasiompy.SU2Run.func.extractloads import extract_loads
+from ceasiompy.SU2Run.func.dotderivatives import (
+ load_parameters,
+ compute_derivatives,
+)
+from cpacspy.cpacsfunctions import (
+ get_value,
+ create_branch,
+ get_value_or_default,
+)
+from ceasiompy.utils.ceasiompyutils import (
+ bool_,
+ get_conditions_from_aeromap,
+ ensure_and_append_text_element,
+)
+from ceasiompy.SU2Run.func.utils import (
+ check_one_entry,
+ get_wetted_area,
+ get_su2_aerocoefs,
+ # process_config_dir,
+ get_aeromap_uid,
+ check_force_file_exists,
+ get_efficiency_and_aoa,
+ get_su2_forces_moments,
+)
+
+from pathlib import Path
+from numpy import ndarray
+from ambiance import Atmosphere
+from typing import (
+ List,
+ Dict,
+ Tuple,
+)
+from tixi3.tixi3wrapper import Tixi3
+from cpacspy.cpacspy import (
+ CPACS,
+ AeroMap,
+)
+
+from ceasiompy import log
+from cpacspy.utils import COEFS
+from ceasiompy.utils.commonnames import (
+ SU2_FORCES_BREAKDOWN_NAME,
+ SURFACE_FLOW_FILE_NAME
+)
+from ceasiompy.utils.commonxpath import (
+ AREA_XPATH,
+ LENGTH_XPATH,
+ GMSH_SYMMETRY_XPATH,
+ RANGE_LD_RATIO_XPATH,
+ WING_SPAN_XPATH,
+ SU2_EXTRACT_LOAD_XPATH,
+ SU2_FIXED_CL_XPATH,
+ SU2_ROTATION_RATE_XPATH,
+ SU2_UPDATE_WETTED_AREA_XPATH,
+ WETTED_AREA_XPATH,
+ SU2_DAMPING_DER_XPATH,
+ SU2_DYNAMICDERIVATIVES_TIMESIZE_XPATH,
+ SU2_DYNAMICDERIVATIVES_DATA_XPATH,
+)
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def save_screenshot(surface_flow_file: Path, scalar: str = "Mach") -> Path:
+ """
+ Save a screenshot as a .png file from a surface_flow .vtu file generated by SU2.
+
+ Args:
+ surface_flow_file (Path): Path to the Surface flow file.
+ scalar (str = "Mach"): Scalar value represented on the screenshot.
+
+ Returns:
+ (Path): Path to the screenshot.
+
+ """
+
+ # Initialize pyvista reader and plotter
+ plotter = pv.Plotter(off_screen=True)
+
+ # Read data and send to plotter
+ mesh = pv.read(str(surface_flow_file))
+ plotter.add_mesh(mesh, scalars=scalar, show_scalar_bar=True)
+
+ # Set camera
+ plotter.camera.azimuth = 110.0
+ plotter.camera.elevation = -25.0
+ plotter.camera.zoom(1.6)
+
+ # Save Screenshot
+ screenshot_path = Path(surface_flow_file.parent, f"3d_view_{scalar}.png")
+ plotter.show(screenshot=screenshot_path)
+
+ log.info(f"A screenshot has been saved at {screenshot_path}.")
+ return screenshot_path
+
+
+def get_value_at(i: int, *lists: List[float]) -> Tuple[float, ...]:
+ return tuple(lst[i] for lst in lists)
+
+
+def update_wetted_area_func(tixi: Tixi3, config_dir: Path) -> None:
+ wetted_area = get_wetted_area(Path(config_dir, "no_deformation", "logfile_SU2_CFD.log"))
+
+ # Check if symmetry plane is defined (Default: False)
+ sym_factor = 1.0
+ if get_value_or_default(tixi, GMSH_SYMMETRY_XPATH, False):
+ log.info("Symmetry plane is defined. Multiplying wetted area by 2.")
+ sym_factor = 2.0
+
+ create_branch(tixi, WETTED_AREA_XPATH)
+ tixi.updateDoubleElement(WETTED_AREA_XPATH, wetted_area * sym_factor, "%g")
+
+
+def update_damping_derivatives(
+ tixi: Tixi3,
+ config_dir: Path,
+ aeromap: AeroMap,
+ alt: float,
+ mach: float,
+ aoa: float,
+ aos: float,
+ coefs: Dict,
+ velocity: float,
+) -> None:
+ rotation_rate = get_value(tixi, SU2_ROTATION_RATE_XPATH)
+ ref_len = tixi.getTextElement(WING_SPAN_XPATH)
+ adim_rot_rate = rotation_rate * ref_len / velocity
+
+ for axis in ["dp", "dq", "dr"]:
+ if f"_{axis}" not in config_dir.name:
+ continue
+
+ for coef in COEFS:
+ coef_baseline = aeromap.get(coef, alt=alt, mach=mach, aoa=aoa, aos=aos)
+ dcoef = (coefs[coef] - coef_baseline) / adim_rot_rate
+ aeromap.add_damping_derivatives(
+ alt=alt,
+ mach=mach,
+ aoa=aoa,
+ aos=aos,
+ coef=coef,
+ axis=axis,
+ value=dcoef,
+ rate=rotation_rate,
+ )
+
+
+def update_fixed_cl(tixi: Tixi3, aeromap: AeroMap, force_file_path: Path) -> None:
+ cl_cd, aoa = get_efficiency_and_aoa(force_file_path)
+
+ # Replace aoa with the with the value from fixed cl calculation
+ aeromap.df.loc[0, ["angleOfAttack"]] = aoa
+
+ # Save cl/cd found during the fixed CL calculation (useful for range analysis)
+ create_branch(tixi, RANGE_LD_RATIO_XPATH)
+ tixi.updateDoubleElement(RANGE_LD_RATIO_XPATH, cl_cd, "%g")
+
+
+def save_screenshots(config_dir: Path):
+ surface_flow_path = Path(config_dir, SURFACE_FLOW_FILE_NAME)
+ if surface_flow_path.exists() and "DISPLAY" in os.environ:
+ save_screenshot(surface_flow_path, "Mach")
+ save_screenshot(surface_flow_path, "Pressure_Coefficient")
+
+
+def get_static_results(
+ tixi: Tixi3,
+ aeromap: AeroMap,
+ config_dir: Path,
+ fixed_cl: str,
+ found_wetted_area: bool,
+) -> None:
+ save_screenshots(config_dir)
+ force_file_path = check_force_file_exists(config_dir)
+ case_nb = int(config_dir.name.split("_")[0].split("Case")[1])
+
+ alt_list, mach_list, aoa_list, aos_list = get_conditions_from_aeromap(aeromap)
+ aoa, aos, mach, alt = get_value_at(case_nb, aoa_list, aos_list, mach_list, alt_list)
+
+ if fixed_cl == "YES":
+ update_fixed_cl(tixi, aeromap, force_file_path)
+
+ # Load aerocoefs
+ cl, cd, cs, cmd, cms, cml, velocity = get_su2_aerocoefs(force_file_path)
+
+ # Damping derivatives
+ if bool_(get_value(tixi, SU2_DAMPING_DER_XPATH)):
+ coefs = {"cl": cl, "cd": cd, "cs": cs, "cmd": cmd, "cms": cms, "cml": cml}
+ update_damping_derivatives(
+ tixi,
+ config_dir,
+ aeromap,
+ alt, mach, aoa, aos,
+ coefs, velocity,
+ )
+ # Baseline coefficients (no damping derivatives)
+ else:
+ aeromap.add_coefficients(
+ alt=alt, mach=mach, aos=aos, aoa=aoa,
+ cd=cd, cl=cl, cs=cs,
+ cml=cml, cmd=cmd, cms=cms,
+ )
+
+ if "_TED_" in config_dir.name:
+ # TODO: convert when it is possible to save TED in cpacspy
+ raise NotImplementedError("TED not implemented yet")
+
+ update_wetted_area = bool_(get_value(tixi, SU2_UPDATE_WETTED_AREA_XPATH))
+ if not found_wetted_area and update_wetted_area:
+ update_wetted_area_func(tixi, config_dir)
+ found_wetted_area = True
+
+ if bool_(get_value(tixi, SU2_EXTRACT_LOAD_XPATH)):
+ extract_loads(config_dir)
+
+
+def get_dynamic_force_files(file_path: Path) -> Path:
+ # Get results from dynstab
+ force_file_paths = list(
+ Path(file_path).glob("forces_breakdown_*.dat")
+ )
+
+ if not force_file_paths:
+ raise OSError("No result force file have been found!")
+ return force_file_paths
+
+
+def compute_dynamic_coefs(
+ tixi: Tixi3,
+ forces_coef_list: List,
+ moments_coef_list: List,
+ f_static: ndarray,
+ m_static: ndarray,
+ alt: float,
+ mach: float,
+ s: float,
+ b: float,
+ c: float,
+) -> Tuple[float, float, float, float]:
+ # Convert the list to a numpy array
+ f_time = np.array(forces_coef_list)
+ m_time = np.array(moments_coef_list)
+
+ # Compute derivatives
+ a, omega, _, t = load_parameters(tixi)
+ fx, fy = compute_derivatives(a, omega, t, f_time, f_static)
+ mx, my = compute_derivatives(a, omega, t, m_time, m_static)
+
+ # Velocity in m/s in atmospheric environment
+ Atm = Atmosphere(alt)
+ velocity = Atm.speed_of_sound[0] * mach
+
+ # Dynamic pressure
+ q_dyn = Atm.density[0] * (velocity ** 2) / 2.0
+
+ qs = q_dyn * s
+ qsb = qs * b
+ qsc = qs * c
+
+ # Scale forces accordingly
+ cfx = fx / qs
+ cfy = fy / qs
+
+ # Scale moments accordingly
+ cmx = np.copy(mx)
+ cmy = np.copy(my)
+ cmx[[0, 2], ] /= qsb
+ cmx[1, ] /= qsc
+ cmy[[0, 2], ] /= qsb
+ cmy[1, ] /= qsc
+ log.info(f"q {q_dyn} fx {fx} mx {mx}, cfx {cfx} cmx {cmx}")
+
+ return cfx, cfy, cmx, cmy
+
+
+def add_dynamic_coefs(
+ tixi: Tixi3,
+ mach: str,
+ alt: str,
+ angle: str,
+ cfx: str,
+ cfy: str,
+ cmx: str,
+ cmy: str,
+) -> None:
+ # Put derivatives in CPACS at SU2 in DynamicDerivatives
+ xpath = SU2_DYNAMICDERIVATIVES_DATA_XPATH
+
+ # Ensure the path exists
+ create_branch(tixi, xpath)
+
+ ensure_and_append_text_element(tixi, xpath, "mach", str(mach))
+ ensure_and_append_text_element(tixi, xpath, "alt", str(alt))
+
+ ensure_and_append_text_element(
+ tixi, xpath, f"cf_{angle}", cfx,
+ )
+ ensure_and_append_text_element(
+ tixi, xpath, f"cf_{angle}_prim", cfy,
+ )
+ ensure_and_append_text_element(
+ tixi, xpath, f"cm_{angle}", cmx,
+ )
+ ensure_and_append_text_element(
+ tixi, xpath, f"cm_{angle}_prim", cmy,
+ )
+
+
+def get_dynstab_results(tixi: Tixi3, dict_dir: Dict) -> None:
+ # Extract unique mach and alt values
+ mach_values = list(set(d['mach'] for d in dict_dir))
+ alt_values = list(set(d['alt'] for d in dict_dir))
+ n = int(get_value(tixi, SU2_DYNAMICDERIVATIVES_TIMESIZE_XPATH))
+ b: float = tixi.getDoubleElement(AREA_XPATH)
+ c: float = tixi.getDoubleElement(LENGTH_XPATH)
+ s: float = b / c
+
+ for mach, alt in itertools.product(mach_values, alt_values):
+ none_file = check_one_entry(dict_dir, mach, alt, "none")
+ alpha_file = check_one_entry(dict_dir, mach, alt, "alpha")
+ # beta_file = check_one_entry(dict_dir, "beta")
+
+ angle_file = {
+ "alpha": alpha_file / "no_deformation"
+ } # "beta": beta_file
+
+ # Retrieve forces and moments for (alpha, alpha_dot) = (0, 0)
+ none_force_file_path = Path(
+ none_file, "no_deformation", SU2_FORCES_BREAKDOWN_NAME
+ )
+
+ (
+ cfx_0, cfy_0, cfz_0,
+ cmx_0, cmy_0, cmz_0,
+
+ ) = get_su2_forces_moments(none_force_file_path)
+
+ log.info(
+ f"cfx_0: {cfx_0}, cfy_0: {cfy_0}, cfz_0: {cfz_0}, "
+ f"cmx_0: {cmx_0}, cmy_0: {cmy_0}, cmz_0: {cmz_0}"
+ )
+
+ # Force
+ f_static = np.tile([
+ cfx_0, cfy_0, cfz_0,
+ ], (n, 1))
+
+ # Moments
+ m_static = np.tile([
+ cmx_0, cmy_0, cmz_0
+ ], (n, 1))
+
+ # Retrive forces and moments for (alpha, alpha_dot) = (alpha(t), alpha_dot(t))
+ for angle in ['alpha']: # , 'beta'
+ force_file_paths = get_dynamic_force_files(angle_file[angle])
+ forces_coef_list, moments_coef_list = [], []
+
+ for force_file_path in force_file_paths:
+ # Access coefficients
+ cfx, cfy, cfz, cmx, cmy, cmz = get_su2_forces_moments(
+ force_file_path
+ )
+ forces_coef_list.append([cfx, cfy, cfz])
+ moments_coef_list.append([cmx, cmy, cmz])
+
+ cfx, cfy, cmx, cmy = compute_dynamic_coefs(
+ tixi,
+ forces_coef_list,
+ moments_coef_list,
+ f_static,
+ m_static,
+ alt,
+ mach,
+ s,
+ b,
+ c,
+ )
+
+ # Add them in the CPACS
+ add_dynamic_coefs(
+ tixi, mach, alt, angle,
+ str(cfx), str(cfy), str(cmx), str(cmy),
+ )
+
+
+def get_su2_results(cpacs: CPACS, wkdir: Path) -> None:
+ """
+ Updates CPACS file with SU2 results.
+
+ Updates Aeromap at xPath:
+ '/cpacs/vehicles/aircraft/model/analyses/aeroPerformance/aeroMap[n]/aeroPerformanceMap'
+
+ """
+ tixi = cpacs.tixi
+ found_wetted_area = False
+
+ fixed_cl = get_value(tixi, SU2_FIXED_CL_XPATH)
+ aeromap_uid = get_aeromap_uid(tixi, fixed_cl)
+ aeromap: AeroMap = cpacs.get_aeromap_by_uid(aeromap_uid)
+
+ case_dir_list = [
+ case_dir
+ for case_dir in wkdir.iterdir()
+ if ("Case" in case_dir.name) and (case_dir.is_dir())
+ ]
+
+ # dict_dir: List[Dict] = []
+
+ for config_dir in sorted(case_dir_list):
+ # process_config_dir(config_dir, dict_dir)
+
+ # Retrieve non dynamic stability data
+ if "dynstab" not in str(config_dir):
+ get_static_results(
+ tixi,
+ aeromap,
+ config_dir,
+ fixed_cl,
+ found_wetted_area
+ )
+
+ aeromap.save()
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/SU2Run/func/runconfigfiles.py b/ceasiompy/SU2Run/func/runconfigfiles.py
new file mode 100644
index 000000000..5ab0fb369
--- /dev/null
+++ b/ceasiompy/SU2Run/func/runconfigfiles.py
@@ -0,0 +1,115 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Module to run SU2 configuration files in CEASIOMpy.
+
+
+| Author: Leon Deligny
+| Creation: 20 March 2025
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+
+from ceasiompy.utils.ceasiompyutils import run_software
+
+from typing import List
+from pathlib import Path
+
+from ceasiompy import log
+from ceasiompy.SU2Run import SOFTWARE_NAME
+from ceasiompy.utils.commonnames import (
+ CONFIG_CFD_NAME,
+ CONFIG_DYNSTAB_NAME,
+ SU2_FORCES_BREAKDOWN_NAME,
+ SU2_DYNSTAB_FORCES_BREAKDOWN_NAME,
+)
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def check_config_file_exists(config_file: List[Path], config_dir: Path) -> None:
+ """
+ Check that there exists exactly 1 configuration file for 1 scenario
+ """
+ if (not config_file):
+ raise ValueError(
+ f"No configuration files '{CONFIG_CFD_NAME}' "
+ f"and '{CONFIG_DYNSTAB_NAME}' have been found in directory {config_dir}."
+ )
+
+ if (len(config_file) > 1):
+ raise ValueError(
+ f"More than one configuration file '{CONFIG_CFD_NAME}' "
+ f"or '{CONFIG_DYNSTAB_NAME}' have been found in directory {config_dir}."
+ )
+
+
+def check_force_files_exists(config_dir: Path) -> None:
+ forces_breakdown_file = Path(config_dir, SU2_FORCES_BREAKDOWN_NAME)
+ dynstab_forces_breakdown_file = Path(config_dir, SU2_DYNSTAB_FORCES_BREAKDOWN_NAME)
+
+ if (not forces_breakdown_file.exists()) and (
+ not dynstab_forces_breakdown_file.exists()):
+ raise ValueError(
+ "The SU2_CFD calculations have not ended correctly, "
+ f"{SU2_FORCES_BREAKDOWN_NAME} and {SU2_DYNSTAB_FORCES_BREAKDOWN_NAME} "
+ f"are missing in {config_dir}."
+ )
+
+
+def run_SU2_multi(wkdir: Path, nb_proc: int = 1) -> None:
+ """
+ Run in the given working directory SU2 calculations.
+ The working directory must have a folder structure created by 'SU2Config' module.
+
+ Args:
+ wkdir (Path): Path to the working directory.
+ nb_proc (int): Number of processor that should be used to run the calculation in parallel.
+
+ """
+
+ case_dir_list = [dir for dir in wkdir.iterdir() if "Case" in dir.name]
+
+ if not case_dir_list:
+ raise FileNotFoundError(
+ f"No Case directory has been found in the working directory: {wkdir}."
+ )
+
+ # Iterate through different cases.
+ for case_dir in case_dir_list:
+
+ # Iterate through [no_deformation, aileron, elevator, rudder].
+ for config_dir in case_dir.iterdir():
+ config_file = [
+ c for c in config_dir.iterdir()
+ if (c.name == CONFIG_CFD_NAME or c.name == CONFIG_DYNSTAB_NAME)
+ ]
+
+ check_config_file_exists(config_file, config_dir)
+
+ run_software(
+ software_name=SOFTWARE_NAME,
+ arguments=[config_file[0]],
+ wkdir=config_dir,
+ with_mpi=True,
+ nb_cpu=nb_proc,
+ log_bool=True,
+ )
+
+ check_force_files_exists(config_dir)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/SU2Run/func/su2config.py b/ceasiompy/SU2Run/func/su2config.py
deleted file mode 100644
index 358adbc35..000000000
--- a/ceasiompy/SU2Run/func/su2config.py
+++ /dev/null
@@ -1,567 +0,0 @@
-"""
-CEASIOMpy: Conceptual Aircraft Design Software
-
-Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-
-Function generate or modify SU2 configuration files
-
-Python version: >=3.8
-
-| Author: Aidan Jungo
-| Creation: 2020-02-24
-
-TODO:
-
- * add and test control surface functions
-
-"""
-
-# =================================================================================================
-# IMPORTS
-# =================================================================================================
-
-from pathlib import Path
-from shutil import copyfile
-
-from ambiance import Atmosphere
-from ceasiompy.SU2Run.func.su2actuatordiskfile import (
- get_advanced_ratio,
- get_radial_stations,
- save_plots,
- thrust_calculator,
- write_actuator_disk_data,
- write_header,
-)
-from ceasiompy.SU2Run.func.su2utils import get_mesh_markers, get_su2_config_template
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.commonnames import (
- ACTUATOR_DISK_FILE_NAME,
- ACTUATOR_DISK_INLET_SUFFIX,
- ACTUATOR_DISK_OUTLET_SUFFIX,
- CONFIG_CFD_NAME,
- SU2_FORCES_BREAKDOWN_NAME,
-)
-from ceasiompy.utils.commonxpath import (
- GMSH_SYMMETRY_XPATH,
- PROP_XPATH,
- RANGE_XPATH,
- SU2_ACTUATOR_DISK_XPATH,
- SU2_AEROMAP_UID_XPATH,
- SU2_BC_FARFIELD_XPATH,
- SU2_BC_WALL_XPATH,
- SU2_CFL_NB_XPATH,
- SU2_CFL_ADAPT_XPATH,
- SU2_CFL_ADAPT_PARAM_DOWN_XPATH,
- SU2_CFL_ADAPT_PARAM_UP_XPATH,
- SU2_CFL_MAX_XPATH,
- SU2_CFL_MIN_XPATH,
- SU2_CONTROL_SURF_XPATH,
- SU2_DAMPING_DER_XPATH,
- SU2_DEF_MESH_XPATH,
- SU2_FIXED_CL_XPATH,
- SU2_MAX_ITER_XPATH,
- SU2_MG_LEVEL_XPATH,
- SU2_ROTATION_RATE_XPATH,
- SU2_TARGET_CL_XPATH,
- SU2MESH_XPATH,
- ENGINE_TYPE_XPATH,
- ENGINE_BC,
-)
-from ceasiompy.utils.configfiles import ConfigFile
-from cpacspy.cpacsfunctions import (
- create_branch,
- get_string_vector,
- get_value,
- get_value_or_default,
-)
-from cpacspy.cpacspy import CPACS
-
-log = get_logger()
-
-MODULE_DIR = Path(__file__).parent
-
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
-
-
-def add_damping_derivatives(cfg, wkdir, case_dir_name, rotation_rate):
- """Add damping derivatives parameter to the config file and save them to their respective
- directory.
-
- Args:
- cfg (ConfigFile): ConfigFile object.
- wkdir (Path): Path to the working directory
- case_dir_name (str): Name of the case directory
- rotation_rate (float): Rotation rate that will be impose to calculate damping derivatives
- """
-
- cfg["GRID_MOVEMENT"] = "ROTATING_FRAME"
-
- cfg["ROTATION_RATE"] = f"{rotation_rate} 0.0 0.0"
- case_dir = Path(wkdir, f"{case_dir_name}_dp")
- case_dir.mkdir()
- cfg.write_file(Path(case_dir, CONFIG_CFD_NAME), overwrite=True)
-
- cfg["ROTATION_RATE"] = f"0.0 {rotation_rate} 0.0"
- case_dir = Path(wkdir, f"{case_dir_name}_dq")
- case_dir.mkdir()
- cfg.write_file(Path(case_dir, CONFIG_CFD_NAME), overwrite=True)
-
- cfg["ROTATION_RATE"] = f"0.0 0.0 {rotation_rate}"
- case_dir = Path(wkdir, f"{case_dir_name}_dr")
- case_dir.mkdir()
- cfg.write_file(Path(case_dir, CONFIG_CFD_NAME), overwrite=True)
-
- log.info("Damping derivatives cases directories has been created.")
-
-
-def add_actuator_disk(cfg, cpacs, case_dir_path, actuator_disk_file, mesh_markers, alt, mach):
- """Add actuator disk parameter to the config file.
-
- Args:
- cfg (ConfigFile): ConfigFile object.
- cpacs (CPACS): CPACS object from cpacspy library
- case_dir_path (Path): Path object to the current case directory
- actuator_disk_file (Path): Path to the actuator disk file
- mesh_markers (dict): Dictionary containing all the mesh markers
-
- Returns:
- cfg (ConfigFile): ConfigFile object.
- """
-
- ad_inlet_marker = sorted(mesh_markers["actuator_disk_inlet"])
- ad_outlet_marker = sorted(mesh_markers["actuator_disk_outlet"])
-
- if "None" in ad_inlet_marker or "None" in ad_outlet_marker:
- return
-
- if len(ad_inlet_marker) != len(ad_outlet_marker):
- raise ValueError(
- "The number of inlet and outlet markers of the actuator disk must be the same."
- )
-
- # Get rotorcraft configuration (propeller)
- try:
- rotorcraft_config = cpacs.rotorcraft.configuration
- except AttributeError:
- raise ValueError(
- "The actuator disk is defined but no rotorcraft configuration is defined in "
- "the CPACS file."
- )
-
- rotor_uid_pos = {}
- for i in range(1, rotorcraft_config.get_rotor_count() + 1):
- rotor = rotorcraft_config.get_rotor(i)
-
- rotor_uid = rotor.get_uid()
- pos_x = rotor.get_translation().x
- pos_y = rotor.get_translation().y
- pos_z = rotor.get_translation().z
- radius = rotor.get_radius()
- hub_radius = 0.0 # TODO: get correctly from CPACS
-
- rotor_xpath = cpacs.tixi.uIDGetXPath(rotor_uid)
-
- number_of_blades_xpath = (
- rotor_xpath + "/rotorHub/rotorBladeAttachments/rotorBladeAttachment/numberOfBlades"
- )
- number_of_blades = get_value_or_default(cpacs.tixi, number_of_blades_xpath, 3)
-
- # TODO: this is the nominal speed, how to get a speed which correspond to each flight cond.
- rotational_velocity_xpath = rotor_xpath + "/nominalRotationsPerMinute"
- rotational_velocity = (
- get_value_or_default(cpacs.tixi, rotational_velocity_xpath, 3000) / 60.0
- )
-
- rotor_uid_pos[rotor_uid] = (
- pos_x,
- pos_y,
- pos_z,
- radius,
- hub_radius,
- number_of_blades,
- rotational_velocity,
- )
-
- cfg["ACTDISK_DOUBLE_SURFACE"] = "YES"
- cfg["ACTDISK_TYPE"] = "VARIABLE_LOAD"
- cfg["ACTDISK_FILENAME"] = ACTUATOR_DISK_FILE_NAME
- cfg["MGLEVEL"] = 0 # Calculation diverges if multigrid is used with a disk actuator
-
- actdisk_markers = []
-
- f = open(actuator_disk_file, "w")
- f = write_header(f)
-
- for maker_inlet, marker_outlet in zip(ad_inlet_marker, ad_outlet_marker):
- inlet_uid = maker_inlet.split(ACTUATOR_DISK_INLET_SUFFIX)[0]
- outlet_uid = marker_outlet.split(ACTUATOR_DISK_OUTLET_SUFFIX)[0]
-
- if inlet_uid != outlet_uid:
- raise ValueError("The inlet and outlet markers of the actuator disk must be the same.")
-
- if "_mirrored" in maker_inlet:
- uid = inlet_uid.split("_mirrored")[0]
- sym = -1
- else:
- uid = inlet_uid
- sym = 1
-
- center = []
- center.append(round(rotor_uid_pos[uid][0], 5))
- center.append(round(sym * rotor_uid_pos[uid][1], 5))
- center.append(round(rotor_uid_pos[uid][2], 5))
-
- axis = (1.0, 0.0, 0.0) # TODO: get the axis by applying the rotation matrix
- radius = round(rotor_uid_pos[uid][3], 5)
- hub_radius = round(rotor_uid_pos[uid][4], 5)
- number_of_blades = round(rotor_uid_pos[uid][5], 5)
- rotational_velocity = round(rotor_uid_pos[uid][6], 5)
-
- actdisk_markers.append(maker_inlet)
- actdisk_markers.append(marker_outlet)
- actdisk_markers.append(str(center[0]))
- actdisk_markers.append(str(center[1]))
- actdisk_markers.append(str(center[2]))
- actdisk_markers.append(str(center[0]))
- actdisk_markers.append(str(center[1]))
- actdisk_markers.append(str(center[2]))
-
- Atm = Atmosphere(alt)
- free_stream_velocity = mach * Atm.speed_of_sound[0]
-
- radial_stations = get_radial_stations(radius, hub_radius)
- advanced_ratio = get_advanced_ratio(free_stream_velocity, rotational_velocity, radius)
-
- prandtl_correction_xpath = PROP_XPATH + "/propeller/blade/loss"
- prandtl_correction = get_value_or_default(cpacs.tixi, prandtl_correction_xpath, True)
-
- thrust_xpath = PROP_XPATH + "/propeller/thrust"
- thrust = get_value_or_default(cpacs.tixi, thrust_xpath, 3000)
- total_thrust_coefficient = float(
- thrust / (Atm.density * rotational_velocity**2 * (radius * 2) ** 4)
- )
-
- (
- radial_thrust_coefs,
- radial_power_coefs,
- non_dimensional_radius,
- optimal_axial_interference_factor,
- optimal_rotational_interference_factor,
- prandtl_correction_values,
- ) = thrust_calculator(
- radial_stations,
- total_thrust_coefficient,
- radius,
- free_stream_velocity,
- prandtl_correction,
- number_of_blades,
- rotational_velocity,
- )
-
- save_plots(
- radial_stations,
- radial_thrust_coefs,
- radial_power_coefs,
- non_dimensional_radius,
- optimal_axial_interference_factor,
- optimal_rotational_interference_factor,
- prandtl_correction_values,
- case_dir_path,
- inlet_uid,
- )
-
- f = write_actuator_disk_data(
- file=f,
- inlet_marker=maker_inlet,
- outlet_marker=marker_outlet,
- center=center,
- axis=axis,
- radius=radius,
- advanced_ratio=advanced_ratio,
- radial_stations=radial_stations,
- radial_thrust_coefs=radial_thrust_coefs,
- radial_power_coefs=radial_power_coefs,
- )
-
- cfg["MARKER_ACTDISK"] = " (" + ", ".join(actdisk_markers) + " )"
-
- f.close()
-
-
-# adding the results of ThermoData to the config file of su2
-
-
-def add_thermodata(cfg, cpacs, alt, case_nb, alt_list):
-
- if cpacs.tixi.checkElement(ENGINE_TYPE_XPATH):
- log.info("adding engine BC to the SU2 config file")
- engine_type = get_value(cpacs.tixi, ENGINE_TYPE_XPATH)
- log.info(f"engine type {engine_type}")
- alt = alt_list[case_nb]
- Atm = Atmosphere(alt)
- tot_temp_in = Atm.temperature[0]
- tot_pressure_in = Atm.pressure[0]
- if len(alt_list) > 1:
- tot_temp_out = get_value(cpacs.tixi, ENGINE_BC + "/temperatureOutlet").split(";")
- tot_pressure_out = get_value(cpacs.tixi, ENGINE_BC + "/pressureOutlet").split(";")
- tot_temp_out = tot_temp_out[case_nb]
- tot_pressure_out = tot_pressure_out[case_nb]
- else:
- tot_temp_out = get_value(cpacs.tixi, ENGINE_BC + "/temperatureOutlet")
- tot_pressure_out = get_value(cpacs.tixi, ENGINE_BC + "/pressureOutlet")
- cfg["INLET_TYPE"] = "TOTAL_CONDITIONS"
- cfg["MARKER_INLET"] = (
- f"(INLET_ENGINE, {tot_temp_in}, {tot_pressure_in}, {1},{0},{0}, "
- f"OUTLET_ENGINE,{tot_temp_out},{tot_pressure_out}, {1},{0},{0})"
- )
-
-
-def generate_su2_cfd_config(cpacs_path, cpacs_out_path, wkdir):
- """Function to create SU2 config file.
-
- Function 'generate_su2_cfd_config' reads data in the CPACS file and generate configuration
- files for one or multiple flight conditions (alt,mach,aoa,aos)
-
- Source:
- * SU2 config template: https://github.com/su2code/SU2/blob/master/config_template.cfg
-
- Args:
- cpacs_path (Path): Path to CPACS file
- cpacs_out_path (Path):Path to CPACS output file
- wkdir (Path): Path to the working directory
-
- """
-
- cpacs = CPACS(cpacs_path)
-
- su2_mesh = Path(get_value(cpacs.tixi, SU2MESH_XPATH))
- if not su2_mesh.is_file():
- raise FileNotFoundError(f"SU2 mesh file {su2_mesh} not found")
-
- mesh_markers = get_mesh_markers(su2_mesh)
- for key, value in mesh_markers.items():
- mesh_markers[key] = [str(item).replace(" ", "") for item in value]
-
- create_branch(cpacs.tixi, SU2_BC_WALL_XPATH)
- bc_wall_str = ";".join(mesh_markers["wall"])
- cpacs.tixi.updateTextElement(SU2_BC_WALL_XPATH, bc_wall_str)
-
- create_branch(cpacs.tixi, SU2_BC_FARFIELD_XPATH)
- bc_farfiled_str = ";".join(mesh_markers["engine_intake"] + mesh_markers["engine_exhaust"])
- cpacs.tixi.updateTextElement(SU2_BC_FARFIELD_XPATH, bc_farfiled_str)
-
- create_branch(cpacs.tixi, SU2_ACTUATOR_DISK_XPATH)
- bc_actuator_disk_str = ";".join(
- mesh_markers["actuator_disk_inlet"] + mesh_markers["actuator_disk_outlet"]
- )
- cpacs.tixi.updateTextElement(SU2_ACTUATOR_DISK_XPATH, bc_actuator_disk_str)
-
- fixed_cl = get_value_or_default(cpacs.tixi, SU2_FIXED_CL_XPATH, "NO")
- target_cl = get_value_or_default(cpacs.tixi, SU2_TARGET_CL_XPATH, 1.0)
-
- if fixed_cl == "NO":
- # Get the first aeroMap as default one or create automatically one
- aeromap_list = cpacs.get_aeromap_uid_list()
-
- if aeromap_list:
- aeromap_default = aeromap_list[0]
- log.info(f"The aeromap is {aeromap_default}")
-
- aeromap_uid = get_value_or_default(cpacs.tixi, SU2_AEROMAP_UID_XPATH, aeromap_default)
-
- activate_aeromap = cpacs.get_aeromap_by_uid(aeromap_uid)
- alt_list = activate_aeromap.get("altitude").tolist()
- mach_list = activate_aeromap.get("machNumber").tolist()
- aoa_list = activate_aeromap.get("angleOfAttack").tolist()
- aos_list = activate_aeromap.get("angleOfSideslip").tolist()
-
- else:
- default_aeromap = cpacs.create_aeromap("DefaultAeromap")
- default_aeromap.description = "AeroMap created automatically"
-
- mach = get_value_or_default(cpacs.tixi, RANGE_XPATH + "/cruiseMach", 0.3)
- alt = get_value_or_default(cpacs.tixi, RANGE_XPATH + "/cruiseAltitude", 10000)
-
- default_aeromap.add_row(alt=alt, mach=mach, aos=0.0, aoa=0.0)
- default_aeromap.save()
-
- alt_list = [alt]
- mach_list = [mach]
- aoa_list = [0.0]
- aos_list = [0.0]
-
- aeromap_uid = get_value_or_default(cpacs.tixi, SU2_AEROMAP_UID_XPATH, "DefaultAeromap")
- log.info(f"{aeromap_uid} has been created")
-
- else: # if fixed_cl == 'YES':
- log.info("Configuration file for fixed CL calculation will be created.")
-
- fixed_cl_aeromap = cpacs.create_aeromap("aeroMap_fixedCL_SU2")
- fixed_cl_aeromap.description = f"AeroMap created for SU2 fixed CL value of {target_cl}"
-
- mach = get_value_or_default(cpacs.tixi, RANGE_XPATH + "/cruiseMach", 0.78)
- alt = get_value_or_default(cpacs.tixi, RANGE_XPATH + "/cruiseAltitude", 12000)
-
- fixed_cl_aeromap.add_row(alt=alt, mach=mach, aos=0.0, aoa=0.0)
- fixed_cl_aeromap.save()
-
- alt_list = [alt]
- mach_list = [mach]
- aoa_list = [0.0]
- aos_list = [0.0]
-
- cfg = ConfigFile(get_su2_config_template())
-
- # Check if symmetry plane is defined (Default: False)
- sym_factor = 1.0
- if get_value_or_default(cpacs.tixi, GMSH_SYMMETRY_XPATH, False):
- log.info("Symmetry plane is defined. The reference area will be divided by 2.")
- sym_factor = 2.0
-
- # General parameters
- cfg["RESTART_SOL"] = "NO"
- cfg["REF_LENGTH"] = cpacs.aircraft.ref_length
- cfg["REF_AREA"] = cpacs.aircraft.ref_area / sym_factor
- cfg["REF_ORIGIN_MOMENT_X"] = cpacs.aircraft.ref_point_x
- cfg["REF_ORIGIN_MOMENT_Y"] = cpacs.aircraft.ref_point_y
- cfg["REF_ORIGIN_MOMENT_Z"] = cpacs.aircraft.ref_point_z
-
- # Settings
-
- cfl_down = get_value_or_default(cpacs.tixi, SU2_CFL_ADAPT_PARAM_DOWN_XPATH, 0.5)
- cfl_up = get_value_or_default(cpacs.tixi, SU2_CFL_ADAPT_PARAM_UP_XPATH, 1.5)
- cfl_min = get_value_or_default(cpacs.tixi, SU2_CFL_MIN_XPATH, 0.5)
- cfl_max = get_value_or_default(cpacs.tixi, SU2_CFL_MAX_XPATH, 100)
-
- if get_value_or_default(cpacs.tixi, SU2_CFL_ADAPT_XPATH, True):
- cfg["CFL_ADAPT"] = "YES"
-
- else:
- cfg["CFL_ADAPT"] = "NO"
-
- cfg["INNER_ITER"] = int(get_value_or_default(cpacs.tixi, SU2_MAX_ITER_XPATH, 200))
- cfg["CFL_NUMBER"] = get_value_or_default(cpacs.tixi, SU2_CFL_NB_XPATH, 1.0)
- cfg["CFL_ADAPT_PARAM"] = f"( {cfl_down}, {cfl_up}, {cfl_min}, {cfl_max} )"
- cfg["MGLEVEL"] = int(get_value_or_default(cpacs.tixi, SU2_MG_LEVEL_XPATH, 3))
-
- # Fixed CL mode (AOA will not be taken into account)
- cfg["FIXED_CL_MODE"] = fixed_cl
- cfg["TARGET_CL"] = target_cl
- cfg["DCL_DALPHA"] = "0.1"
- cfg["UPDATE_AOA_ITER_LIMIT"] = "50"
- cfg["ITER_DCL_DALPHA"] = "80"
-
- # Mesh Marker
- bc_wall_str = f"( {','.join(mesh_markers['wall'])} )"
-
- cfg["MARKER_EULER"] = bc_wall_str
- farfield_bc = (
- mesh_markers["farfield"] + mesh_markers["engine_intake"] + mesh_markers["engine_exhaust"]
- )
- cfg["MARKER_FAR"] = f"( {','.join(farfield_bc)} )"
- cfg["MARKER_SYM"] = f"( {','.join(mesh_markers['symmetry'])} )"
- cfg["MARKER_PLOTTING"] = bc_wall_str
- cfg["MARKER_MONITORING"] = bc_wall_str
- cfg["MARKER_MOVING"] = "( NONE )" # TODO: when do we need to define MARKER_MOVING?
- cfg["DV_MARKER"] = bc_wall_str
-
- # Output
- cfg["WRT_FORCES_BREAKDOWN"] = "YES"
- cfg["BREAKDOWN_FILENAME"] = SU2_FORCES_BREAKDOWN_NAME
- cfg["OUTPUT_FILES"] = "(RESTART, PARAVIEW, SURFACE_PARAVIEW)"
- cfg["HISTORY_OUTPUT"] = "(INNER_ITER, RMS_RES, AERO_COEFF)"
-
- # Parameters which will vary for the different cases (alt,mach,aoa,aos)
-
- for case_nb in range(len(alt_list)):
- cfg["MESH_FILENAME"] = str(su2_mesh)
-
- alt = alt_list[case_nb]
- mach = mach_list[case_nb]
- aoa = aoa_list[case_nb]
- aos = aos_list[case_nb]
-
- Atm = Atmosphere(alt)
-
- cfg["MACH_NUMBER"] = mach
- cfg["AOA"] = aoa
- cfg["SIDESLIP_ANGLE"] = aos
- cfg["FREESTREAM_PRESSURE"] = Atm.pressure[0]
- cfg["FREESTREAM_TEMPERATURE"] = Atm.temperature[0]
- cfg["ROTATION_RATE"] = "0.0 0.0 0.0"
-
- case_dir_name = (
- f"Case{str(case_nb).zfill(2)}_alt{alt}_mach{round(mach, 2)}"
- f"_aoa{round(aoa, 1)}_aos{round(aos, 1)}"
- )
-
- add_thermodata(cfg, cpacs, alt, case_nb, alt_list)
-
- case_dir_path = Path(wkdir, case_dir_name)
- if not case_dir_path.exists():
- case_dir_path.mkdir()
-
- if get_value_or_default(cpacs.tixi, SU2_ACTUATOR_DISK_XPATH, False):
- actuator_disk_file = Path(wkdir, ACTUATOR_DISK_FILE_NAME)
- add_actuator_disk(
- cfg, cpacs, case_dir_path, actuator_disk_file, mesh_markers, alt, mach
- )
-
- if actuator_disk_file.exists():
- case_actuator_disk_file = Path(case_dir_path, ACTUATOR_DISK_FILE_NAME)
- copyfile(actuator_disk_file, case_actuator_disk_file)
-
- bc_wall_str = (
- "("
- + ",".join(
- mesh_markers["wall"]
- + mesh_markers["actuator_disk_inlet"]
- + mesh_markers["actuator_disk_outlet"]
- )
- + ")"
- )
-
- cfg["MARKER_PLOTTING"] = bc_wall_str
- cfg["MARKER_MONITORING"] = bc_wall_str
-
- config_output_path = Path(case_dir_path, CONFIG_CFD_NAME)
- cfg.write_file(config_output_path, overwrite=True)
-
- if get_value_or_default(cpacs.tixi, SU2_DAMPING_DER_XPATH, False):
- rotation_rate = get_value_or_default(cpacs.tixi, SU2_ROTATION_RATE_XPATH, 1.0)
- add_damping_derivatives(cfg, wkdir, case_dir_name, rotation_rate)
-
- # Control surfaces deflections (TODO: create a subfunctions)
- if get_value_or_default(cpacs.tixi, SU2_CONTROL_SURF_XPATH, False):
- # Get deformed mesh list
- if cpacs.tixi.checkElement(SU2_DEF_MESH_XPATH):
- su2_def_mesh_list = get_string_vector(cpacs.tixi, SU2_DEF_MESH_XPATH)
- else:
- log.warning("No SU2 deformed mesh has been found!")
- su2_def_mesh_list = []
-
- for su2_def_mesh in su2_def_mesh_list:
- mesh_path = Path(wkdir, "MESH", su2_def_mesh)
- config_dir_path = Path(wkdir, case_dir_name + "_" + su2_def_mesh.split(".")[0])
- config_dir_path.mkdir()
- cfg["MESH_FILENAME"] = mesh_path
-
- cfg.write_file(Path(config_dir_path, CONFIG_CFD_NAME), overwrite=True)
-
- cpacs.save_cpacs(cpacs_out_path, overwrite=True)
-
-
-# =================================================================================================
-# MAIN
-# =================================================================================================
-
-if __name__ == "__main__":
- log.info("Nothing to execute!")
diff --git a/ceasiompy/SU2Run/func/su2config_rans.py b/ceasiompy/SU2Run/func/su2config_rans.py
deleted file mode 100644
index 77b6bbeb6..000000000
--- a/ceasiompy/SU2Run/func/su2config_rans.py
+++ /dev/null
@@ -1,582 +0,0 @@
-"""
-CEASIOMpy: Conceptual Aircraft Design Software
-
-Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-
-Function generate or modify SU2 configuration files
-
-Python version: >=3.8
-
-| Author: Aidan Jungo
-| Creation: 2020-02-24
-
-TODO:
-
- * add and test control surface functions
-
-"""
-
-# =================================================================================================
-# IMPORTS
-# =================================================================================================
-
-from pathlib import Path
-from shutil import copyfile
-
-from ambiance import Atmosphere
-from ceasiompy.SU2Run.func.su2actuatordiskfile import (
- get_advanced_ratio,
- get_radial_stations,
- save_plots,
- thrust_calculator,
- write_actuator_disk_data,
- write_header,
-)
-from ceasiompy.CPACS2GMSH.func.mesh_sizing import wings_size
-from ceasiompy.SU2Run.func.su2utils import get_mesh_markers, get_su2_config_template_rans
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.commonnames import (
- ACTUATOR_DISK_FILE_NAME,
- ACTUATOR_DISK_INLET_SUFFIX,
- ACTUATOR_DISK_OUTLET_SUFFIX,
- CONFIG_CFD_NAME,
- SU2_FORCES_BREAKDOWN_NAME,
-)
-from ceasiompy.utils.commonxpath import (
- GMSH_SYMMETRY_XPATH,
- PROP_XPATH,
- RANGE_XPATH,
- SU2_ACTUATOR_DISK_XPATH,
- SU2_AEROMAP_UID_XPATH,
- SU2_BC_FARFIELD_XPATH,
- SU2_BC_WALL_XPATH,
- SU2_CFL_NB_XPATH,
- SU2_CFL_ADAPT_XPATH,
- SU2_CFL_ADAPT_PARAM_DOWN_XPATH,
- SU2_CFL_ADAPT_PARAM_UP_XPATH,
- SU2_CFL_MAX_XPATH,
- SU2_CFL_MIN_XPATH,
- SU2_CONTROL_SURF_XPATH,
- SU2_DAMPING_DER_XPATH,
- SU2_DEF_MESH_XPATH,
- SU2_FIXED_CL_XPATH,
- SU2_MAX_ITER_XPATH,
- SU2_MG_LEVEL_XPATH,
- SU2_ROTATION_RATE_XPATH,
- SU2_TARGET_CL_XPATH,
- SU2MESH_XPATH,
- ENGINE_TYPE_XPATH,
- ENGINE_BC,
-)
-from ceasiompy.utils.configfiles import ConfigFile
-from cpacspy.cpacsfunctions import (
- create_branch,
- get_string_vector,
- get_value,
- get_value_or_default,
-)
-from cpacspy.cpacspy import CPACS
-
-log = get_logger()
-
-MODULE_DIR = Path(__file__).parent
-
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
-
-
-def add_damping_derivatives(cfg, wkdir, case_dir_name, rotation_rate):
- """Add damping derivatives parameter to the config file and save them to their respective
- directory.
-
- Args:
- cfg (ConfigFile): ConfigFile object.
- wkdir (Path): Path to the working directory
- case_dir_name (str): Name of the case directory
- rotation_rate (float): Rotation rate that will be impose to calculate damping derivatives
- """
-
- cfg["GRID_MOVEMENT"] = "ROTATING_FRAME"
-
- cfg["ROTATION_RATE"] = f"{rotation_rate} 0.0 0.0"
- case_dir = Path(wkdir, f"{case_dir_name}_dp")
- case_dir.mkdir()
- cfg.write_file(Path(case_dir, CONFIG_CFD_NAME), overwrite=True)
-
- cfg["ROTATION_RATE"] = f"0.0 {rotation_rate} 0.0"
- case_dir = Path(wkdir, f"{case_dir_name}_dq")
- case_dir.mkdir()
- cfg.write_file(Path(case_dir, CONFIG_CFD_NAME), overwrite=True)
-
- cfg["ROTATION_RATE"] = f"0.0 0.0 {rotation_rate}"
- case_dir = Path(wkdir, f"{case_dir_name}_dr")
- case_dir.mkdir()
- cfg.write_file(Path(case_dir, CONFIG_CFD_NAME), overwrite=True)
-
- log.info("Damping derivatives cases directories has been created.")
-
-
-def add_actuator_disk(cfg, cpacs, case_dir_path, actuator_disk_file, mesh_markers, alt, mach):
- """Add actuator disk parameter to the config file.
-
- Args:
- cfg (ConfigFile): ConfigFile object.
- cpacs (CPACS): CPACS object from cpacspy library
- case_dir_path (Path): Path object to the current case directory
- actuator_disk_file (Path): Path to the actuator disk file
- mesh_markers (dict): Dictionary containing all the mesh markers
-
- Returns:
- cfg (ConfigFile): ConfigFile object.
- """
-
- ad_inlet_marker = sorted(mesh_markers["actuator_disk_inlet"])
- ad_outlet_marker = sorted(mesh_markers["actuator_disk_outlet"])
-
- if "None" in ad_inlet_marker or "None" in ad_outlet_marker:
- return
-
- if len(ad_inlet_marker) != len(ad_outlet_marker):
- raise ValueError(
- "The number of inlet and outlet markers of the actuator disk must be the same."
- )
-
- # Get rotorcraft configuration (propeller)
- try:
- rotorcraft_config = cpacs.rotorcraft.configuration
- except AttributeError:
- raise ValueError(
- "The actuator disk is defined but no rotorcraft configuration is defined in "
- "the CPACS file."
- )
-
- rotor_uid_pos = {}
- for i in range(1, rotorcraft_config.get_rotor_count() + 1):
- rotor = rotorcraft_config.get_rotor(i)
-
- rotor_uid = rotor.get_uid()
- pos_x = rotor.get_translation().x
- pos_y = rotor.get_translation().y
- pos_z = rotor.get_translation().z
- radius = rotor.get_radius()
- hub_radius = 0.0 # TODO: get correctly from CPACS
-
- rotor_xpath = cpacs.tixi.uIDGetXPath(rotor_uid)
-
- number_of_blades_xpath = (
- rotor_xpath + "/rotorHub/rotorBladeAttachments/rotorBladeAttachment/numberOfBlades"
- )
- number_of_blades = get_value_or_default(cpacs.tixi, number_of_blades_xpath, 3)
-
- # TODO: this is the nominal speed, how to get a speed which correspond to each flight cond.
- rotational_velocity_xpath = rotor_xpath + "/nominalRotationsPerMinute"
- rotational_velocity = (
- get_value_or_default(cpacs.tixi, rotational_velocity_xpath, 3000) / 60.0
- )
-
- rotor_uid_pos[rotor_uid] = (
- pos_x,
- pos_y,
- pos_z,
- radius,
- hub_radius,
- number_of_blades,
- rotational_velocity,
- )
-
- cfg["ACTDISK_DOUBLE_SURFACE"] = "YES"
- cfg["ACTDISK_TYPE"] = "VARIABLE_LOAD"
- cfg["ACTDISK_FILENAME"] = ACTUATOR_DISK_FILE_NAME
- cfg["MGLEVEL"] = 0 # Calculation diverges if multigrid is used with a disk actuator
-
- actdisk_markers = []
-
- f = open(actuator_disk_file, "w")
- f = write_header(f)
-
- for maker_inlet, marker_outlet in zip(ad_inlet_marker, ad_outlet_marker):
- inlet_uid = maker_inlet.split(ACTUATOR_DISK_INLET_SUFFIX)[0]
- outlet_uid = marker_outlet.split(ACTUATOR_DISK_OUTLET_SUFFIX)[0]
-
- if inlet_uid != outlet_uid:
- raise ValueError("The inlet and outlet markers of the actuator disk must be the same.")
-
- if "_mirrored" in maker_inlet:
- uid = inlet_uid.split("_mirrored")[0]
- sym = -1
- else:
- uid = inlet_uid
- sym = 1
-
- center = []
- center.append(round(rotor_uid_pos[uid][0], 5))
- center.append(round(sym * rotor_uid_pos[uid][1], 5))
- center.append(round(rotor_uid_pos[uid][2], 5))
-
- axis = (1.0, 0.0, 0.0) # TODO: get the axis by applying the rotation matrix
- radius = round(rotor_uid_pos[uid][3], 5)
- hub_radius = round(rotor_uid_pos[uid][4], 5)
- number_of_blades = round(rotor_uid_pos[uid][5], 5)
- rotational_velocity = round(rotor_uid_pos[uid][6], 5)
-
- actdisk_markers.append(maker_inlet)
- actdisk_markers.append(marker_outlet)
- actdisk_markers.append(str(center[0]))
- actdisk_markers.append(str(center[1]))
- actdisk_markers.append(str(center[2]))
- actdisk_markers.append(str(center[0]))
- actdisk_markers.append(str(center[1]))
- actdisk_markers.append(str(center[2]))
-
- Atm = Atmosphere(alt)
- free_stream_velocity = mach * Atm.speed_of_sound[0]
-
- radial_stations = get_radial_stations(radius, hub_radius)
- advanced_ratio = get_advanced_ratio(free_stream_velocity, rotational_velocity, radius)
-
- prandtl_correction_xpath = PROP_XPATH + "/propeller/blade/loss"
- prandtl_correction = get_value_or_default(cpacs.tixi, prandtl_correction_xpath, True)
-
- thrust_xpath = PROP_XPATH + "/propeller/thrust"
- thrust = get_value_or_default(cpacs.tixi, thrust_xpath, 3000)
- total_thrust_coefficient = float(
- thrust / (Atm.density * rotational_velocity**2 * (radius * 2) ** 4)
- )
-
- (
- radial_thrust_coefs,
- radial_power_coefs,
- non_dimensional_radius,
- optimal_axial_interference_factor,
- optimal_rotational_interference_factor,
- prandtl_correction_values,
- ) = thrust_calculator(
- radial_stations,
- total_thrust_coefficient,
- radius,
- free_stream_velocity,
- prandtl_correction,
- number_of_blades,
- rotational_velocity,
- )
-
- save_plots(
- radial_stations,
- radial_thrust_coefs,
- radial_power_coefs,
- non_dimensional_radius,
- optimal_axial_interference_factor,
- optimal_rotational_interference_factor,
- prandtl_correction_values,
- case_dir_path,
- inlet_uid,
- )
-
- f = write_actuator_disk_data(
- file=f,
- inlet_marker=maker_inlet,
- outlet_marker=marker_outlet,
- center=center,
- axis=axis,
- radius=radius,
- advanced_ratio=advanced_ratio,
- radial_stations=radial_stations,
- radial_thrust_coefs=radial_thrust_coefs,
- radial_power_coefs=radial_power_coefs,
- )
-
- cfg["MARKER_ACTDISK"] = " (" + ", ".join(actdisk_markers) + " )"
-
- f.close()
-
-
-def add_thermodata(cfg, cpacs, alt, case_nb, alt_list):
- if cpacs.tixi.checkElement(ENGINE_TYPE_XPATH):
- log.info("adding engine BC to the SU2 config file")
- engine_type = get_value(cpacs.tixi, ENGINE_TYPE_XPATH)
- log.info(f"engine type {engine_type}")
- alt = alt_list[case_nb]
- Atm = Atmosphere(alt)
- tot_temp_in = Atm.temperature[0]
- tot_pressure_in = Atm.pressure[0]
- if len(alt_list) > 1:
- tot_temp_out = get_value(cpacs.tixi, ENGINE_BC + "/temperatureOutlet").split(";")
- tot_pressure_out = get_value(cpacs.tixi, ENGINE_BC + "/pressureOutlet").split(";")
- tot_temp_out = tot_temp_out[case_nb]
- tot_pressure_out = tot_pressure_out[case_nb]
- else:
- tot_temp_out = get_value(cpacs.tixi, ENGINE_BC + "/temperatureOutlet")
- tot_pressure_out = get_value(cpacs.tixi, ENGINE_BC + "/pressureOutlet")
- cfg["INLET_TYPE"] = "TOTAL_CONDITIONS"
- cfg["MARKER_INLET"] = (
- f"(INLET_ENGINE, {tot_temp_in}, {tot_pressure_in}, {1},{0},{0}, "
- f"OUTLET_ENGINE,{tot_temp_out},{tot_pressure_out}, {1},{0},{0})"
- )
-
-
-def add_reynods_number(alt, mach, cfg, cpacs_path):
-
- Atm = Atmosphere(alt)
-
- # Get speed from Mach Number
- speed = mach * Atm.speed_of_sound[0]
-
- ref_chord = wings_size(cpacs_path)[0] / 0.15
- print(ref_chord)
-
- # Reynolds number based on the mean chord
- reynolds = ref_chord * speed / Atm.kinematic_viscosity[0]
- log.info(f"Reynolds number= {int(reynolds)}")
- cfg["REYNOLDS_NUMBER"] = int(reynolds)
-
-
-def generate_su2_cfd_config_rans(cpacs_path, cpacs_out_path, wkdir):
- """Function to create SU2 config file.
-
- Function 'generate_su2_cfd_config' reads data in the CPACS file and generate configuration
- files for one or multiple flight conditions (alt,mach,aoa,aos)
-
- Source:
- * SU2 config template: https://github.com/su2code/SU2/blob/master/config_template.cfg
-
- Args:
- cpacs_path (Path): Path to CPACS file
- cpacs_out_path (Path):Path to CPACS output file
- wkdir (Path): Path to the working directory
-
- """
-
- cpacs = CPACS(cpacs_path)
-
- # creare delle nuove xpath per la mesh su2
-
- su2_mesh = Path(get_value(cpacs.tixi, SU2MESH_XPATH))
- if not su2_mesh.is_file():
- raise FileNotFoundError(f"SU2 mesh file {su2_mesh} not found")
-
- mesh_markers = get_mesh_markers(su2_mesh)
-
- create_branch(cpacs.tixi, SU2_BC_WALL_XPATH)
- bc_wall_str = ";".join(mesh_markers["wall"])
- cpacs.tixi.updateTextElement(SU2_BC_WALL_XPATH, bc_wall_str)
-
- create_branch(cpacs.tixi, SU2_BC_FARFIELD_XPATH)
- bc_farfiled_str = ";".join(mesh_markers["engine_intake"] + mesh_markers["engine_exhaust"])
- cpacs.tixi.updateTextElement(SU2_BC_FARFIELD_XPATH, bc_farfiled_str)
-
- create_branch(cpacs.tixi, SU2_ACTUATOR_DISK_XPATH)
- bc_actuator_disk_str = ";".join(
- mesh_markers["actuator_disk_inlet"] + mesh_markers["actuator_disk_outlet"]
- )
- cpacs.tixi.updateTextElement(SU2_ACTUATOR_DISK_XPATH, bc_actuator_disk_str)
-
- fixed_cl = get_value_or_default(cpacs.tixi, SU2_FIXED_CL_XPATH, "NO")
- target_cl = get_value_or_default(cpacs.tixi, SU2_TARGET_CL_XPATH, 1.0)
-
- if fixed_cl == "NO":
- # Get the first aeroMap as default one or create automatically one
- aeromap_list = cpacs.get_aeromap_uid_list()
-
- if aeromap_list:
- aeromap_default = aeromap_list[0]
- log.info(f"The aeromap is {aeromap_default}")
-
- aeromap_uid = get_value_or_default(cpacs.tixi, SU2_AEROMAP_UID_XPATH, aeromap_default)
-
- activate_aeromap = cpacs.get_aeromap_by_uid(aeromap_uid)
- alt_list = activate_aeromap.get("altitude").tolist()
- mach_list = activate_aeromap.get("machNumber").tolist()
- aoa_list = activate_aeromap.get("angleOfAttack").tolist()
- aos_list = activate_aeromap.get("angleOfSideslip").tolist()
-
- else:
- default_aeromap = cpacs.create_aeromap("DefaultAeromap")
- default_aeromap.description = "AeroMap created automatically"
-
- mach = get_value_or_default(cpacs.tixi, RANGE_XPATH + "/cruiseMach", 0.3)
- alt = get_value_or_default(cpacs.tixi, RANGE_XPATH + "/cruiseAltitude", 10000)
-
- default_aeromap.add_row(alt=alt, mach=mach, aos=0.0, aoa=0.0)
- default_aeromap.save()
-
- alt_list = [alt]
- mach_list = [mach]
- aoa_list = [0.0]
- aos_list = [0.0]
-
- aeromap_uid = get_value_or_default(cpacs.tixi, SU2_AEROMAP_UID_XPATH, "DefaultAeromap")
- log.info(f"{aeromap_uid} has been created")
-
- else: # if fixed_cl == 'YES':
- log.info("Configuration file for fixed CL calculation will be created.")
-
- fixed_cl_aeromap = cpacs.create_aeromap("aeroMap_fixedCL_SU2")
- fixed_cl_aeromap.description = f"AeroMap created for SU2 fixed CL value of {target_cl}"
-
- mach = get_value_or_default(cpacs.tixi, RANGE_XPATH + "/cruiseMach", 0.78)
- alt = get_value_or_default(cpacs.tixi, RANGE_XPATH + "/cruiseAltitude", 12000)
-
- fixed_cl_aeromap.add_row(alt=alt, mach=mach, aos=0.0, aoa=0.0)
- fixed_cl_aeromap.save()
-
- alt_list = [alt]
- mach_list = [mach]
- aoa_list = [0.0]
- aos_list = [0.0]
-
- cfg = ConfigFile(get_su2_config_template_rans())
-
- # Check if symmetry plane is defined (Default: False)
- sym_factor = 1.0
- if get_value_or_default(cpacs.tixi, GMSH_SYMMETRY_XPATH, False):
- log.info("Symmetry plane is defined. The reference area will be divided by 2.")
- sym_factor = 2.0
-
- # General parameters
- cfg["RESTART_SOL"] = "NO"
- cfg["REF_LENGTH"] = cpacs.aircraft.ref_length
- cfg["REF_AREA"] = cpacs.aircraft.ref_area / sym_factor
- cfg["REF_ORIGIN_MOMENT_X"] = cpacs.aircraft.ref_point_x
- cfg["REF_ORIGIN_MOMENT_Y"] = cpacs.aircraft.ref_point_y
- cfg["REF_ORIGIN_MOMENT_Z"] = cpacs.aircraft.ref_point_z
-
- # Settings
-
- cfl_down = get_value_or_default(cpacs.tixi, SU2_CFL_ADAPT_PARAM_DOWN_XPATH, 0.5)
- cfl_up = get_value_or_default(cpacs.tixi, SU2_CFL_ADAPT_PARAM_UP_XPATH, 1.5)
- cfl_min = get_value_or_default(cpacs.tixi, SU2_CFL_MIN_XPATH, 0.5)
- cfl_max = get_value_or_default(cpacs.tixi, SU2_CFL_MAX_XPATH, 100)
-
- if get_value_or_default(cpacs.tixi, SU2_CFL_ADAPT_XPATH, True):
- cfg["CFL_ADAPT"] = "YES"
-
- else:
- cfg["CFL_ADAPT"] = "NO"
-
- cfg["INNER_ITER"] = int(get_value_or_default(cpacs.tixi, SU2_MAX_ITER_XPATH, 200))
- cfg["CFL_NUMBER"] = get_value_or_default(cpacs.tixi, SU2_CFL_NB_XPATH, 1.0)
- cfg["CFL_ADAPT_PARAM"] = f"( {cfl_down}, {cfl_up}, {cfl_min}, {cfl_max} )"
- cfg["MGLEVEL"] = int(get_value_or_default(cpacs.tixi, SU2_MG_LEVEL_XPATH, 3))
-
- # Fixed CL mode (AOA will not be taken into account)
- cfg["FIXED_CL_MODE"] = fixed_cl
- cfg["TARGET_CL"] = target_cl
- cfg["DCL_DALPHA"] = "0.1"
- cfg["UPDATE_AOA_ITER_LIMIT"] = "50"
- cfg["ITER_DCL_DALPHA"] = "80"
-
- # Mesh Marker
- bc_wall_str = f"( {','.join(mesh_markers['wall'])} )"
-
- cfg["MARKER_EULER"] = bc_wall_str
- farfield_bc = (
- mesh_markers["farfield"] + mesh_markers["engine_intake"] + mesh_markers["engine_exhaust"]
- )
- cfg["MARKER_FAR"] = f"( {','.join(farfield_bc)} )"
- cfg["MARKER_SYM"] = f"( {','.join(mesh_markers['symmetry'])} )"
- cfg["MARKER_PLOTTING"] = bc_wall_str
- cfg["MARKER_MONITORING"] = bc_wall_str
- cfg["MARKER_MOVING"] = "( NONE )" # TODO: when do we need to define MARKER_MOVING?
- cfg["DV_MARKER"] = bc_wall_str
-
- # Output
- cfg["WRT_FORCES_BREAKDOWN"] = "YES"
- cfg["BREAKDOWN_FILENAME"] = SU2_FORCES_BREAKDOWN_NAME
- cfg["OUTPUT_FILES"] = "(RESTART, PARAVIEW, SURFACE_PARAVIEW)"
- cfg["HISTORY_OUTPUT"] = "(INNER_ITER, RMS_RES, AERO_COEFF)"
-
- # Parameters which will vary for the different cases (alt,mach,aoa,aos)
-
- for case_nb in range(len(alt_list)):
- cfg["MESH_FILENAME"] = str(su2_mesh)
-
- alt = alt_list[case_nb]
- mach = mach_list[case_nb]
- aoa = aoa_list[case_nb]
- aos = aos_list[case_nb]
-
- Atm = Atmosphere(alt)
-
- cfg["MACH_NUMBER"] = mach
- cfg["AOA"] = aoa
- cfg["SIDESLIP_ANGLE"] = aos
- cfg["FREESTREAM_PRESSURE"] = Atm.pressure[0]
- cfg["FREESTREAM_TEMPERATURE"] = Atm.temperature[0]
- cfg["ROTATION_RATE"] = "0.0 0.0 0.0"
-
- case_dir_name = (
- f"Case{str(case_nb).zfill(2)}_alt{alt}_mach{round(mach, 2)}"
- f"_aoa{round(aoa, 1)}_aos{round(aos, 1)}"
- )
-
- add_thermodata(cfg, cpacs, alt, case_nb, alt_list)
-
- add_reynods_number(alt, mach, cfg, cpacs_path)
-
- case_dir_path = Path(wkdir, case_dir_name)
- if not case_dir_path.exists():
- case_dir_path.mkdir()
-
- if get_value_or_default(cpacs.tixi, SU2_ACTUATOR_DISK_XPATH, False):
- actuator_disk_file = Path(wkdir, ACTUATOR_DISK_FILE_NAME)
- add_actuator_disk(
- cfg, cpacs, case_dir_path, actuator_disk_file, mesh_markers, alt, mach
- )
-
- if actuator_disk_file.exists():
- case_actuator_disk_file = Path(case_dir_path, ACTUATOR_DISK_FILE_NAME)
- copyfile(actuator_disk_file, case_actuator_disk_file)
-
- bc_wall_str = (
- "("
- + ",".join(
- mesh_markers["wall"]
- + mesh_markers["actuator_disk_inlet"]
- + mesh_markers["actuator_disk_outlet"]
- )
- + ")"
- )
-
- cfg["MARKER_PLOTTING"] = bc_wall_str
- cfg["MARKER_MONITORING"] = bc_wall_str
-
- config_output_path = Path(case_dir_path, CONFIG_CFD_NAME)
- cfg.write_file(config_output_path, overwrite=True)
-
- if get_value_or_default(cpacs.tixi, SU2_DAMPING_DER_XPATH, False):
- rotation_rate = get_value_or_default(cpacs.tixi, SU2_ROTATION_RATE_XPATH, 1.0)
- add_damping_derivatives(cfg, wkdir, case_dir_name, rotation_rate)
-
- # Control surfaces deflections (TODO: create a subfunctions)
- if get_value_or_default(cpacs.tixi, SU2_CONTROL_SURF_XPATH, False):
- # Get deformed mesh list
- if cpacs.tixi.checkElement(SU2_DEF_MESH_XPATH):
- su2_def_mesh_list = get_string_vector(cpacs.tixi, SU2_DEF_MESH_XPATH)
- else:
- log.warning("No SU2 deformed mesh has been found!")
- su2_def_mesh_list = []
-
- for su2_def_mesh in su2_def_mesh_list:
- mesh_path = Path(wkdir, "MESH", su2_def_mesh)
- config_dir_path = Path(wkdir, case_dir_name + "_" + su2_def_mesh.split(".")[0])
- config_dir_path.mkdir()
- cfg["MESH_FILENAME"] = mesh_path
-
- cfg.write_file(Path(config_dir_path, CONFIG_CFD_NAME), overwrite=True)
-
- cpacs.save_cpacs(cpacs_out_path, overwrite=True)
-
-
-# =================================================================================================
-# MAIN
-# =================================================================================================
-
-if __name__ == "__main__":
- log.info("Nothing to execute!")
diff --git a/ceasiompy/SU2Run/func/su2results.py b/ceasiompy/SU2Run/func/su2results.py
deleted file mode 100644
index 87c06b992..000000000
--- a/ceasiompy/SU2Run/func/su2results.py
+++ /dev/null
@@ -1,260 +0,0 @@
-"""
-CEASIOMpy: Conceptual Aircraft Design Software
-
-Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-
-Extract results from SU2 calculations and save them in a CPACS file.
-
-Python version: >=3.8
-
-| Author: Aidan Jungo
-| Creation: 2019-10-02
-
-TODO:
-
- * Saving for Control surface deflections
-
-"""
-
-# =================================================================================================
-# IMPORTS
-# =================================================================================================
-
-import os
-from pathlib import Path
-
-import pyvista as pv
-from ceasiompy.SU2Run.func.extractloads import extract_loads
-from ceasiompy.SU2Run.func.su2utils import (
- get_efficiency_and_aoa,
- get_su2_aerocoefs,
- get_wetted_area,
-)
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.commonnames import SU2_FORCES_BREAKDOWN_NAME, SURFACE_FLOW_FILE_NAME
-from ceasiompy.utils.commonxpath import (
- GMSH_SYMMETRY_XPATH,
- RANGE_LD_RATIO_XPATH,
- SU2_AEROMAP_UID_XPATH,
- SU2_EXTRACT_LOAD_XPATH,
- SU2_FIXED_CL_XPATH,
- SU2_ROTATION_RATE_XPATH,
- SU2_UPDATE_WETTED_AREA_XPATH,
- WETTED_AREA_XPATH,
-)
-from cpacspy.cpacsfunctions import create_branch, get_value, get_value_or_default
-from cpacspy.cpacspy import CPACS
-from cpacspy.utils import COEFS
-
-log = get_logger()
-
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
-
-
-def save_screenshot(surface_flow_file, scalar="Mach"):
- """Save a screenshot (.png) from a surface_flow file (.vtu) generated by SU2.
-
- Args:
- surface_flow_file (Path): Path to the Surface flow file
- scalar (str, optional): Scalar value represented on the screenshot.
- """
-
- # Initialize pyvista reader and plotter
- plotter = pv.Plotter(off_screen=True)
-
- # Read data and send to plotter
- mesh = pv.read(str(surface_flow_file))
- plotter.add_mesh(mesh, scalars=scalar, show_scalar_bar=True)
-
- # Set camera
- plotter.camera.azimuth = 110.0
- plotter.camera.elevation = -25.0
- plotter.camera.zoom(1.6)
-
- # Save Screenshot
- screenshot_filename = Path(surface_flow_file.parent, f"3d_view_{scalar}.png")
- plotter.show(screenshot=screenshot_filename)
-
- log.info(f"A screenshot has been saved at {screenshot_filename}")
-
- return screenshot_filename
-
-
-def get_su2_results(cpacs_path, cpacs_out_path, wkdir):
- """Function to write SU2 results in a CPACS file.
-
- Function 'get_su2_results' gets available results from the latest SU2 calculation and put them
- at the correct place in the CPACS file.
-
- '/cpacs/vehicles/aircraft/model/analyses/aeroPerformance/aeroMap[n]/aeroPerformanceMap'
-
- Args:
- cpacs_path (Path): Path to input CPACS file
- cpacs_out_path (Path): Path to output CPACS file
- wkdir (Path): Path to the working directory
-
- """
-
- cpacs = CPACS(cpacs_path)
-
- if not wkdir.exists():
- raise OSError(f"The working directory : {wkdir} does not exit!")
-
- fixed_cl = get_value_or_default(cpacs.tixi, SU2_FIXED_CL_XPATH, "NO")
-
- if fixed_cl == "YES":
- aeromap_uid = "aeroMap_fixedCL_SU2"
- elif fixed_cl == "NO":
- aeromap_uid = get_value(cpacs.tixi, SU2_AEROMAP_UID_XPATH)
- else:
- raise ValueError("The value for fixed_cl is not valid! Should be YES or NO")
-
- log.info(f"The aeromap uid is: {aeromap_uid}")
- aeromap = cpacs.get_aeromap_by_uid(aeromap_uid)
-
- alt_list = aeromap.get("altitude").tolist()
- mach_list = aeromap.get("machNumber").tolist()
- aoa_list = aeromap.get("angleOfAttack").tolist()
- aos_list = aeromap.get("angleOfSideslip").tolist()
-
- case_dir_list = [case_dir for case_dir in wkdir.iterdir() if "Case" in case_dir.name]
-
- found_wetted_area = False
-
- for config_dir in sorted(case_dir_list):
-
- if not config_dir.is_dir():
- continue
-
- surface_flow_path = Path(config_dir, SURFACE_FLOW_FILE_NAME)
- if surface_flow_path.exists() and "DISPLAY" in os.environ:
- save_screenshot(surface_flow_path, "Mach")
- save_screenshot(surface_flow_path, "Pressure_Coefficient")
-
- force_file_path = Path(config_dir, SU2_FORCES_BREAKDOWN_NAME)
- if not force_file_path.exists():
- raise OSError("No result force file have been found!")
-
- baseline_coef = True
-
- case_nb = int(config_dir.name.split("_")[0].split("Case")[1])
-
- aoa = aoa_list[case_nb]
- aos = aos_list[case_nb]
- mach = mach_list[case_nb]
- alt = alt_list[case_nb]
-
- if fixed_cl == "YES":
- cl_cd, aoa = get_efficiency_and_aoa(force_file_path)
-
- # Replace aoa with the with the value from fixed cl calculation
- aeromap.df.loc[0, ["angleOfAttack"]] = aoa
-
- # Save cl/cd found during the fixed CL calculation (useful for range analysis)
- create_branch(cpacs.tixi, RANGE_LD_RATIO_XPATH)
- cpacs.tixi.updateDoubleElement(RANGE_LD_RATIO_XPATH, cl_cd, "%g")
-
- cl, cd, cs, cmd, cms, cml, velocity = get_su2_aerocoefs(force_file_path)
-
- # Damping derivatives
- rotation_rate = get_value_or_default(cpacs.tixi, SU2_ROTATION_RATE_XPATH, -1.0)
- ref_len = cpacs.aircraft.ref_length
- adim_rot_rate = rotation_rate * ref_len / velocity
-
- coefs = {"cl": cl, "cd": cd, "cs": cs, "cmd": cmd, "cms": cms, "cml": cml}
-
- for axis in ["dp", "dq", "dr"]:
-
- if f"_{axis}" not in config_dir.name:
- continue
-
- baseline_coef = False
-
- for coef in COEFS:
- coef_baseline = aeromap.get(coef, alt=alt, mach=mach, aoa=aoa, aos=aos)
- dcoef = (coefs[coef] - coef_baseline) / adim_rot_rate
- aeromap.add_damping_derivatives(
- alt=alt,
- mach=mach,
- aos=aos,
- aoa=aoa,
- coef=coef,
- axis=axis,
- value=dcoef,
- rate=rotation_rate,
- )
-
- if "_TED_" in config_dir.name:
-
- # TODO: convert when it is possible to save TED in cpacspy
- raise NotImplementedError("TED not implemented yet")
-
- # baseline_coef = False
- # config_dir_split = config_dir.split('_')
- # ted_idx = config_dir_split.index('TED')
- # ted_uid = config_dir_split[ted_idx+1]
- # defl_angle = float(config_dir.split('_defl')[1])
- # try:
- # print(Coef.IncrMap.dcl)
- # except AttributeError:
- # Coef.IncrMap = a.p.m.f.IncrementMap(ted_uid)
- # dcl = (cl-Coef.cl[-1])
- # dcd = (cd-Coef.cd[-1])
- # dcs = (cs-Coef.cs[-1])
- # dcml = (cml-Coef.cml[-1])
- # dcmd = (cmd-Coef.cmd[-1])
- # dcms = (cms-Coef.cms[-1])
- # control_parameter = -1
- # Coef.IncrMap.add_cs_coef(dcl,dcd,dcs,dcml,dcmd,dcms,ted_uid,control_parameter)
-
- # Baseline coefficients (no damping derivative or control surfaces case)
- if baseline_coef:
- aeromap.add_coefficients(
- alt=alt,
- mach=mach,
- aos=aos,
- aoa=aoa,
- cd=cd,
- cl=cl,
- cs=cs,
- cml=cml,
- cmd=cmd,
- cms=cms,
- )
-
- update_wetted_area = get_value_or_default(cpacs.tixi, SU2_UPDATE_WETTED_AREA_XPATH, False)
- if not found_wetted_area and update_wetted_area:
- wetted_area = get_wetted_area(Path(config_dir, "logfile_SU2_CFD.log"))
-
- # Check if symmetry plane is defined (Default: False)
- sym_factor = 1.0
- if get_value_or_default(cpacs.tixi, GMSH_SYMMETRY_XPATH, False):
- log.info("Symmetry plane is defined. The wetted area will be multiplied by 2.")
- sym_factor = 2.0
-
- create_branch(cpacs.tixi, WETTED_AREA_XPATH)
- cpacs.tixi.updateDoubleElement(WETTED_AREA_XPATH, wetted_area * sym_factor, "%g")
- found_wetted_area = True
-
- if get_value_or_default(cpacs.tixi, SU2_EXTRACT_LOAD_XPATH, False):
- extract_loads(config_dir)
-
- aeromap.save()
- cpacs.save_cpacs(cpacs_out_path, overwrite=True)
-
-
-# =================================================================================================
-# MAIN
-# =================================================================================================
-
-if __name__ == "__main__":
-
- log.info("Nothing to execute!")
diff --git a/ceasiompy/SU2Run/func/su2utils.py b/ceasiompy/SU2Run/func/su2utils.py
deleted file mode 100644
index b815cb012..000000000
--- a/ceasiompy/SU2Run/func/su2utils.py
+++ /dev/null
@@ -1,298 +0,0 @@
-"""
-CEASIOMpy: Conceptual Aircraft Design Software
-
-Developed for CFS ENGINEERING, 1015 Lausanne, Switzerland
-
-Functions to manipulate SU2 configuration and results.
-
-Python version: >=3.8
-
-| Author : Aidan Jungo
-| Creation: 2019-09-30
-
-TODO:
-
-"""
-
-# =================================================================================================
-# IMPORTS
-# =================================================================================================
-
-import re
-from pathlib import Path
-
-import requests
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.ceasiompyutils import get_install_path
-from ceasiompy.utils.commonnames import (
- ACTUATOR_DISK_INLET_SUFFIX,
- ACTUATOR_DISK_OUTLET_SUFFIX,
- ENGINE_EXHAUST_SUFFIX,
- ENGINE_INTAKE_SUFFIX,
-)
-from ceasiompy.utils.moduleinterfaces import get_module_path
-
-log = get_logger()
-
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
-
-
-def get_mesh_markers(su2_mesh_path):
- """Function to get the name of all the SU2 mesh marker
-
- Function 'get_mesh_markers' return a dictionary of the mesh markers found in the SU2 mesh file
- sorted as farfield, symmetry, engine_intake, engine_exhaust, wall.
-
- Args:
- su2_mesh_path (Path): Path to the SU2 mesh
-
- Returns:
- mesh_markers (dict): Dictionary of the mesh markers found in the SU2 mesh file
-
- """
-
- if not su2_mesh_path.is_file():
- raise FileNotFoundError(f"The SU2 mesh '{su2_mesh_path}' has not been found!")
-
- if not su2_mesh_path.suffix == ".su2":
- raise ValueError("The input must be SU2 mesh (*.su2)!")
-
- mesh_markers = {
- "farfield": [],
- "symmetry": [],
- "engine_intake": [],
- "engine_exhaust": [],
- "actuator_disk_inlet": [],
- "actuator_disk_outlet": [],
- "wall": [],
- }
-
- with open(su2_mesh_path) as f:
- lines = f.readlines()
-
- for line in lines:
- if "MARKER_TAG" not in line:
- continue
-
- marker = line.split("=")[1].strip()
-
- if "farfield" in marker.lower():
- mesh_markers["farfield"].append(marker)
- log.info(f"'{marker}' marker has been marked as farfield.")
- elif "symmetry" in marker.lower():
- mesh_markers["symmetry"].append(marker)
- log.info(f"'{marker}' marker has been marked as symmetry.")
- elif marker.endswith(ENGINE_INTAKE_SUFFIX):
- mesh_markers["engine_intake"].append(marker)
- log.info(f"'{marker}' marker has been marked as engine_intake.")
- elif marker.endswith(ENGINE_EXHAUST_SUFFIX):
- mesh_markers["engine_exhaust"].append(marker)
- log.info(f"'{marker}' marker has been marked as engine_exhaust.")
- elif marker.endswith(ACTUATOR_DISK_INLET_SUFFIX):
- mesh_markers["actuator_disk_inlet"].append(marker)
- log.info(f"'{marker}' marker has been marked as actuator_disk_inlet.")
- elif marker.endswith(ACTUATOR_DISK_OUTLET_SUFFIX):
- mesh_markers["actuator_disk_outlet"].append(marker)
- log.info(f"'{marker}' marker has been marked as actuator_disk_outlet.")
- else:
- mesh_markers["wall"].append(marker)
- log.info(f"'{marker}' marker has been marked as wall.")
-
- if not any(mesh_markers.values()):
- raise ValueError("No mesh markers found!")
-
- for key, value in mesh_markers.items():
- if not value:
- mesh_markers[key] = ["None"]
-
- return mesh_markers
-
-
-def get_su2_version():
- """
- Return the installed version of SU2.
- """
-
- su2py_path = get_install_path("SU2_CFD.py")
-
- if not su2py_path:
- return None
-
- with open(su2py_path, "r") as f:
- for line in f.readlines():
- if "version" not in line:
- continue
-
- version = re.search(r"version\s*([\d.]+)", line).group(1)
- log.info(f"Version of SU2: {version}")
- return version
-
-
-def get_su2_config_template():
- """Return path of the SU2 config template corresponding to the SU2 version."""
-
- # su2_version = get_su2_version()
- su2_dir = get_module_path("SU2Run")
- su2_config_template_path_euler = Path(su2_dir, "files", "config_template_euler.cfg")
-
- # su2_config_template_path = Path(su2_dir, "files", f"config_template_v{su2_version}.cfg")
-
- # if not su2_config_template_path.exists():
-
- # # Use the Euler Onera M6 config as template
- # url = (
- # f"https://raw.githubusercontent.com/su2code/SU2/v{su2_version}"
- # "/TestCases/euler/oneram6/inv_ONERAM6.cfg"
- # )
- # r = requests.get(url)
-
- # if r.status_code == 404:
- # raise FileNotFoundError(
- # f"The SU2 config template for SU2 version {su2_version} does not exist."
- # )
-
- # if not r.status_code == 200:
- # raise ConnectionError(
- # f"Cannot download the template file for SU2 version {su2_version} at {url}"
- # )
-
- # with open(su2_config_template_path, "wb") as f:
- # f.write(r.content)
-
- return su2_config_template_path_euler
-
-
-def get_su2_config_template_rans():
- su2_dir = get_module_path("SU2Run")
- su2_config_template_path_rans = Path(su2_dir, "files", "config_template_rans.cfg")
-
- return su2_config_template_path_rans
-
-
-def get_su2_aerocoefs(force_file):
- """Get aerodynamic coefficients and velocity from SU2 forces file (forces_breakdown.dat)
-
- Args:
- force_file (Path): Path to the SU2 forces file
-
- Returns:
- cl, cd, cs, cmd, cms, cml, velocity: Aerodynamic coefficients and velocity
- """
-
- if not force_file.is_file():
- raise FileNotFoundError(f"The SU2 forces file '{force_file}' has not been found!")
-
- cl, cd, cs, cmd, cms, cml, velocity = None, None, None, None, None, None, None
-
- with open(force_file) as f:
- for line in f.readlines():
- if "Total CL:" in line:
- cl = float(line.split(":")[1].split("|")[0])
- if "Total CD:" in line:
- cd = float(line.split(":")[1].split("|")[0])
- if "Total CSF:" in line:
- cs = float(line.split(":")[1].split("|")[0])
- # TODO: Check which axis name correspond to that: cml, cmd, cms
- if "Total CMx:" in line:
- cmd = float(line.split(":")[1].split("|")[0])
- if "Total CMy:" in line:
- cms = float(line.split(":")[1].split("|")[0])
- if "Total CMz:" in line:
- cml = float(line.split(":")[1].split("|")[0])
- if "Free-stream velocity" in line and "m/s" in line:
- velocity = float(line.split(" ")[7])
-
- return cl, cd, cs, cmd, cms, cml, velocity
-
-
-def get_efficiency_and_aoa(force_file):
- """Function to get efficiency (CL/CD) and angle of attack (AoA)
-
- Function 'get_efficiency_and_aoa' search for the efficiency (CL/CD) and
- the Angle of Attack (AoA) in the results file (forces_breakdown.dat)
-
- Args:
- force_file (Path): Path to the SU2 forces file
-
- Returns:
- cl_cd (float): CL/CD ratio [-]
- aoa (float): Angle of Attack [deg]
-
- """
-
- if not force_file.is_file():
- raise FileNotFoundError(f"The SU2 forces file '{force_file}' has not been found!")
-
- cl_cd = None
- aoa = None
-
- with open(force_file) as f:
- for line in f.readlines():
- if "CL/CD" in line:
- cl_cd = float(line.split(":")[1].split("|")[0])
- continue
-
- if "Angle of attack (AoA):" in line:
- aoa = float(line.split("Angle of attack (AoA):")[1].split("deg,")[0].strip())
- continue
-
- if cl_cd and aoa:
- break
-
- if cl_cd is None:
- raise ValueError(f"No value has been found for the CL/CD ratio in {force_file}")
- else:
- log.info("CL/CD ratio has been found and is equal to: " + str(cl_cd) + "[-]")
-
- if aoa is None:
- raise ValueError(f"No value has been found for the AoA in {force_file}")
- else:
- log.info("AoA has been found and is equal to: " + str(aoa) + "[-]")
-
- return cl_cd, aoa
-
-
-def get_wetted_area(su2_logfile):
- """Function get the wetted area calculated by SU2
-
- Function 'get_wetted_area' finds the SU2 logfile and returns the wetted
- area value previously calculated by SU2.
-
- Args:
- su2_logfile (Path): Path to the working directory
-
- Returns:
- wetted_area (float): Wetted area calculated by SU2 [m^2]
-
- """
-
- if not su2_logfile.is_file():
- raise FileNotFoundError(f"The SU2 logfile '{su2_logfile}' has not been found!")
-
- with open(su2_logfile) as f:
- for line in f.readlines():
- if "Wetted area =" not in line:
- continue
-
- wetted_area = float(line.split(" ")[3])
- log.info(f"Wetted area value has been found : {wetted_area} [m^2]")
- return wetted_area
-
- log.warning("No value has been found for the wetted area!, returning 0 [m^2]")
- return 0
-
-
-# =================================================================================================
-# MAIN
-# =================================================================================================
-
-if __name__ == "__main__":
- print("Nothing to execute!")
diff --git a/ceasiompy/SU2Run/func/utils.py b/ceasiompy/SU2Run/func/utils.py
new file mode 100644
index 000000000..269a2e460
--- /dev/null
+++ b/ceasiompy/SU2Run/func/utils.py
@@ -0,0 +1,520 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed for CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Functions to manipulate SU2 configuration and results.
+
+
+| Author : Aidan Jungo
+| Creation: 2019-09-30
+| Modified: Leon Deligny
+| Date: 24-Feb-2025
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+
+from cpacspy.cpacsfunctions import get_value
+
+from pathlib import Path
+from tixi3.tixi3wrapper import Tixi3
+from ceasiompy.utils.configfiles import ConfigFile
+from ceasiompy.Database.func.storing import CeasiompyDb
+from typing import (
+ Dict,
+ List,
+ Tuple,
+)
+
+from ceasiompy import log
+from ceasiompy.SU2Run.func import (
+ SU2_FORCES_MOM,
+ AERO_COEFFICIENTS,
+)
+from ceasiompy.SU2Run import (
+ MODULE_DIR,
+ TEMPLATE_TYPE,
+ CONTROL_SURFACE_LIST
+)
+from ceasiompy.utils.commonxpath import (
+ USED_SU2_MESH_XPATH,
+ SU2_CONTROL_SURF_BOOL_XPATH,
+ SU2_CONTROL_SURF_ANGLE_XPATH,
+ SU2_AEROMAP_UID_XPATH,
+)
+from ceasiompy.utils.commonnames import (
+ CONFIG_CFD_NAME,
+ ENGINE_INTAKE_SUFFIX,
+ ENGINE_EXHAUST_SUFFIX,
+ ACTUATOR_DISK_INLET_SUFFIX,
+ ACTUATOR_DISK_OUTLET_SUFFIX,
+ SU2_FORCES_BREAKDOWN_NAME,
+)
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def check_one_entry(dict_dir: List[Dict], mach: float, alt: float, angle: str) -> Path:
+ """
+ Check that there exists exactly one entry with "angle" == "none" in dict_dir
+ """
+
+ one_entry = [
+ d for d in dict_dir
+ if d['mach'] == mach and d['alt'] == alt and d['angle'] == angle
+ ]
+ if len(one_entry) != 1:
+ raise ValueError(
+ f"Expected exactly one angle={angle}"
+ f"for mach={mach}, alt={alt}, "
+ f"but found {len(one_entry)}."
+ )
+ else:
+ return one_entry[0]["dir"]
+
+
+def process_config_dir(config_dir: Path, dict_dir: List[Dict]) -> None:
+ config_name = config_dir.name
+ log.info(f"dir.name {config_name}")
+ if config_name.endswith("aoa0.0_aos0.0"):
+ angle = "none"
+ elif config_name.endswith("dynstab"):
+ angle = config_name.split("_")[3].split("angle")[1]
+ else:
+ log.warning(f"Skipping results of directory {config_dir}.")
+
+ dict_dir.append(
+ {
+ "mach": float(config_name.split("_")[2].split("mach")[1]),
+ "alt": float(config_name.split("_")[1].split("alt")[1]),
+ "dir": config_dir,
+ "angle": angle,
+ },
+ )
+
+
+def check_force_file_exists(config_dir: Path) -> Path:
+ force_file_path = Path(config_dir, "no_deformation", SU2_FORCES_BREAKDOWN_NAME)
+ if not force_file_path.exists():
+ raise OSError("No result force file have been found!")
+ return force_file_path
+
+
+def get_aeromap_uid(tixi: Tixi3, fixed_cl: str) -> str:
+ if fixed_cl == "YES":
+ return "aeroMap_fixedCL_SU2"
+ else:
+ return str(get_value(tixi, SU2_AEROMAP_UID_XPATH))
+
+
+def su2_format(string: str) -> str:
+ """
+ Converts a string to SU2 tuple string format.
+ """
+ return "( " + string + " )"
+
+
+def save_cfg_dir(
+ cfg: ConfigFile,
+ wkdir: Path,
+ case_dir_name: str,
+ rate_type: str,
+ cfg_dict: Dict,
+ cfg_param: str
+) -> None:
+ """Specific use for add_damping_derivatives."""
+ cfg[cfg_param] = cfg_dict[rate_type][0]
+ drate = cfg_dict[rate_type][1]
+ case_dir = Path(wkdir, f"{case_dir_name}_{drate}")
+ case_dir.mkdir()
+ cfg.write_file(Path(case_dir, CONFIG_CFD_NAME), overwrite=True)
+
+
+def add_damping_derivatives(
+ cfg: ConfigFile,
+ wkdir: Path,
+ case_dir_name: str,
+ rotation_rate: float
+) -> None:
+ """
+ Add damping derivatives parameter to the config file cfg and
+ save them to their respective directory.
+
+ Args:
+ cfg (ConfigFile): ConfigFile object.
+ wkdir (Path): Path to the working directory.
+ case_dir_name (str): Name of the case directory.
+ rotation_rate (float): Rotation rate that will be impose to calculate damping derivatives.
+
+ """
+
+ cfg["GRID_MOVEMENT"] = "ROTATING_FRAME"
+
+ RATE_DICT = {
+ "roll": [f"{rotation_rate} 0.0 0.0", f"p_{rotation_rate}"],
+ "pitch": [f"0.0 {rotation_rate} 0.0", f"q_{rotation_rate}"],
+ "yaw": [f"0.0 0.0 {rotation_rate}", f"r_{rotation_rate}"],
+ }
+
+ RATE = "ROTATION_RATE"
+ save_cfg_dir(cfg, wkdir, case_dir_name, "roll", RATE_DICT, RATE)
+ save_cfg_dir(cfg, wkdir, case_dir_name, "pitch", RATE_DICT, RATE)
+ save_cfg_dir(cfg, wkdir, case_dir_name, "yaw", RATE_DICT, RATE)
+
+ log.info("Damping derivatives cases directories have been created.")
+
+
+def access_coef(line: str) -> float:
+ """
+ Extracts and returns the coefficient from a given line of text.
+ """
+ return float(line.split(":")[1].split("|")[0])
+
+
+def validate_file(_file: Path, file_name: str) -> None:
+ """
+ Check if the _file is indeed a file.
+ """
+ if not _file.is_file():
+ raise FileNotFoundError(f"The {file_name} file at '{_file}' has not been found!")
+
+
+def get_mesh_markers(su2_mesh_path: Path) -> Dict:
+ """
+ Get the name of all the SU2 mesh marker found in the SU2 mesh file
+ sorted as farfield, symmetry, engine_intake, engine_exhaust and wall.
+
+ Args:
+ su2_mesh_path (Path): Path to the SU2 mesh.
+
+ Returns:
+ Dict: Dictionary of the mesh markers found in the SU2 mesh file.
+
+ """
+
+ validate_file(su2_mesh_path, "SU2 mesh")
+
+ # Check if it is a su2 file
+ if not su2_mesh_path.suffix == ".su2":
+ raise ValueError("The input must be SU2 mesh (*.su2)!")
+
+ mesh_markers = {
+ "farfield": [],
+ "symmetry": [],
+ "engine_intake": [],
+ "engine_exhaust": [],
+ "actuator_disk_inlet": [],
+ "actuator_disk_outlet": [],
+ "wall": [],
+ }
+
+ with open(su2_mesh_path) as f:
+
+ for line in f.readlines():
+ if "MARKER_TAG" not in line:
+ continue
+
+ marker = line.split("=")[1].strip()
+
+ if "farfield" in marker.lower():
+ mesh_markers["farfield"].append(marker)
+ log.info(f"'{marker}' marker has been marked as farfield.")
+ elif "symmetry" in marker.lower():
+ mesh_markers["symmetry"].append(marker)
+ log.info(f"'{marker}' marker has been marked as symmetry.")
+ elif marker.endswith(ENGINE_INTAKE_SUFFIX):
+ mesh_markers["engine_intake"].append(marker)
+ log.info(f"'{marker}' marker has been marked as engine_intake.")
+ elif marker.endswith(ENGINE_EXHAUST_SUFFIX):
+ mesh_markers["engine_exhaust"].append(marker)
+ log.info(f"'{marker}' marker has been marked as engine_exhaust.")
+ elif marker.endswith(ACTUATOR_DISK_INLET_SUFFIX):
+ mesh_markers["actuator_disk_inlet"].append(marker)
+ log.info(f"'{marker}' marker has been marked as actuator_disk_inlet.")
+ elif marker.endswith(ACTUATOR_DISK_OUTLET_SUFFIX):
+ mesh_markers["actuator_disk_outlet"].append(marker)
+ log.info(f"'{marker}' marker has been marked as actuator_disk_outlet.")
+ else:
+ # In SU2 wings and fuselages are marked as wall.
+ mesh_markers["wall"].append(marker)
+ log.info(f"'{marker}' marker has been marked as wall.")
+
+ # Check if markers were found
+ if not any(mesh_markers.values()):
+ raise ValueError("No mesh markers found!")
+
+ # Add none to False values in mesh_markers
+ for key, value in mesh_markers.items():
+ if not value:
+ mesh_markers[key] = ["None"]
+
+ return mesh_markers
+
+
+def check_control_surface(input_string: str) -> str:
+ if "aileron" in input_string:
+ return "aileron"
+ elif "rudder" in input_string:
+ return "rudder"
+ elif "elevator" in input_string:
+ return "elevator"
+ else:
+ return "no_deformation"
+
+
+def retrieve_su2_mesh(
+ su2_mesh_list: List[Tuple[bytes, str, str, float]],
+ aircraft_name: str,
+ angle: float,
+ deformation_list: List[str],
+) -> List[Tuple[bytes, str, str, float]]:
+ """
+ Connect to ceasiompy.db and retrieve the first .su2 mesh for:
+ - Specific aircraft name
+ - Specific deformation from the deformation_list
+ - Specific angle of deformation
+
+ Append the result to the su2_mesh_list.
+ """
+ db = CeasiompyDb()
+
+ for deformation in deformation_list:
+ # Query to retrieve the last su2_data value
+ query = """
+ SELECT su2_file_data
+ FROM gmsh_data
+ WHERE aircraft = ? AND deformation = ? AND angle = ?
+ ORDER BY timestamp DESC
+ LIMIT 1
+ """
+ db.cursor.execute(query, (aircraft_name, deformation, angle))
+
+ su2_mesh: bytes = db.cursor.fetchone()
+ log.info(
+ f"Loading .su2 file for aircraft {aircraft_name}, "
+ f"deformation {deformation} of angle {angle} [deg]."
+ )
+ su2_mesh_list.append(
+ (su2_mesh[0], aircraft_name, deformation, angle)
+ )
+
+ db.close()
+
+
+def get_surface_pitching_omega(oscillation_type: str, omega: float) -> str:
+ """
+ Returns how the deforming surface will move around origin point,
+ in terms on what oscillation we choose.
+
+ Args:
+ oscillation_type (str): Either 'alpha' or 'beta'.
+ omega (float): Angular frequency.
+
+ Raises:
+ ValueError: Checks correct format for oscillation_type.
+
+ Returns:
+ str: Either '0.0 omega 0.0 ' or '0.0 0.0 omega '.
+
+ """
+ if oscillation_type == "alpha":
+ return f"0.0 {omega} 0.0 "
+ elif oscillation_type == "beta":
+ return f"0.0 0.0 {omega} "
+ else:
+ raise ValueError("Invalid oscillation_type in get_surface_pitching_omega.")
+
+
+def su2_mesh_list_from_db(
+ tixi: Tixi3
+) -> List[Tuple[bytes, str, str, float]]:
+
+ aircraft_name = get_value(tixi, USED_SU2_MESH_XPATH + "list")
+ su2_mesh_list = []
+
+ # No control surfaces
+ if not get_value(tixi, SU2_CONTROL_SURF_BOOL_XPATH):
+ retrieve_su2_mesh(su2_mesh_list, aircraft_name, 0.0, ["no_deformation"])
+
+ else:
+ angles = str(get_value(tixi, SU2_CONTROL_SURF_ANGLE_XPATH))
+ angles_list = list(set([float(angle) for angle in angles.split(';')]))
+
+ for angle in angles_list:
+ if angle != 0.0:
+ retrieve_su2_mesh(su2_mesh_list, aircraft_name, angle, CONTROL_SURFACE_LIST)
+ else:
+ retrieve_su2_mesh(su2_mesh_list, aircraft_name, 0.0, ["no_deformation"])
+
+ return su2_mesh_list
+
+
+def get_su2_cfg_tpl(tpl_type: str) -> Path:
+ """
+ Get path of the SU2 config template of template_type
+ corresponding to the correct SU2 version.
+
+ Args:
+ template_type (str): Either "euler" or "rans".
+
+ Returns:
+ su2_cfg_tpe_euler_path (str): Path of the SU2 config template.
+
+ """
+
+ if tpl_type not in TEMPLATE_TYPE:
+ log.warning(
+ "template_type (str) should be either "
+ "'EULER' or 'RANS' in get_su2_config_template."
+ )
+
+ tpl_type = tpl_type.lower()
+
+ return Path(MODULE_DIR, "files", f"cfg_tpl_{tpl_type}.cfg")
+
+
+def get_su2_aerocoefs(
+ force_file: Path
+) -> Tuple[float, float, float, float, float, float, float]:
+ """
+ Get aerodynamic coefficients and velocity from the force_file.
+
+ Note:
+ force_file should be: forces_breakdown.dat
+
+ Args:
+ force_file (Path): Path to the SU2 forces file.
+
+ Returns:
+ cl, cd, cs, cmd, cms, cml, velocity: Aerodynamic coefficients and velocity.
+
+ """
+
+ validate_file(force_file, "SU2 force")
+
+ # In case they are not found in force_file
+ results = {key: None for key in AERO_COEFFICIENTS.values()}
+
+ # Call access_coef for the correct coefficient
+ def parse_line(line: str) -> None:
+ for key, var_name in AERO_COEFFICIENTS.items():
+ if key in line:
+ if (key == "Free-stream velocity") and ("m/s" in line):
+ results[var_name] = float(line.split(" ")[7])
+ elif (not key == "Free-stream velocity"):
+ results[var_name] = access_coef(line)
+
+ # Parse each line in the file
+ with open(force_file) as f:
+ for line in f.readlines():
+ parse_line(line)
+
+ return (
+ results["cl"], results["cd"], results["cs"],
+ results["cmd"], results["cms"], results["cml"],
+ results["velocity"],
+ )
+
+
+def get_su2_forces_moments(
+ force_file: Path
+) -> Tuple[float, float, float, float, float, float, float]:
+ """
+ Get aerodynamic forces and moments from the force_file.
+
+ Note:
+ force_file should be: forces_breakdown.dat
+
+ Args:
+ force_file (Path): Path to the SU2 forces file.
+
+ Returns:
+ cl, cd, cs, cmd, cms, cml: Aerodynamic coefficients and velocity.
+
+ """
+
+ validate_file(force_file, "SU2 force")
+
+ # In case they are not found in force_file
+ results = {key: None for key in SU2_FORCES_MOM.values()}
+
+ # Call access_coef for the correct coefficient
+ def parse_line(line: str):
+ for key, var_name in SU2_FORCES_MOM.items():
+ if key in line:
+ results[var_name] = access_coef(line)
+
+ # Parse each line in the file
+ with open(force_file) as f:
+ for line in f.readlines():
+ parse_line(line)
+
+ return (
+ results["cfx"], results["cfy"], results["cfz"],
+ results["cmx"], results["cmy"], results["cmz"],
+ )
+
+
+def get_efficiency_and_aoa(force_file: Path) -> Tuple[float, float]:
+ """
+ Get efficiency (CL/CD) and Angle of Attack (AoA) in the force_file.
+ """
+
+ validate_file(force_file, "SU2 force")
+ cl_cd, aoa = None, None
+
+ with open(force_file) as f:
+ for line in f.readlines():
+ if "CL/CD" in line:
+ cl_cd = access_coef(line)
+ continue
+
+ if "Angle of attack (AoA):" in line:
+ aoa = float(line.split("Angle of attack (AoA):")[1].split("deg,")[0].strip())
+ continue
+
+ if cl_cd and aoa:
+ log.info(f"CL/CD ratio has been found and is equal to: {cl_cd}.")
+ log.info(f"AoA has been found and is equal to: {aoa}.")
+ return cl_cd, aoa
+
+ if cl_cd is None:
+ raise ValueError(f"No value has been found for the CL/CD ratio in {force_file}.")
+ else:
+ raise ValueError(f"No value has been found for the AoA in {force_file}")
+
+
+def get_wetted_area(su2_logfile: Path) -> float:
+ """
+ Get SU2 logfile and returns the wetted area value
+ previously calculated by SU2.
+ """
+
+ validate_file(su2_logfile, "SU2 log")
+
+ with open(su2_logfile) as f:
+ for line in f.readlines():
+ if "Wetted area =" not in line:
+ continue
+
+ wetted_area = float(line.split(" ")[3])
+ log.info(f"Wetted area value has been found : {wetted_area} [m^2].")
+ return wetted_area
+
+ log.warning("No value has been found for the wetted area, returning 0 [m^2].")
+
+ return 0
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/SU2Run/su2run.py b/ceasiompy/SU2Run/su2run.py
index ea8499714..987e996e3 100644
--- a/ceasiompy/SU2Run/su2run.py
+++ b/ceasiompy/SU2Run/su2run.py
@@ -5,17 +5,14 @@
Module to run SU2 Calculation in CEASIOMpy
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2018-11-06
+| Modified: Leon Deligny
+| Date: 24-Feb-2025
TODO:
-
- * Create test functions
- * complete input/output in __specs__
- * Check platform with-> sys.platform
- * Move run_SU2_fsi to /SU2Run/func/su2fsi.py
+ * Check platform with -> sys.platform
"""
@@ -23,113 +20,99 @@
# IMPORTS
# =================================================================================================
-from pathlib import Path
-
-from ceasiompy.SU2Run.func.su2config import generate_su2_cfd_config
-from ceasiompy.SU2Run.func.su2config_rans import generate_su2_cfd_config_rans
-from ceasiompy.SU2Run.func.su2results import get_su2_results
-from ceasiompy.utils.ceasiomlogger import get_logger
+from cpacspy.cpacsfunctions import get_value
+from ceasiompy.SU2Run.func.results import get_su2_results
+from ceasiompy.SU2Run.func.runconfigfiles import run_SU2_multi
from ceasiompy.utils.ceasiompyutils import (
- get_reasonable_nb_cpu,
- get_results_directory,
- run_software,
+ bool_,
+ call_main,
+)
+from ceasiompy.SU2Run.func.config import (
+ define_markers,
+ load_su2_mesh_paths,
+ generate_su2_cfd_config,
)
-from ceasiompy.utils.commonnames import CONFIG_CFD_NAME, SU2_FORCES_BREAKDOWN_NAME
-from ceasiompy.utils.commonxpath import SU2_NB_CPU_XPATH, SU2_CONFIG_RANS_XPATH
-from ceasiompy.utils.moduleinterfaces import get_toolinput_file_path, get_tooloutput_file_path
-from cpacspy.cpacsfunctions import get_value_or_default, open_tixi
-
-log = get_logger()
-
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
-# =================================================================================================
-# CLASSES
-# =================================================================================================
+from pathlib import Path
+from cpacspy.cpacspy import CPACS
+
+from ceasiompy import log
+from ceasiompy.SU2Run import MODULE_NAME
+from ceasiompy.utils.commonxpath import (
+ SU2_NB_CPU_XPATH,
+ SU2_CONFIG_RANS_XPATH,
+ SU2_DYNAMICDERIVATIVES_BOOL_XPATH,
+)
# =================================================================================================
-# FUNCTIONS
+# MAIN
# =================================================================================================
-def run_SU2_multi(wkdir, nb_proc=1):
- """Function to run a multiple SU2 calculation.
-
- Function 'run_SU2_multi' will run in the given working directory SU2 calculations. The working
- directory must have a folder structure created by 'SU2Config' module.
-
- Args:
- wkdir (Path): Path to the working directory
- nb_proc (int): Number of processor that should be used to run the calculation in parallel
+def main(cpacs: CPACS, results_dir: Path) -> None:
"""
+ SU2Run module is decomposed into 4 parts.
- if not wkdir.exists():
- raise OSError(f"The working directory : {wkdir} does not exit!")
-
- case_dir_list = [dir for dir in wkdir.iterdir() if "Case" in dir.name]
- if not case_dir_list:
- raise OSError(f"No Case directory has been found in the working directory: {wkdir}")
-
- for config_dir in sorted(case_dir_list):
- config_cfd = [c for c in config_dir.iterdir() if c.name == CONFIG_CFD_NAME]
+ 1. Retrieve the correct geometry: .su2 mesh files.
+ 2. For each .su2 file create a .cfg configuration file.
+ 3. Run each .cfg file in SU2_CFD.
+ 4. Retrieve force files results in configuration directory.
- if not config_cfd:
- raise ValueError(f"No '{CONFIG_CFD_NAME}' file has been found in this directory!")
+ Args:
+ cpacs (CPACS): Input CPACS file.
+ wkdir (Path): SU2Run Results directory.
- if len(config_cfd) > 1:
- raise ValueError(f"More than one '{CONFIG_CFD_NAME}' file in this directory!")
+ """
- run_software(
- software_name="SU2_CFD",
- arguments=[config_cfd[0]],
- wkdir=config_dir,
- with_mpi=True,
- nb_cpu=nb_proc,
+ # Define variable
+ tixi = cpacs.tixi
+
+ # Define constants
+ nb_proc = int(get_value(tixi, SU2_NB_CPU_XPATH))
+ config_file_type = str(get_value(tixi, SU2_CONFIG_RANS_XPATH))
+ rans: bool = (config_file_type == "RANS")
+
+ # 1. Load .su2 mesh files
+ su2_mesh_paths, dynstab_su2_mesh_paths = load_su2_mesh_paths(tixi, results_dir)
+
+ # Load only 1 mesh file for the su2 markers
+ # Accross all different meshes for the same aircraft,
+ # the markers will remain the same.
+ mesh_markers = define_markers(tixi, su2_mesh_paths[0])
+
+ # 2. Create configuration files
+ if bool_(get_value(tixi, SU2_DYNAMICDERIVATIVES_BOOL_XPATH)):
+ log.info("----- Generating Dynamic Stability ConfigFile -----")
+
+ generate_su2_cfd_config(
+ cpacs=cpacs,
+ wkdir=results_dir,
+ su2_mesh_paths=dynstab_su2_mesh_paths,
+ mesh_markers=mesh_markers,
+ dyn_stab=True,
+ rans=rans,
)
- forces_breakdown_file = Path(config_dir, SU2_FORCES_BREAKDOWN_NAME)
- if not forces_breakdown_file.exists():
- raise ValueError(
- "The SU2_CFD calculation has not ended correctly,"
- f"{SU2_FORCES_BREAKDOWN_NAME} is missing!"
- )
-
-
-# =================================================================================================
-# MAIN
-# =================================================================================================
-
-
-def main(cpacs_path, cpacs_out_path):
- log.info("----- Start of " + MODULE_NAME + " -----")
-
- tixi = open_tixi(cpacs_path)
- nb_proc = get_value_or_default(tixi, SU2_NB_CPU_XPATH, get_reasonable_nb_cpu())
-
- results_dir = get_results_directory("SU2Run")
-
- # Temporary CPACS to be stored after "generate_su2_cfd_config"
- cpacs_tmp_cfg = Path(cpacs_out_path.parent, "ConfigTMP.xml")
-
- config_file_type = get_value_or_default(tixi, SU2_CONFIG_RANS_XPATH, "Euler")
-
- if config_file_type == "RANS":
- log.info("RANS simulation")
- generate_su2_cfd_config_rans(cpacs_path, cpacs_tmp_cfg, results_dir)
- else:
- log.info("Euler simulation")
- generate_su2_cfd_config(cpacs_path, cpacs_tmp_cfg, results_dir)
-
+ log.info(f"----- Generating {config_file_type} ConfigFile -----")
+ generate_su2_cfd_config(
+ cpacs=cpacs,
+ wkdir=results_dir,
+ su2_mesh_paths=su2_mesh_paths,
+ mesh_markers=mesh_markers,
+ dyn_stab=False,
+ rans=rans,
+ )
+
+ # 3. Run each configuration file in SU2
+ log.info(f"----- Running {config_file_type} simulations -----")
run_SU2_multi(results_dir, nb_proc)
- get_su2_results(cpacs_tmp_cfg, cpacs_out_path, results_dir)
- log.info("----- End of " + MODULE_NAME + " -----")
+ # 4. Retrieve SU2 results
+ log.info("----- Updating CPACS and accessing results -----")
+ get_su2_results(cpacs, results_dir)
if __name__ == "__main__":
- cpacs_path = get_toolinput_file_path(MODULE_NAME)
- cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
-
- main(cpacs_path, cpacs_out_path)
+ # TODO : Specify an option to give as input a SU2 file path
+ call_main(main, MODULE_NAME)
diff --git a/ceasiompy/SU2Run/tests/tests_su2_results/test_su2results.py b/ceasiompy/SU2Run/tests/tests_su2_results/test_su2results.py
index da9e572d5..51fb160aa 100644
--- a/ceasiompy/SU2Run/tests/tests_su2_results/test_su2results.py
+++ b/ceasiompy/SU2Run/tests/tests_su2_results/test_su2results.py
@@ -3,13 +3,13 @@
Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-Test functions of 'ceasiompy/SU2Run/func/su2config.py'
-
-Python version: >=3.8
+Test for get_su2_results function.
| Author : Aidan Jungo
| Creation: 2022-04-07
+| Modified: Leon Deligny
+| Date: 21 March 2025
"""
@@ -18,43 +18,38 @@
# =================================================================================================
import os
-from pathlib import Path
-
import pytest
-from ceasiompy.SU2Run.func.su2results import save_screenshot
-from ceasiompy.utils.commonpaths import TEST_RESULTS_FILES_PATH
-
-MODULE_DIR = Path(__file__).parent
+import unittest
+from ceasiompy.SU2Run.func.results import save_screenshot
-# =================================================================================================
-# CLASSES
-# =================================================================================================
+from pathlib import Path
+from ceasiompy.utils.ceasiompytest import CeasiompyTest
+from ceasiompy.utils.commonpaths import TEST_RESULTS_FILES_PATH
# =================================================================================================
# FUNCTIONS
# =================================================================================================
-@pytest.mark.skipif("DISPLAY" not in os.environ, reason="requires display")
-def test_save_screenshot():
- """Test the function 'save_screenshot'"""
+class TestSU2Results(CeasiompyTest):
- surface_flow_file = Path(TEST_RESULTS_FILES_PATH, "surface_flow.vtu")
- screenshot_file = save_screenshot(surface_flow_file, "Mach")
- assert screenshot_file.exists()
+ @pytest.mark.skipif("DISPLAY" not in os.environ, reason="requires display")
+ def test_save_screenshot(self):
+ """Test the function 'save_screenshot'"""
- if screenshot_file.exists():
- screenshot_file.unlink()
+ surface_flow_file = Path(TEST_RESULTS_FILES_PATH, "surface_flow.vtu")
+ screenshot_file = save_screenshot(surface_flow_file, "Mach")
+ assert screenshot_file.exists()
+ if screenshot_file.exists():
+ screenshot_file.unlink()
# =================================================================================================
# MAIN
# =================================================================================================
-if __name__ == "__main__":
- print("Test configfile.py")
- print("To run test use the following command:")
- print(">> pytest -v")
+if __name__ == "__main__":
+ unittest.main(verbosity=0)
diff --git a/ceasiompy/SU2Run/tests/tests_su2actuatordisk/test_su2actuatordisk.py b/ceasiompy/SU2Run/tests/tests_su2actuatordisk/test_su2actuatordisk.py
index 549754bcf..bbf91d737 100644
--- a/ceasiompy/SU2Run/tests/tests_su2actuatordisk/test_su2actuatordisk.py
+++ b/ceasiompy/SU2Run/tests/tests_su2actuatordisk/test_su2actuatordisk.py
@@ -5,7 +5,6 @@
Test functions of 'ceasiompy/SU2Run/func/su2actuatordisk.py'
-Python version: >=3.8
| Author : Aidan Jungo and Giacomo Benedetti
| Creation: 2022-12-05
@@ -22,17 +21,18 @@
import numpy as np
import pytest
from ambiance import Atmosphere
-from ceasiompy.SU2Run.func.su2actuatordiskfile import (
+from ceasiompy.SU2Run.func.actuatordiskfile import (
axial_interference_function,
calculate_radial_thrust_coefs,
check_input_output_values,
get_advanced_ratio,
get_prandtl_correction_values,
get_radial_stations,
- save_plots,
thrust_calculator,
write_actuator_disk_data,
)
+from ceasiompy.SU2Run.func.plot import save_plots
+
from ceasiompy.utils.ceasiompyutils import get_results_directory
MODULE_DIR = Path(__file__).parent
@@ -322,3 +322,4 @@ def test_write_actuator_disk_data(tmp_path):
print("Test configfile.py")
print("To run test use the following command:")
print(">> pytest -v")
+ test_get_radial_stations()
diff --git a/ceasiompy/SU2Run/tests/tests_su2config/test_su2config.py b/ceasiompy/SU2Run/tests/tests_su2config/test_su2config.py
index e7b968f39..82e3109c2 100644
--- a/ceasiompy/SU2Run/tests/tests_su2config/test_su2config.py
+++ b/ceasiompy/SU2Run/tests/tests_su2config/test_su2config.py
@@ -5,8 +5,6 @@
Test functions of 'ceasiompy/SU2Run/func/su2config.py'
-Python version: >=3.8
-
| Author : Aidan Jungo
| Creation: 2022-04-07
@@ -20,16 +18,11 @@
from pathlib import Path
import pytest
-from ceasiompy.SU2Run.func.su2config import add_damping_derivatives
+from ceasiompy.SU2Run.func.config import add_damping_derivatives
from ceasiompy.utils.configfiles import ConfigFile
MODULE_DIR = Path(__file__).parent
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-
# =================================================================================================
# FUNCTIONS
# =================================================================================================
@@ -41,11 +34,12 @@ def test_add_damping_derivatives(tmp_path):
cfg = ConfigFile()
case_dir_name = "test_damping_der_cfg"
- add_damping_derivatives(cfg, tmp_path, case_dir_name, 5.4)
+ rotation_rate = 5.4
+ add_damping_derivatives(cfg, tmp_path, case_dir_name, rotation_rate)
- case_dp = Path(tmp_path, f"{case_dir_name}_dp")
- case_dq = Path(tmp_path, f"{case_dir_name}_dq")
- case_dr = Path(tmp_path, f"{case_dir_name}_dr")
+ case_dp = Path(tmp_path, f"{case_dir_name}_p_{rotation_rate}")
+ case_dq = Path(tmp_path, f"{case_dir_name}_q_{rotation_rate}")
+ case_dr = Path(tmp_path, f"{case_dir_name}_r_{rotation_rate}")
assert case_dp.exists()
assert case_dq.exists()
@@ -57,18 +51,16 @@ def test_add_damping_derivatives(tmp_path):
@pytest.mark.skip(reason="Not implemented yet")
-def test_add_actuator_disk(tmp_path):
+def test_add_actuator_disk():
"""Test function 'add_actuator_disk'"""
-
- pass
-
+ ...
# =================================================================================================
# MAIN
# =================================================================================================
-if __name__ == "__main__":
- print("Test configfile.py")
+if __name__ == "__main__":
+ print("Test test_su2configfile.py")
print("To run test use the following command:")
print(">> pytest -v")
diff --git a/ceasiompy/SU2Run/tests/tests_su2utils/forces_breakdown.dat b/ceasiompy/SU2Run/tests/tests_su2utils/forces_breakdown.dat
index 73a325926..d8a44bd10 100644
--- a/ceasiompy/SU2Run/tests/tests_su2utils/forces_breakdown.dat
+++ b/ceasiompy/SU2Run/tests/tests_su2utils/forces_breakdown.dat
@@ -41,10 +41,10 @@ Inviscid flow: Computing density based on free-stream
temperature and pressure using the ideal gas law.
Force coefficients computed using free-stream values.
-- Input conditions:
-Fluid Model: STANDARD_AIR
+Fluid Model: STANDARD_AIR
Specific gas constant: 287.058 N.m/kg.K.
Specific gas constant (non-dim): 7.93651
-Specific Heat Ratio: 1.4000
+Specific Heat Ratio: 1.4000
Free-stream static pressure: 101325 Pa.
Free-stream total pressure: 107853 Pa.
Free-stream temperature: 288.15 K.
diff --git a/ceasiompy/SU2Run/tests/tests_su2utils/logfile_SU2_CFD.log b/ceasiompy/SU2Run/tests/tests_su2utils/logfile_SU2_CFD.log
index 61c7a00b6..81d8832d0 100644
--- a/ceasiompy/SU2Run/tests/tests_su2utils/logfile_SU2_CFD.log
+++ b/ceasiompy/SU2Run/tests/tests_su2utils/logfile_SU2_CFD.log
@@ -38,8 +38,8 @@ Non-Dimensional simulation (V=1.0, Rho=1.0, T=1.0 at the farfield).
The reference area is 122.4 m^2.
The semi-span will be computed using the max y(3D) value.
The reference length is 4.193 m.
-Surface(s) where the force coefficients are evaluated and
-their reference origin for moment computation:
+Surface(s) where the force coefficients are evaluated and
+their reference origin for moment computation:
- Fuselage1 (0, 0, 0).
- Wing3V (0, 0, 0).
- Wing2H (0, 0, 0).
@@ -122,8 +122,8 @@ Distributing ParMETIS coloring.
Rebalancing vertices.
Rebalancing volume element connectivity.
Rebalancing markers and surface elements.
-145547 vertices including ghost points.
-771469 interior elements including halo cells.
+145547 vertices including ghost points.
+771469 interior elements including halo cells.
771469 tetrahedra.
Establishing MPI communication patterns.
Setting point connectivity.
diff --git a/ceasiompy/SU2Run/tests/tests_su2utils/logfile_SU2_CFD_no_wetted_area.log b/ceasiompy/SU2Run/tests/tests_su2utils/logfile_SU2_CFD_no_wetted_area.log
index 04cd50420..10f659e5d 100644
--- a/ceasiompy/SU2Run/tests/tests_su2utils/logfile_SU2_CFD_no_wetted_area.log
+++ b/ceasiompy/SU2Run/tests/tests_su2utils/logfile_SU2_CFD_no_wetted_area.log
@@ -38,8 +38,8 @@ Non-Dimensional simulation (V=1.0, Rho=1.0, T=1.0 at the farfield).
The reference area is 122.4 m^2.
The semi-span will be computed using the max y(3D) value.
The reference length is 4.193 m.
-Surface(s) where the force coefficients are evaluated and
-their reference origin for moment computation:
+Surface(s) where the force coefficients are evaluated and
+their reference origin for moment computation:
- Fuselage1 (0, 0, 0).
- Wing3V (0, 0, 0).
- Wing2H (0, 0, 0).
diff --git a/ceasiompy/SU2Run/tests/tests_su2utils/test_su2utils.py b/ceasiompy/SU2Run/tests/tests_su2utils/test_su2utils.py
index 648fe8eac..b0866df63 100644
--- a/ceasiompy/SU2Run/tests/tests_su2utils/test_su2utils.py
+++ b/ceasiompy/SU2Run/tests/tests_su2utils/test_su2utils.py
@@ -5,7 +5,6 @@
Test functions for 'ceasiompy/SU2Run/func/meshutils.py'
-Python version: >=3.8
| Author : Aidan Jungo
@@ -18,18 +17,15 @@
# =================================================================================================
from pathlib import Path
-from unittest.mock import mock_open, patch
+from ceasiompy import log
import pytest
-from ceasiompy.SU2Run.func.su2utils import (
+from ceasiompy.SU2Run.func.utils import (
get_efficiency_and_aoa,
get_mesh_markers,
get_su2_aerocoefs,
- get_su2_config_template,
- get_su2_version,
get_wetted_area,
)
-from ceasiompy.utils.moduleinterfaces import get_module_path
from pytest import approx
MODULE_DIR = Path(__file__).parent
@@ -38,10 +34,6 @@
SU2_LOGFILE = Path(MODULE_DIR, "logfile_SU2_CFD.log")
SU2_LOGFILE_NO_WETTED_AREA = Path(MODULE_DIR, "logfile_SU2_CFD_no_wetted_area.log")
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
# =================================================================================================
# FUNCTIONS
@@ -79,64 +71,6 @@ def test_get_mesh_marker():
assert mesh_markers["symmetry"] == ["None"]
-def test_get_su2_version():
- """Test function 'get_su2_version'"""
-
- mock_text = (
- r"## \file SU2_CFD.py\n"
- r"# \brief Python script to launch SU2_CFD through the Python Wrapper.\n"
- r"# \author David Thomas\n"
- r'# \version 9.9.9 "Blackbird"\n'
- r"#"
- r"# SU2 Project Website: https://su2code.github.io\n"
- )
-
- mock_data = mock_open(read_data=mock_text)
-
- # TODO: When Python 3.10 will be used, with could use one "with" with parentheses
- with patch(
- "ceasiompy.SU2Run.func.su2utils.get_install_path",
- return_value=Path("/mockpath/bin/SU2_CFD"),
- ):
- with patch.object(Path, "exists", return_value=True):
- with patch("builtins.open", mock_data):
- assert get_su2_version() == "9.9.9"
-
- mock_text_no_version = (
- r"## \file SU2_CFD.py\n"
- r"# \brief Python script to launch SU2_CFD through the Python Wrapper.\n"
- )
-
- mock_data_no_version = mock_open(read_data=mock_text_no_version)
-
- # TODO: When Python 3.10 will be used, with could use one "with" with parentheses
- with patch(
- "ceasiompy.SU2Run.func.su2utils.get_install_path",
- return_value=Path("/mockpath/bin/SU2_CFD"),
- ):
- with patch.object(Path, "exists", return_value=True):
- with patch("builtins.open", mock_data_no_version):
- assert get_su2_version() is None
-
-
-def test_get_su2_config_template():
- """Test function 'get_su2_config_template'"""
-
- su2_dir = get_module_path("SU2Run")
-
- # Test with an old version of the config template
- config_template_path = Path(su2_dir, "files", "config_template_euler.cfg")
-
- # Remove the config template file if it exists (from a previous test)
- if config_template_path.exists():
- assert get_su2_config_template() == config_template_path
-
- # # Test with an inexistent config template version
- # with patch("ceasiompy.SU2Run.func.su2utils.get_su2_version", return_value="9.9.99"):
- # with pytest.raises(FileNotFoundError):
- # assert get_su2_config_template() == config_template_path
-
-
def test_get_su2_aerocoefs():
"""Test function 'get_su2_aerocoefs'"""
@@ -183,4 +117,4 @@ def test_get_wetted_area():
# =================================================================================================
if __name__ == "__main__":
- print("Nothing to execute!")
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/SUMOAutoMesh/ToolInput/.directory b/ceasiompy/SUMOAutoMesh/ToolInput/.directory
deleted file mode 100644
index 2bf4b2b28..000000000
--- a/ceasiompy/SUMOAutoMesh/ToolInput/.directory
+++ /dev/null
@@ -1,6 +0,0 @@
-[Dolphin]
-Timestamp=2018,10,30,15,7,19
-Version=3
-
-[Settings]
-HiddenFilesShown=true
diff --git a/ceasiompy/SUMOAutoMesh/ToolInput/.keep b/ceasiompy/SUMOAutoMesh/ToolInput/.keep
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/ceasiompy/SUMOAutoMesh/ToolInput/.keep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ceasiompy/SUMOAutoMesh/ToolOutput/.directory b/ceasiompy/SUMOAutoMesh/ToolOutput/.directory
deleted file mode 100644
index 023a7b11d..000000000
--- a/ceasiompy/SUMOAutoMesh/ToolOutput/.directory
+++ /dev/null
@@ -1,6 +0,0 @@
-[Dolphin]
-Timestamp=2018,10,30,15,7,27
-Version=3
-
-[Settings]
-HiddenFilesShown=true
diff --git a/ceasiompy/SUMOAutoMesh/ToolOutput/.keep b/ceasiompy/SUMOAutoMesh/ToolOutput/.keep
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/ceasiompy/SUMOAutoMesh/ToolOutput/.keep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ceasiompy/SUMOAutoMesh/__init__.py b/ceasiompy/SUMOAutoMesh/__init__.py
index e69de29bb..6eac71886 100644
--- a/ceasiompy/SUMOAutoMesh/__init__.py
+++ b/ceasiompy/SUMOAutoMesh/__init__.py
@@ -0,0 +1,35 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for SUMOAutoMesh module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = True
+
+# ===== Include GUI =====
+include_gui = True
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+# ===== Add a Results Directory =====
+RES_DIR = True
diff --git a/ceasiompy/SUMOAutoMesh/__specs__.py b/ceasiompy/SUMOAutoMesh/__specs__.py
index 407eab0d2..2804ab90c 100644
--- a/ceasiompy/SUMOAutoMesh/__specs__.py
+++ b/ceasiompy/SUMOAutoMesh/__specs__.py
@@ -1,17 +1,11 @@
-from pathlib import Path
-from ceasiompy.utils.commonxpath import SU2MESH_XPATH, SUMO_REFINE_LEVEL_XPATH, SUMOFILE_XPATH
+from ceasiompy.utils.commonxpath import (
+ SU2MESH_XPATH,
+ SUMO_REFINE_LEVEL_XPATH,
+ SPECIFIED_SUMOFILE_XPATH,
+)
from ceasiompy.utils.moduleinterfaces import CPACSInOut
-# ===== Module Status =====
-# True if the module is active
-# False if the module is disabled (not working or not ready)
-module_status = True
-
-# ===== Results directory path =====
-
-RESULTS_DIR = Path("Results", "SUMO")
-
# ===== CPACS inputs and outputs =====
@@ -25,7 +19,7 @@
default_value="-",
unit="1",
descr="Absolute path to the SUMO file",
- xpath=SUMOFILE_XPATH,
+ xpath=SPECIFIED_SUMOFILE_XPATH,
gui=True,
gui_name="SUMO File path",
gui_group="Inputs",
diff --git a/ceasiompy/SUMOAutoMesh/doc/sumo_help b/ceasiompy/SUMOAutoMesh/doc/sumo_help
index 5ae77b88c..2ebd24f11 100644
--- a/ceasiompy/SUMOAutoMesh/doc/sumo_help
+++ b/ceasiompy/SUMOAutoMesh/doc/sumo_help
@@ -1,29 +1,29 @@
-
+
sumo --help
Surface modeling tool and mesh generator for aircraft configurations.
Usage: dwfsumo -batch [options] aircraft.smx
Options:
--output=iges,dwfs,edge,cgns,tau,su2
- Generate output files for the
+-output=iges,dwfs,edge,cgns,tau,su2
+ Generate output files for the
named formats. Will not start mesh generation unless at least
one mesh format (dwfs,edge,tau,cgns,su2) is named.
Default is -output=iges,dwfs,edge
Note: This version of sumo was compiled *without* support for NetCDF;
therefore, the TAU mesh format is *not* supported.
--tetgen-options=flags
- Call tetgen as in 'tetgen -flags model.smesh' when generating
+-tetgen-options=flags
+ Call tetgen as in 'tetgen -flags model.smesh' when generating
a volume mesh.The default is -tetgen-options=pq1.4V
Examples:
dwfsumo -batch -output=iges aircraft.smx
- Convert geometry of aircraft.smx to IGES and exit. Will not
+ Convert geometry of aircraft.smx to IGES and exit. Will not
generate any mesh.
dwfsumo -batch -output=cgns,edge -tetgen-options=pq1.16VY aircraft.smx
First, generate a surface mesh (not written) for aircraft.smx,
then produce a volume mesh by calling
tetgen -pq1.16VY aircraft.smesh
on it, then convert the tetgen output to CGNS and EDGE files.
-
+
On linux the command could be "sumo" instead of "dwfsumo"
diff --git a/ceasiompy/SUMOAutoMesh/doc/tetgen_help b/ceasiompy/SUMOAutoMesh/doc/tetgen_help
index e2b93fe05..c4cc8aee8 100644
--- a/ceasiompy/SUMOAutoMesh/doc/tetgen_help
+++ b/ceasiompy/SUMOAutoMesh/doc/tetgen_help
@@ -11,30 +11,30 @@ What Can TetGen Do?
Delaunay tetrahedralizations, and quality tetrahedral meshes.
Command Line Syntax:
-
- Below is the basic command line syntax of TetGen with a list of short
- descriptions. Underscores indicate that numbers may optionally
- follow certain switches. Do not leave any space between a switch
- and its numeric parameter. 'input_file' contains input data
- depending on the switches you supplied which may be a piecewise
- linear complex or a list of nodes. File formats and detailed
- description of command line switches are found in user's manual.
-
- tetgen [-pYrq_Aa_miO_S_T_XMwcdzfenvgkJBNEFICQVh] input_file
- -p Tetrahedralizes a piecewise linear complex (PLC).
- -Y Preserves the input surface mesh (does not modify it).
- -r Reconstructs a previously generated mesh.
- -q Refines mesh (to improve mesh quality).
- -R Mesh coarsening (to reduce the mesh elements).
- -A Assigns attributes to tetrahedra in different regions.
- -a Applies a maximum tetrahedron volume constraint.
- -m Applies a mesh sizing function.
- -i Inserts a list of additional points.
- -O Specifies the level of mesh optimization.
- -S Specifies maximum number of added points.
- -T Sets a tolerance for coplanar test (default 1e-8).
- -X Suppresses use of exact arithmetic.
- -M No merge of coplanar facets or very close vertices.
+
+ Below is the basic command line syntax of TetGen with a list of short
+ descriptions. Underscores indicate that numbers may optionally
+ follow certain switches. Do not leave any space between a switch
+ and its numeric parameter. 'input_file' contains input data
+ depending on the switches you supplied which may be a piecewise
+ linear complex or a list of nodes. File formats and detailed
+ description of command line switches are found in user's manual.
+
+ tetgen [-pYrq_Aa_miO_S_T_XMwcdzfenvgkJBNEFICQVh] input_file
+ -p Tetrahedralizes a piecewise linear complex (PLC).
+ -Y Preserves the input surface mesh (does not modify it).
+ -r Reconstructs a previously generated mesh.
+ -q Refines mesh (to improve mesh quality).
+ -R Mesh coarsening (to reduce the mesh elements).
+ -A Assigns attributes to tetrahedra in different regions.
+ -a Applies a maximum tetrahedron volume constraint.
+ -m Applies a mesh sizing function.
+ -i Inserts a list of additional points.
+ -O Specifies the level of mesh optimization.
+ -S Specifies maximum number of added points.
+ -T Sets a tolerance for coplanar test (default 1e-8).
+ -X Suppresses use of exact arithmetic.
+ -M No merge of coplanar facets or very close vertices.
-w Generates weighted Delaunay (regular) triangulation.
-c Retains the convex hull of the PLC.
-d Detects self-intersections of facets of the PLC.
diff --git a/ceasiompy/SUMOAutoMesh/sumoautomesh.py b/ceasiompy/SUMOAutoMesh/sumoautomesh.py
index 4e87280d3..59a0c208c 100644
--- a/ceasiompy/SUMOAutoMesh/sumoautomesh.py
+++ b/ceasiompy/SUMOAutoMesh/sumoautomesh.py
@@ -5,7 +5,6 @@
Module to create a simple SU2 mesh from SUMO file (.smx)
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2018-10-29
@@ -22,22 +21,40 @@
# IMPORTS
# =================================================================================================
+import os
import math
-import platform
import shutil
-from pathlib import Path
+import platform
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.ceasiompyutils import aircraft_name, get_results_directory, run_software
-from ceasiompy.utils.commonxpath import SU2MESH_XPATH, SUMO_REFINE_LEVEL_XPATH, SUMOFILE_XPATH
-from ceasiompy.utils.moduleinterfaces import get_toolinput_file_path, get_tooloutput_file_path
-from cpacspy.cpacsfunctions import create_branch, get_value_or_default, open_tixi
+from cpacspy.cpacsfunctions import (
+ open_tixi,
+ get_value,
+ create_branch,
+ get_value_or_default,
+)
+
+from ceasiompy.utils.ceasiompyutils import (
+ call_main,
+ run_software,
+ aircraft_name,
+ get_results_directory,
+ get_reasonable_nb_cpu,
+)
-log = get_logger()
+from pathlib import Path
+from cpacspy.cpacspy import CPACS
+
+from ceasiompy import log
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
+from ceasiompy.utils.commonxpath import (
+ SU2MESH_XPATH,
+ SUMO_REFINE_LEVEL_XPATH,
+ SUMOFILE_XPATH,
+ SPECIFIED_SUMOFILE_XPATH,
+)
+from ceasiompy.SUMOAutoMesh import MODULE_NAME
+from ceasiompy.CPACS2SUMO import MODULE_NAME as CPACS2SUMO
# =================================================================================================
# FUNCTIONS
@@ -121,7 +138,7 @@ def add_mesh_parameters(sumo_file_path, refine_level=0.0):
body_cnt = get_part_count(sumo, ROOT_XPATH, part_name="BodySkeleton")
for i_body in range(body_cnt):
- body_xpath = ROOT_XPATH + f"/BodySkeleton[{i_body+1}]"
+ body_xpath = ROOT_XPATH + f"/BodySkeleton[{i_body + 1}]"
circ_list = []
min_radius = 10e6
@@ -164,7 +181,7 @@ def add_mesh_parameters(sumo_file_path, refine_level=0.0):
wing_cnt = get_part_count(sumo, ROOT_XPATH, part_name="WingSkeleton")
for i_wing in range(wing_cnt):
- wing_xpath = ROOT_XPATH + f"/WingSkeleton[{i_wing+1}]"
+ wing_xpath = ROOT_XPATH + f"/WingSkeleton[{i_wing + 1}]"
chord_list = []
@@ -209,7 +226,7 @@ def add_mesh_parameters(sumo_file_path, refine_level=0.0):
sumo.save(str(sumo_file_path))
-def create_SU2_mesh(cpacs_path, cpacs_out_path):
+def main(cpacs: CPACS, wkdir: Path):
"""Function to create a simple SU2 mesh form an SUMO file (.smx)
Function 'create_mesh' is used to generate an unstructured mesh with SUMO
@@ -220,28 +237,26 @@ def create_SU2_mesh(cpacs_path, cpacs_out_path):
Source :
* sumo help, tetgen help (in the folder /doc)
- Args:
- cpacs_path (Path): Path to the CPACS file
- cpacs_out_path (Path): Path to the output CPACS file
-
"""
+ tixi = cpacs.tixi
- tixi = open_tixi(cpacs_path)
+ cpacs_to_sumo_wkdir = get_results_directory(CPACS2SUMO)
+ su2_mesh_path = Path(cpacs_to_sumo_wkdir, "ToolOutput.smx")
- sumo_results_dir = get_results_directory("SUMOAutoMesh")
- su2_mesh_path = Path(sumo_results_dir, "ToolOutput.su2")
+ if tixi.checkElement(SUMOFILE_XPATH):
+ sumo_file_path = Path(get_value(tixi, SUMOFILE_XPATH))
+ else:
+ sumo_file_path = Path(get_value(tixi, SPECIFIED_SUMOFILE_XPATH))
- sumo_file_path = Path(get_value_or_default(tixi, SUMOFILE_XPATH, ""))
if not sumo_file_path.exists():
raise FileNotFoundError(f"No SUMO file has been found at: {sumo_file_path}")
- log.info("Setting mesh parameters...")
refine_level = get_value_or_default(tixi, SUMO_REFINE_LEVEL_XPATH, 1.0)
log.info(f"Mesh refinement level: {refine_level}")
add_mesh_parameters(sumo_file_path, refine_level)
# Tetgen option, see the help for more options
- output = "su2"
+ output = "smx"
options = "pq1.16VY"
arguments = [
"-batch",
@@ -252,8 +267,10 @@ def create_SU2_mesh(cpacs_path, cpacs_out_path):
current_os = platform.system()
- if current_os == "Darwin":
- log.info("Your OS is MacOS")
+ nb_cpu = get_reasonable_nb_cpu()
+
+ if (current_os == "Darwin") or (current_os == "Linux"):
+ log.info("Your OS is supported by SUMOAutoMesh.")
# The complete command line to run is:
# /Applications/SUMO/dwfsumo.app/Contents/MacOS/dwfsumo
@@ -263,15 +280,13 @@ def create_SU2_mesh(cpacs_path, cpacs_out_path):
# with QT occurs when trying to run the command.
# The folder which contains the 'dwfsumo' executable must be in the PATH
- run_software("dwfsumo", arguments, sumo_results_dir)
-
- elif current_os == "Linux":
- log.info("Your OS is Linux")
-
- # The complete command line to run is:
- # sumo -batch -output=su2 -tetgen-options=pq1.16VY ToolOutput.smx
-
- run_software("dwfsumo", arguments, sumo_results_dir)
+ run_software(
+ software_name="dwfsumo",
+ arguments=arguments,
+ wkdir=wkdir,
+ with_mpi=False,
+ nb_cpu=nb_cpu
+ )
elif current_os == "Windows":
log.info("Your OS is Windows")
@@ -285,35 +300,27 @@ def create_SU2_mesh(cpacs_path, cpacs_out_path):
# Copy the mesh in the MESH directory
su2_mesh_name = aircraft_name(tixi) + "_baseline.su2"
- su2_mesh_out_path = Path(sumo_results_dir, su2_mesh_name)
+ su2_mesh_out_path = Path(wkdir, su2_mesh_name)
+
+ if not os.path.exists(su2_mesh_path):
+ log.error(f"SU2 mesh file does not exist: {su2_mesh_path}")
+ return
+
shutil.copyfile(su2_mesh_path, su2_mesh_out_path)
if not su2_mesh_out_path.exists():
raise ValueError("No SU2 Mesh file has been generated!")
+ else:
+ log.info("A SU2 Mesh has been correctly generated.")
- log.info("An SU2 Mesh has been correctly generated.")
- create_branch(tixi, SU2MESH_XPATH)
- tixi.updateTextElement(SU2MESH_XPATH, str(su2_mesh_out_path))
- su2_mesh_path.unlink()
-
- tixi.save(str(cpacs_out_path))
-
+ create_branch(tixi, SU2MESH_XPATH)
+ tixi.updateTextElement(SU2MESH_XPATH, str(su2_mesh_out_path))
+ su2_mesh_path.unlink()
# =================================================================================================
# MAIN
# =================================================================================================
-def main(cpacs_path, cpacs_out_path):
- log.info("----- Start of " + MODULE_NAME + " -----")
-
- create_SU2_mesh(cpacs_path, cpacs_out_path)
-
- log.info("----- End of " + MODULE_NAME + " -----")
-
-
if __name__ == "__main__":
- cpacs_path = get_toolinput_file_path(MODULE_NAME)
- cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
-
- main(cpacs_path, cpacs_out_path)
+ call_main(main, MODULE_NAME)
diff --git a/ceasiompy/SaveAeroCoefficients/Results/SaveAeroCoefficients/D150.png b/ceasiompy/SaveAeroCoefficients/Results/SaveAeroCoefficients/D150.png
new file mode 100644
index 000000000..ebd9eda8b
Binary files /dev/null and b/ceasiompy/SaveAeroCoefficients/Results/SaveAeroCoefficients/D150.png differ
diff --git a/ceasiompy/SaveAeroCoefficients/ToolInput/.keep b/ceasiompy/SaveAeroCoefficients/ToolInput/.keep
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/ceasiompy/SaveAeroCoefficients/ToolInput/.keep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ceasiompy/SaveAeroCoefficients/ToolOutput/.keep b/ceasiompy/SaveAeroCoefficients/ToolOutput/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/ceasiompy/SaveAeroCoefficients/__init__.py b/ceasiompy/SaveAeroCoefficients/__init__.py
index e69de29bb..8eaccc295 100644
--- a/ceasiompy/SaveAeroCoefficients/__init__.py
+++ b/ceasiompy/SaveAeroCoefficients/__init__.py
@@ -0,0 +1,54 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for SaveAeroCoefficients module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+from ceasiompy import log
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = True
+
+# ===== Include GUI =====
+include_gui = True
+
+# ===== Add a Results Directory =====
+RES_DIR = True
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+# None options
+NONE_LIST = ["None", "NONE", "No", "NO", "N", "n", "-", " ", ""] # ?
+
+FEATURE_DICT = {
+ "machNumber": "Mach",
+ "angleOfAttack": "AoA",
+ "angleOfSideslip": "AoS",
+ "altitude": "alt",
+}
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/SaveAeroCoefficients/__specs__.py b/ceasiompy/SaveAeroCoefficients/__specs__.py
index 94155e1a3..2eadacbb3 100644
--- a/ceasiompy/SaveAeroCoefficients/__specs__.py
+++ b/ceasiompy/SaveAeroCoefficients/__specs__.py
@@ -1,30 +1,48 @@
-from pathlib import Path
-from ceasiompy.utils.moduleinterfaces import CPACSInOut
-from ceasiompy.utils.commonxpath import PLOT_XPATH
-# ===== Module Status =====
-# True if the module is active
-# False if the module is disabled (not working or not ready)
-module_status = True
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
-# ===== Results directory path =====
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-RESULTS_DIR = Path("Results", "AeroCoefficients")
+GUI Interface of SaveAeroCoefficients.
-# ===== CPACS inputs and outputs =====
-cpacs_inout = CPACSInOut()
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+"""
-# ===== Input =====
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from ceasiompy.utils.moduleinterfaces import CPACSInOut
+
+from ceasiompy import log
+from ceasiompy.SaveAeroCoefficients import include_gui
+
+from ceasiompy.utils.commonxpath import (
+ PLOT_XPATH,
+ AEROMAP_TO_PLOT_XPATH,
+)
+
+# ==============================================================================
+# VARIABLE
+# ==============================================================================
+
+cpacs_inout = CPACSInOut()
+
+# ==============================================================================
+# GUI INPUTS
+# ==============================================================================
cpacs_inout.add_input(
var_name="",
var_type=list,
default_value=None,
descr="List of aeroMap to plot",
- xpath=PLOT_XPATH + "/aeroMapToPlot",
- gui=True,
+ xpath=AEROMAP_TO_PLOT_XPATH,
+ gui=include_gui,
gui_name="__AEROMAP_CHECKBOX",
gui_group="Aeromap settings",
)
@@ -35,7 +53,7 @@
default_value="None",
descr="Altitude inclusion criteria",
xpath=PLOT_XPATH + "/criterion/alt",
- gui=True,
+ gui=include_gui,
gui_name="Altitdue criteria",
gui_group="Plot vs AoA",
)
@@ -46,7 +64,7 @@
default_value="None",
descr="Mach inclusion criteria",
xpath=PLOT_XPATH + "/criterion/mach",
- gui=True,
+ gui=include_gui,
gui_name="Mach criteria",
gui_group="Plot vs AoA",
)
@@ -57,7 +75,14 @@
default_value="None",
descr="Angle of Sideslip (AoS) inclusion criteria",
xpath=PLOT_XPATH + "/criterion/aos",
- gui=True,
+ gui=include_gui,
gui_name="AoS criteria",
gui_group="Plot vs AoA",
)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/SaveAeroCoefficients/func/plot.py b/ceasiompy/SaveAeroCoefficients/func/plot.py
new file mode 100644
index 000000000..1baed3739
--- /dev/null
+++ b/ceasiompy/SaveAeroCoefficients/func/plot.py
@@ -0,0 +1,92 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Plot Aerodynamic coefficients from CPACS v3 aeroMaps
+
+
+| Author: Leon Deligny
+| Creation: 27 march 2025
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+import os
+import matplotlib
+import numpy as np
+import matplotlib.pyplot as plt
+
+from ceasiompy.SaveAeroCoefficients.func.utils import (
+ write_legend,
+ subplot_options,
+)
+
+from pathlib import Path
+from typing import List
+
+from ceasiompy import log
+
+# =================================================================================================
+# BACKEND SETTING
+# =================================================================================================
+
+if os.environ.get('DISPLAY', '') == '':
+ matplotlib.use('Agg')
+else:
+ matplotlib.use('TkAgg')
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def plot(wkdir: Path, groupby_list: List, title: str, aeromap, criterion) -> None:
+ fig, axs = plt.subplots(2, 3)
+ fig.suptitle(title, fontsize=14)
+ fig.set_figheight(8)
+ fig.set_figwidth(15)
+ fig.subplots_adjust(left=0.06)
+ axs[0, 1].axhline(y=0.0, color="k", linestyle="-") # Line at Cm=0
+
+ for value, grp in aeromap.loc[criterion].groupby(groupby_list):
+ legend = write_legend(groupby_list, value)
+
+ # Convert data to NumPy arrays before plotting
+ angle_of_attack = np.array(grp["angleOfAttack"])
+ cl = np.array(grp["cl"])
+ cd = np.array(grp["cd"])
+ cms = np.array(grp["cms"])
+ cl_cd = cl / cd
+
+ axs[0, 0].plot(angle_of_attack, cl, "x-", label=legend)
+ axs[1, 0].plot(angle_of_attack, cd, "x-")
+ axs[0, 1].plot(angle_of_attack, cms, "x-")
+ axs[1, 1].plot(angle_of_attack, cl_cd, "x-")
+ axs[0, 2].plot(cd, cl, "x-")
+ axs[1, 2].plot(cl, cl_cd, "x-")
+
+ subplot_options(axs[0, 0], "CL", "AoA")
+ subplot_options(axs[1, 0], "CD", "AoA")
+ subplot_options(axs[0, 1], "Cm", "AoA")
+ subplot_options(axs[1, 1], "CL/CD", "AoA")
+ subplot_options(axs[0, 2], "CL", "CD")
+ subplot_options(axs[1, 2], "CL/CD", "CL")
+ fig.legend(loc="upper right")
+
+ fig_name = title.replace(" ", "").replace("=", "") + ".png"
+ fig_path = Path(wkdir, fig_name)
+ plt.savefig(fig_path)
+ log.info(f"Figure saved at: {fig_path}")
+
+
+# ==============================================================================
+# MAIN
+# ==============================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/SaveAeroCoefficients/func/utils.py b/ceasiompy/SaveAeroCoefficients/func/utils.py
new file mode 100644
index 000000000..a6f3e9426
--- /dev/null
+++ b/ceasiompy/SaveAeroCoefficients/func/utils.py
@@ -0,0 +1,108 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Small description of the script
+
+
+| Author: Leon Deligny
+| Creation: 27 March 2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from typing import List
+from pandas import DataFrame
+
+from ceasiompy import log
+from ceasiompy.SaveAeroCoefficients import (
+ NONE_LIST,
+ FEATURE_DICT,
+)
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def write_legend(groupby_list: List, value) -> None:
+ """Write legend with the correct format for the plot.
+
+ Args:
+ groupby_list (list): List of parameter which will be use to group plot data
+ value (...): If one value (str of float), if multiple value (tuple)
+ """
+
+ groupby_name_list = []
+ for name in groupby_list:
+ if name in FEATURE_DICT:
+ groupby_name_list.append(FEATURE_DICT[name])
+ else:
+ groupby_name_list.append(name)
+
+ legend = ""
+ for i, param in enumerate(groupby_list):
+
+ if len(groupby_list) > 1:
+ value_i = value[i]
+ else:
+ value_i = value
+
+ if param == "uid":
+ legend += str(value_i)
+ else:
+ if name in FEATURE_DICT:
+ name = FEATURE_DICT[name]
+ else:
+ name = param
+
+ legend += name + "=" + str(value_i)
+
+ if i + 1 != len(groupby_list):
+ legend += "\n"
+
+ return legend
+
+
+def subplot_options(ax, ylabel: str, xlabel: str) -> None:
+ """Set xlabel, ylabel and grid on a specific subplot.
+
+ Args:
+ ax (Axes objects): Axes of the concerned subplot
+ xlabel (str): x label name
+ ylabel (str): y label name
+ """
+
+ ax.set_ylabel(ylabel)
+ ax.set_xlabel(xlabel)
+ ax.grid()
+
+
+def deal_with_feature(
+ title: str,
+ criterion,
+ aeromap: DataFrame,
+ groupby_list: List,
+ feature: str,
+ crit: str,
+) -> None:
+ feature_ = FEATURE_DICT[feature]
+ if len(aeromap[feature].unique()) == 1:
+ title += f" - {feature_} = " + str(aeromap[feature].loc[0])
+ groupby_list.remove(feature)
+ elif crit not in NONE_LIST:
+ criterion = criterion & (aeromap.angleOfSideslip == float(crit))
+ title += f" - {feature_} = " + crit
+ groupby_list.remove(feature)
+
+
+# ==============================================================================
+# MAIN
+# ==============================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/SaveAeroCoefficients/saveaerocoef.py b/ceasiompy/SaveAeroCoefficients/saveaerocoef.py
index 2da510f68..59eb264bf 100644
--- a/ceasiompy/SaveAeroCoefficients/saveaerocoef.py
+++ b/ceasiompy/SaveAeroCoefficients/saveaerocoef.py
@@ -5,13 +5,11 @@
Plot Aerodynamic coefficients from CPACS v3 aeroMaps
-Python version: >=3.8
| Author: Aidan jungo
| Creation: 2019-08-19
TODO:
-
* add plot vs Mach, vs sideslip angle, damping derivatives, CS deflections
"""
@@ -20,121 +18,53 @@
# IMPORTS
# =================================================================================================
-from pathlib import Path
-
-import matplotlib.pyplot as plt
import pandas as pd
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.ceasiompyutils import get_aeromap_list_from_xpath, get_results_directory
-from ceasiompy.utils.commonxpath import PLOT_XPATH
-from ceasiompy.utils.moduleinterfaces import get_toolinput_file_path, get_tooloutput_file_path
-from cpacspy.cpacsfunctions import get_value_or_default
-from cpacspy.cpacspy import CPACS
-log = get_logger()
+from cpacspy.cpacsfunctions import get_value_or_default
+from ceasiompy.SaveAeroCoefficients.func.plot import plot
+from ceasiompy.SaveAeroCoefficients.func.utils import deal_with_feature
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
+from ceasiompy.utils.ceasiompyutils import (
+ call_main,
+ get_aeromap_list_from_xpath,
+)
-NONE_LIST = ["None", "NONE", "No", "NO", "N", "n", "-", " ", ""]
+from pathlib import Path
+from cpacspy.cpacspy import (
+ CPACS,
+ AeroMap,
+)
-# =================================================================================================
-# CLASSES
-# =================================================================================================
+from ceasiompy.SaveAeroCoefficients import MODULE_NAME
+from ceasiompy.utils.commonxpath import (
+ PLOT_XPATH,
+ AIRCRAFT_NAME_XPATH,
+ AEROMAP_TO_PLOT_XPATH,
+)
# =================================================================================================
-# FUNCTIONS
+# MAIN
# =================================================================================================
-def write_legend(groupby_list, value):
- """Write legend with the correct format for the plot.
-
- Args:
- groupby_list (list): List of parameter which will be use to group plot data
- value (...): If one value (str of float), if multiple value (tuple)
+def main(cpacs: CPACS, wkdir: Path) -> None:
"""
-
- groupby_name_list = []
- for name in groupby_list:
- if name == "machNumber":
- groupby_name_list.append("Mach")
- elif name == "angleOfAttack":
- groupby_name_list.append("AoA")
- elif name == "angleOfSideslip":
- groupby_name_list.append("AoS")
- elif name == "altitude":
- groupby_name_list.append("Alt")
- else:
- groupby_name_list.append(name)
-
- legend = ""
- for i, param in enumerate(groupby_list):
-
- if len(groupby_list) > 1:
- value_i = value[i]
- else:
- value_i = value
-
- if param == "uid":
- legend += value_i
- else:
- if param == "machNumber":
- name = "Mach"
- elif param == "angleOfAttack":
- name = "AoA"
- elif param == "angleOfSideslip":
- name = "AoS"
- elif param == "altitude":
- name = "Alt"
- else:
- name = param
- legend += name + "=" + str(value_i)
-
- if i + 1 != len(groupby_list):
- legend += "\n"
-
- return legend
-
-
-def subplot_options(ax, ylabel, xlabel):
- """Set xlabel, ylabel and grid on a specific subplot.
-
- Args:
- ax (Axes objects): Axes of the concerned subplot
- xlabel (str): x label name
- ylabel (str): y label name
- """
-
- ax.set_ylabel(ylabel)
- ax.set_xlabel(xlabel)
- ax.grid()
-
-
-def save_aero_coef(cpacs_path, cpacs_out_path):
- """Save Aero coefficients from the chosen aeroMap in the CPACS file
-
- Function 'save_aero_coef' can save one or several aeromap from the CPACS
- file according to some user defined option, these option will be shown in the the
- GUI interface or default values will be used.
-
- Args:
- cpacs_path (Path): Path to CPACS file
- cpacs_out_path (Path):Path to CPACS output file
+ Save Aero coefficients from the chosen aeroMap in the CPACS file.
"""
- cpacs = CPACS(cpacs_path)
-
- aeromap_to_plot_xpath = PLOT_XPATH + "/aeroMapToPlot"
- aeromap_uid_list = get_aeromap_list_from_xpath(cpacs, aeromap_to_plot_xpath)
+ # Define variables
+ tixi = cpacs.tixi
+ groupby_list = ["uid", "machNumber", "altitude", "angleOfSideslip"]
+ # Get list of aeromaps
+ aeromap_uid_list = get_aeromap_list_from_xpath(cpacs, AEROMAP_TO_PLOT_XPATH)
aeromap_df_list = []
for aeromap_uid in aeromap_uid_list:
- aeromap_df = cpacs.get_aeromap_by_uid(aeromap_uid).df
+ aeromap: AeroMap = cpacs.get_aeromap_by_uid(aeromap_uid)
+ aeromap_df = aeromap.df
aeromap_df["uid"] = aeromap_uid
aeromap_df_list.append(aeromap_df)
-
aeromap = pd.concat(aeromap_df_list, ignore_index=True)
if len(aeromap_uid_list) > 1:
@@ -142,96 +72,26 @@ def save_aero_coef(cpacs_path, cpacs_out_path):
else:
uid_crit = aeromap_uid_list[0]
- title = cpacs.ac_name
+ title = tixi.getTextElement(AIRCRAFT_NAME_XPATH)
criterion = pd.Series([True] * len(aeromap.index))
- groupby_list = ["uid", "machNumber", "altitude", "angleOfSideslip"]
crit_xpath = PLOT_XPATH + "/criterion"
- alt_crit = get_value_or_default(cpacs.tixi, crit_xpath + "/alt", "None")
- mach_crit = get_value_or_default(cpacs.tixi, crit_xpath + "/mach", "None")
- aos_crit = get_value_or_default(cpacs.tixi, crit_xpath + "/aos", "None")
-
- cpacs.save_cpacs(cpacs_out_path, overwrite=True)
-
- # Modify criterion and title according to user option
- if len(aeromap["altitude"].unique()) == 1:
- title += " - Alt = " + str(aeromap["altitude"].loc[0])
- groupby_list.remove("altitude")
- elif alt_crit not in NONE_LIST:
- criterion = criterion & (aeromap.altitude == alt_crit)
- title += " - Alt = " + str(alt_crit)
- groupby_list.remove("altitude")
-
- if len(aeromap["machNumber"].unique()) == 1:
- title += " - Mach = " + str(aeromap["machNumber"].loc[0])
- groupby_list.remove("machNumber")
- elif mach_crit not in NONE_LIST:
- criterion = criterion & (aeromap.machNumber == mach_crit)
- title += " - Mach = " + str(mach_crit)
- groupby_list.remove("machNumber")
-
- if len(aeromap["angleOfSideslip"].unique()) == 1:
- title += " - AoS = " + str(aeromap["angleOfSideslip"].loc[0])
- groupby_list.remove("angleOfSideslip")
- elif aos_crit not in NONE_LIST:
- criterion = criterion & (aeromap.angleOfSideslip == aos_crit)
- title += " - AoS = " + str(aos_crit)
- groupby_list.remove("angleOfSideslip")
+ alt_crit = get_value_or_default(tixi, crit_xpath + "/alt", "None")
+ mach_crit = get_value_or_default(tixi, crit_xpath + "/mach", "None")
+ aos_crit = get_value_or_default(tixi, crit_xpath + "/aos", "None")
+
+ deal_with_feature(title, criterion, aeromap, groupby_list, "altitude", alt_crit)
+ deal_with_feature(title, criterion, aeromap, groupby_list, "machNumber", mach_crit)
+ deal_with_feature(title, criterion, aeromap, groupby_list, "angleOfSideslip", aos_crit)
if uid_crit is not None and len(groupby_list) > 1:
criterion = criterion & (aeromap.uid == uid_crit)
title += " - " + uid_crit
groupby_list.remove("uid")
- fig, axs = plt.subplots(2, 3)
- fig.suptitle(title, fontsize=14)
- fig.set_figheight(8)
- fig.set_figwidth(15)
- fig.subplots_adjust(left=0.06)
- axs[0, 1].axhline(y=0.0, color="k", linestyle="-") # Line at Cm=0
-
- for value, grp in aeromap.loc[criterion].groupby(groupby_list):
-
- legend = write_legend(groupby_list, value)
-
- axs[0, 0].plot(grp["angleOfAttack"], grp["cl"], "x-", label=legend)
- axs[1, 0].plot(grp["angleOfAttack"], grp["cd"], "x-")
- axs[0, 1].plot(grp["angleOfAttack"], grp["cms"], "x-")
- axs[1, 1].plot(grp["angleOfAttack"], grp["cl"] / grp["cd"], "x-")
- axs[0, 2].plot(grp["cd"], grp["cl"], "x-")
- axs[1, 2].plot(grp["cl"], grp["cl"] / grp["cd"], "x-")
-
- subplot_options(axs[0, 0], "CL", "AoA")
- subplot_options(axs[1, 0], "CD", "AoA")
- subplot_options(axs[0, 1], "Cm", "AoA")
- subplot_options(axs[1, 1], "CL/CD", "AoA")
- subplot_options(axs[0, 2], "CL", "CD")
- subplot_options(axs[1, 2], "CL/CD", "CL")
- fig.legend(loc="upper right")
-
- results_dir = get_results_directory("SaveAeroCoefficients")
- fig_name = title.replace(" ", "").replace("=", "") + ".png"
- fig_path = Path(results_dir, fig_name)
- plt.savefig(fig_path)
-
-
-# =================================================================================================
-# MAIN
-# =================================================================================================
-
-
-def main(cpacs_path, cpacs_out_path):
-
- log.info("----- Start of " + MODULE_NAME + " -----")
-
- save_aero_coef(cpacs_path, cpacs_out_path)
-
- log.info("----- End of " + MODULE_NAME + " -----")
+ # Generate plots
+ plot(wkdir, groupby_list, title, aeromap, criterion)
if __name__ == "__main__":
-
- cpacs_path = get_toolinput_file_path(MODULE_NAME)
- cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
-
- main(cpacs_path, cpacs_out_path)
+ call_main(main, MODULE_NAME)
diff --git a/ceasiompy/SaveAeroCoefficients/tests/test_saveaerocoef.py b/ceasiompy/SaveAeroCoefficients/tests/test_saveaerocoef.py
index 2d4905fd7..3d9ef7e86 100644
--- a/ceasiompy/SaveAeroCoefficients/tests/test_saveaerocoef.py
+++ b/ceasiompy/SaveAeroCoefficients/tests/test_saveaerocoef.py
@@ -5,7 +5,6 @@
Test functions for 'ceasiompy/SaveAeroCoefficients/saveaerocoef.py'
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2022-09-23
@@ -16,22 +15,18 @@
# IMPORTS
# =================================================================================================
+from ceasiompy.SaveAeroCoefficients.saveaerocoef import main as save_aero_coef
+from ceasiompy.utils.ceasiompyutils import change_working_dir, get_results_directory
from pathlib import Path
+from cpacspy.cpacspy import CPACS
+
-from ceasiompy.SaveAeroCoefficients.saveaerocoef import save_aero_coef
-from ceasiompy.utils.ceasiompyutils import change_working_dir
from ceasiompy.utils.commonpaths import CPACS_FILES_PATH
+from ceasiompy.SaveAeroCoefficients import MODULE_NAME, MODULE_DIR
-MODULE_DIR = Path(__file__).parent
CPACS_IN_PATH = Path(CPACS_FILES_PATH, "D150_simple.xml")
CPACS_OUT_PATH = Path(MODULE_DIR, "D150_simple_saveaerocoef_test.xml")
-FIG_PATH = Path(MODULE_DIR, "Results", "AeroCoefficients", "D150-Alt0.0-Mach0.3-AoS0.0.png")
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
# =================================================================================================
# FUNCTIONS
@@ -44,20 +39,19 @@ def test_save_aero_coef():
"""
with change_working_dir(MODULE_DIR):
- save_aero_coef(CPACS_IN_PATH, CPACS_OUT_PATH)
- assert FIG_PATH.exists()
-
- if CPACS_OUT_PATH.exists():
- CPACS_OUT_PATH.unlink()
-
- if FIG_PATH.exists():
- FIG_PATH.unlink()
+ cpacs = CPACS(CPACS_IN_PATH)
+ results_dir = get_results_directory(MODULE_NAME)
+ save_aero_coef(cpacs, results_dir)
+ # Assert a .png file exists in the directory
+ png_files = list(results_dir.glob("*.png"))
+ assert png_files, f"No .png file found in {results_dir}"
# =================================================================================================
# MAIN
# =================================================================================================
+
if __name__ == "__main__":
print("Test SaveAeroCoefficients")
print("To run test use the following command:")
diff --git a/ceasiompy/SkinFriction/README.md b/ceasiompy/SkinFriction/README.md
index af2817737..4ae0da0d3 100644
--- a/ceasiompy/SkinFriction/README.md
+++ b/ceasiompy/SkinFriction/README.md
@@ -6,7 +6,7 @@
**State**: :heavy_check_mark:
-`SkinFriction` module adds skin friction drag to an Aeromap calculated without it. The aerodynamic coefficients calculated with pyTornado or Euler methods (SU2) do not depend on atmospheric parameters and do not include the skin friction drag. It means that for different altitudes as input you will get the same aerodynamic coefficients. Furthermore, the drag calculated with these methods is generally underestimated. You can use this module to estimate the skin friction drag from empirical formula and add it to the selected aeroMap.
+`SkinFriction` module adds skin friction drag to an Aeromap calculated without it. The calculated aerodynamic coefficients do not depend on atmospheric parameters and do not include the skin friction drag. It means that for different altitudes as input you will get the same aerodynamic coefficients. Furthermore, the drag calculated with these methods is generally underestimated. You can use this module to estimate the skin friction drag from empirical formula and add it to the selected aeroMap.
## Inputs
diff --git a/ceasiompy/SkinFriction/ToolOutput/.keep b/ceasiompy/SkinFriction/ToolOutput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/SkinFriction/ToolOutput/.keep
+++ b/ceasiompy/SkinFriction/ToolOutput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/SkinFriction/__init__.py b/ceasiompy/SkinFriction/__init__.py
index e69de29bb..9d394fac1 100644
--- a/ceasiompy/SkinFriction/__init__.py
+++ b/ceasiompy/SkinFriction/__init__.py
@@ -0,0 +1,35 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for SkinFriction module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = True
+
+# ===== Include GUI =====
+include_gui = True
+
+# ===== Add a Results Directory =====
+RES_DIR = True
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
diff --git a/ceasiompy/SkinFriction/__specs__.py b/ceasiompy/SkinFriction/__specs__.py
index f82791f11..6697dcf71 100644
--- a/ceasiompy/SkinFriction/__specs__.py
+++ b/ceasiompy/SkinFriction/__specs__.py
@@ -1,27 +1,50 @@
-from ceasiompy.utils.moduleinterfaces import CPACSInOut
-from ceasiompy.utils.commonxpath import GEOM_XPATH, RANGE_XPATH, SF_XPATH, PLOT_XPATH
-from pathlib import Path
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+GUI Interface of SkinFriction.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
-# ===== Module Status =====
-# True if the module is active
-# False if the module is disabled (not working or not ready)
-module_status = True
+from ceasiompy.utils.moduleinterfaces import CPACSInOut
-# ===== Results directory path =====
+from ceasiompy import log
+from ceasiompy.SkinFriction import include_gui
-RESULTS_DIR = Path("Results", "SkinFriction")
+from ceasiompy.utils.commonxpath import (
+ SF_XPATH,
+ GEOM_XPATH,
+ PLOT_XPATH,
+ RANGE_CRUISE_ALT_XPATH,
+ RANGE_CRUISE_MACH_XPATH,
+)
-# ===== Input =====
+# ==============================================================================
+# VARIABLE
+# ==============================================================================
cpacs_inout = CPACSInOut()
+# ==============================================================================
+# GUI INPUTS
+# ==============================================================================
+
cpacs_inout.add_input(
var_name="",
var_type=list,
default_value=None,
descr="To which aeroMap the skin friction coef should be added",
xpath=SF_XPATH + "/aeroMapToCalculate",
- gui=True,
+ gui=include_gui,
gui_name="__AEROMAP_CHECKBOX",
gui_group="Aeromap settings",
)
@@ -35,38 +58,46 @@
xpath=GEOM_XPATH + "/analysis/wettedArea",
gui=False,
gui_name="Wetted Area",
- gui_group=None,
+ gui_group="Wetted Area",
)
cpacs_inout.add_input(
- var_name="",
+ var_name="Delete",
var_type=bool,
default_value=False,
unit=None,
descr="Delete original aeroMap once skin friction coefficient has been added",
xpath=SF_XPATH + "/deleteOriginal",
- gui=True,
+ gui=include_gui,
gui_name="Delete Original",
- gui_group=None,
+ gui_group="Delete",
)
cpacs_inout.add_input(
var_name="cruise_mach",
default_value=0.78,
- unit="-",
- descr="Aircraft cruise Mach number",
- xpath=RANGE_XPATH + "/cruiseMach",
+ unit="[Mach]",
+ descr="Cruise speed of aircraft",
+ xpath=RANGE_CRUISE_MACH_XPATH,
+ gui=include_gui,
+ gui_name="Aircraft cruise speed",
+ gui_group="Aircraft cruise parameters",
)
cpacs_inout.add_input(
var_name="cruise_alt",
- default_value=12000,
- unit="m",
- descr="Aircraft cruise altitude",
- xpath=RANGE_XPATH + "/cruiseAltitude",
+ default_value=12_000,
+ unit="[m]",
+ descr="Cruise altitude of aircraft",
+ xpath=RANGE_CRUISE_ALT_XPATH,
+ gui=include_gui,
+ gui_name="Aircraft cruise altitude",
+ gui_group="Aircraft cruise parameters",
)
-# ===== Output =====
+# ==============================================================================
+# GUI OUTPUTS
+# ==============================================================================
cpacs_inout.add_output(
var_name="cd0",
@@ -99,3 +130,10 @@
descr="List of aeroMap to plot",
xpath=PLOT_XPATH + "/aeroMapToPlot",
)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/SkinFriction/skinfriction.py b/ceasiompy/SkinFriction/skinfriction.py
index ba6a8ca3a..d18ba3079 100644
--- a/ceasiompy/SkinFriction/skinfriction.py
+++ b/ceasiompy/SkinFriction/skinfriction.py
@@ -5,13 +5,11 @@
Calculate skin friction drag coefficient
-Python version: >=3.8
| Author: Aidan Jungo
| Creation: 2019-06-13
TODO:
-
* update __specs__ file
* (Check if projected value are realistic for different cases)
* Adapt the code deal with fixed CL mode case
@@ -22,45 +20,35 @@
# IMPORTS
# =================================================================================================
-import copy
import math
-from pathlib import Path
-from ambiance import Atmosphere
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.ceasiompyutils import get_aeromap_list_from_xpath, get_results_directory
-from ceasiompy.utils.moduleinterfaces import (
- check_cpacs_input_requirements,
- get_toolinput_file_path,
- get_tooloutput_file_path,
-)
-from ceasiompy.utils.commonxpath import (
- PLOT_XPATH,
- SF_XPATH,
- WETTED_AREA_XPATH,
- WING_AREA_XPATH,
- WING_SPAN_XPATH,
+from ceasiompy.utils.ceasiompyutils import (
+ call_main,
+ get_aeromap_list_from_xpath,
)
from cpacspy.cpacsfunctions import (
- add_string_vector,
- create_branch,
- get_string_vector,
get_value,
+ create_branch,
+ add_string_vector,
get_value_or_default,
)
+
+from pathlib import Path
+from ambiance import Atmosphere
from cpacspy.cpacspy import CPACS
from markdownpy.markdownpy import MarkdownDoc
-log = get_logger()
-
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
+from ceasiompy import log
+from ceasiompy.utils.commonxpath import (
+ PLOT_XPATH,
+ SF_XPATH,
+ WETTED_AREA_XPATH,
+ WING_AREA_XPATH,
+ WING_SPAN_XPATH,
+)
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
+from ceasiompy.SkinFriction import MODULE_NAME
# =================================================================================================
# FUNCTIONS
@@ -134,11 +122,11 @@ def estimate_skin_friction_coef(wetted_area, wing_area, wing_span, mach, alt):
return cd0
-def add_skin_friction(cpacs_path, cpacs_out_path):
+def main(cpacs: CPACS, wkdir: Path):
"""Function to add the skin frictions drag coefficient to aerodynamic coefficients
Function 'add_skin_friction' add the skin friction drag 'cd0' to the
- SU2 and pyTornado aeroMap, if their UID is not given, it will add skin
+ aeroMap, if their UID is not given, it will add skin
friction to all aeroMap. For each aeroMap it creates a new aeroMap where
the skin friction drag coefficient is added with the correct projections.
@@ -147,18 +135,16 @@ def add_skin_friction(cpacs_path, cpacs_out_path):
cpacs_out_path (Path): Path to CPACS output file
"""
- cpacs = CPACS(cpacs_path)
-
- results_dir = get_results_directory("SkinFriction")
- md = MarkdownDoc(Path(results_dir, "Skin_Friction.md"))
+ tixi = cpacs.tixi
+ md = MarkdownDoc(Path(wkdir, "Skin_Friction.md"))
md.h2("SkinFriction")
md.h3("Geometry")
- wetted_area = get_value(cpacs.tixi, WETTED_AREA_XPATH)
+ wetted_area = get_value(tixi, WETTED_AREA_XPATH)
md.p(f"Wetted area: {wetted_area:.1f} [m^2]")
- wing_area = get_value_or_default(cpacs.tixi, WING_AREA_XPATH, cpacs.aircraft.wing_area)
+ wing_area = get_value_or_default(tixi, WING_AREA_XPATH, cpacs.aircraft.wing_area)
md.p(f"Wing area: {wing_area:.1f} [m^2]")
- wing_span = get_value_or_default(cpacs.tixi, WING_SPAN_XPATH, cpacs.aircraft.wing_span)
+ wing_span = get_value_or_default(tixi, WING_SPAN_XPATH, cpacs.aircraft.wing_span)
md.p(f"Wing span: {wing_span:.1f} [m]")
# Get aeroMapToCalculate
@@ -185,7 +171,7 @@ def add_skin_friction(cpacs_path, cpacs_out_path):
aeromap = cpacs.get_aeromap_by_uid(aeromap_uid)
# Export aeromaps without skin friction
- csv_path = Path(results_dir, f"{aeromap.uid}.csv")
+ csv_path = Path(wkdir, f"{aeromap.uid}.csv")
aeromap.export_csv(csv_path)
# Create new aeromap object to store coef with added skin friction
@@ -220,7 +206,7 @@ def add_skin_friction(cpacs_path, cpacs_out_path):
axis=1,
)
- aeromap_sf_csv = Path(results_dir, f"{aeromap_sf.uid}.csv")
+ aeromap_sf_csv = Path(wkdir, f"{aeromap_sf.uid}.csv")
aeromap.export_csv(aeromap_sf_csv)
# TODO: Should we change something in moment coef?
@@ -231,7 +217,7 @@ def add_skin_friction(cpacs_path, cpacs_out_path):
# Get aeroMap list to plot
aeromap_to_plot_xpath = PLOT_XPATH + "/aeroMapToPlot"
- if cpacs.tixi.checkElement(aeromap_to_plot_xpath):
+ if tixi.checkElement(aeromap_to_plot_xpath):
aeromap_uid_list = get_aeromap_list_from_xpath(
cpacs, aeromap_to_plot_xpath, empty_if_not_found=True
@@ -239,40 +225,23 @@ def add_skin_friction(cpacs_path, cpacs_out_path):
if not aeromap_uid_list:
aeromap_uid_list = get_value_or_default(
- cpacs.tixi, aeromap_to_plot_xpath, "DefaultAeromap"
+ tixi, aeromap_to_plot_xpath, "DefaultAeromap"
)
new_aeromap_to_plot = aeromap_uid_list + new_aeromap_uid_list
new_aeromap_to_plot = list(set(new_aeromap_to_plot))
- add_string_vector(cpacs.tixi, aeromap_to_plot_xpath, new_aeromap_to_plot)
+ add_string_vector(tixi, aeromap_to_plot_xpath, new_aeromap_to_plot)
else:
- create_branch(cpacs.tixi, aeromap_to_plot_xpath)
- add_string_vector(cpacs.tixi, aeromap_to_plot_xpath, new_aeromap_uid_list)
+ create_branch(tixi, aeromap_to_plot_xpath)
+ add_string_vector(tixi, aeromap_to_plot_xpath, new_aeromap_uid_list)
log.info('AeroMap "' + aeromap_uid + '" has been added to the CPACS file')
-
- cpacs.save_cpacs(cpacs_out_path, overwrite=True)
md.save()
-
-def main(cpacs_path, cpacs_out_path):
-
- log.info("----- Start of " + MODULE_NAME + " -----")
-
- add_skin_friction(cpacs_path, cpacs_out_path)
-
- log.info("----- End of " + MODULE_NAME + " -----")
-
-
# =================================================================================================
# MAIN
# =================================================================================================
-if __name__ == "__main__":
-
- cpacs_path = get_toolinput_file_path(MODULE_NAME)
- cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
- main(cpacs_path, cpacs_out_path)
-
- check_cpacs_input_requirements(cpacs_path)
+if __name__ == "__main__":
+ call_main(main, MODULE_NAME)
diff --git a/ceasiompy/SkinFriction/tests/test_skinfriction.py b/ceasiompy/SkinFriction/tests/test_skinfriction.py
index e0dc74eb8..2176b7b06 100644
--- a/ceasiompy/SkinFriction/tests/test_skinfriction.py
+++ b/ceasiompy/SkinFriction/tests/test_skinfriction.py
@@ -5,7 +5,6 @@
Test functions for 'lib/SkinFriction/skinfriction.py'
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2019-07-17
@@ -16,28 +15,28 @@
# IMPORTS
# =================================================================================================
-import logging
from pathlib import Path
-from ceasiompy.SkinFriction.skinfriction import add_skin_friction, estimate_skin_friction_coef
+from ceasiompy.utils.ceasiompyutils import get_results_directory, current_workflow_dir
+from ceasiompy.SkinFriction.skinfriction import (
+ main as add_skin_friction,
+ estimate_skin_friction_coef,
+)
from cpacspy.cpacspy import CPACS
from pytest import approx
+from ceasiompy.utils.commonpaths import LOGFILE
+
MODULE_DIR = Path(__file__).parent
CPACS_IN_PATH = Path(MODULE_DIR, "D150_simple_SkinFriction_test.xml")
CPACS_OUT_PATH = Path(MODULE_DIR, "D150_simple_skinfriction_test_output.xml")
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-
# =================================================================================================
# FUNCTIONS
# =================================================================================================
-def test_estimate_skin_friction_coef(caplog):
+def test_estimate_skin_friction_coef():
"""Test function 'estimate_skin_friction_coef'"""
# Normal case
@@ -51,34 +50,35 @@ def test_estimate_skin_friction_coef(caplog):
for cd0, inputs in test_dict.items():
assert cd0 == approx(estimate_skin_friction_coef(*inputs))
- # Case that raise warning (in logs)
- caplog.clear()
-
- with caplog.at_level(logging.WARNING):
+ with open(LOGFILE, "r") as log_file:
estimate_skin_friction_coef(400.0, 50, 20, 0.22, 12000)
- assert "Reynolds number is out of range." in caplog.text
+ assert "Reynolds number is out of range." in log_file.read()
- with caplog.at_level(logging.WARNING):
+ with open(LOGFILE, "r") as log_file:
estimate_skin_friction_coef(3401.0, 100, 20, 0.78, 12000)
- assert "Wetted area is not in the correct range." in caplog.text
+ assert "Wetted area is not in the correct range." in log_file.read()
- with caplog.at_level(logging.WARNING):
+ with open(LOGFILE, "r") as log_file:
estimate_skin_friction_coef(701.813, 19, 20, 0.78, 12000)
- assert "Wing area is not in the correct range." in caplog.text
+ assert "Wing area is not in the correct range." in log_file.read()
- with caplog.at_level(logging.WARNING):
+ with open(LOGFILE, "r") as log_file:
estimate_skin_friction_coef(701.813, 100, 75, 0.78, 12000)
- assert "Wing span is not in the correct range." in caplog.text
+ assert "Wing span is not in the correct range." in log_file.read()
def test_add_skin_friction():
"""Test function 'add_skin_friction'"""
+ cpacs = CPACS(CPACS_IN_PATH)
+ workflow_dir = current_workflow_dir()
# User the function to add skin frictions
- add_skin_friction(CPACS_IN_PATH, CPACS_OUT_PATH)
+ add_skin_friction(
+ cpacs,
+ wkdir=get_results_directory("SkinFriction", create=True, wkflow_dir=workflow_dir)
+ )
# Read the aeromap with the skin friction added in the output cpacs file
- cpacs = CPACS(CPACS_OUT_PATH)
apm_sf = cpacs.get_aeromap_by_uid("test_apm_SkinFriction")
# Expected values
@@ -100,7 +100,6 @@ def test_add_skin_friction():
# =================================================================================================
if __name__ == "__main__":
-
print("Test SkinFriction")
print("To run test use the following command:")
print(">> pytest -v")
diff --git a/ceasiompy/StaticStability/README.md b/ceasiompy/StaticStability/README.md
index cc8f08628..096de2869 100644
--- a/ceasiompy/StaticStability/README.md
+++ b/ceasiompy/StaticStability/README.md
@@ -6,25 +6,31 @@
**State**: :heavy_check_mark:
-`StaticStability` module checks the three main static stabilities, for one or several aeromaps. In order to assert those stability, it only check the sign of the slope corresponding to its stability:
+`StaticStability` module checks the three main static stabilities, that is the sign of the slope corresponding to its stability:
-- `cms` vs `angleOfAttack` for the longitudinal stability (negative means stable)
-- `cml` vs `angleOfSideslip` for the directional stability (positive means stable)
-- `cmd` vs `angleOfSideslip` for the lateral stability (negative means stable)
+- Pitch moment `Cm` vs `angleOfAttack` for the longitudinal stability (negative slope means stable)
+- Yaw moment `Cn` vs `angleOfSideslip` for the directional stability (positive slope means stable)
+- Roll moment `Cl` vs `angleOfSideslip` for the lateral stability (negative slope means stable)
(Names and reference system are those used by CPACS, more info [here](https://www.cpacs.de/documentation/CPACS_3_4_0_Docs/html/89b6a288-0944-bd56-a1ef-8d3c8e48ad95.htm))
## Inputs
-`StaticStability` only takes as input a CPACS file. However the CPACS file must contained at least one aeromap. In the settings, the user can chose on which aeromap and which stability the calculation will be performed.
+`StaticStability` only takes as input a CPACS file. However the CPACS file must contained at least one aeromap. In the settings, the user can choose on which aeromap and which stability the calculation will be performed.
## Analyses
-Depending how much points are stored in the aeromap, the different stability could be calculated or not. In order to check a stability the aeromap must contain at least two point with the same altitude, mach number, angle of attack/sideslip but with a different angle of sideslip/attack. The moment coefficient corresponding the stability must also be stored in the aeromap.
+1. If you use the PyAVL module before the StaticStability module :
+A unique (Mach, Altitude, AoA, AoS) in the aeromap suffices to do the Static Stability analysis as the necessary derivates are already computed.
+
+2. If you are not using PyAVL, then the model does a linear regression (it assumes that the moments are linear wrt to the angles of interest). Therefore you need to add strictly more then one different angle of attack and sideslip angle in the aeromap to access the stability derivatives.
## Outputs
- The output of `StaticStability` is a Markdown file (in the results directory) which contains a table for each different aeromap and stability. In every table the flight conditions and the stability result (Stable/Unstable) are shown.
+In the results directory you can find:
+ 1. a Markdown file which contains a table with the stability results (Stable/Unstable wrt to the 3 different axes) for the specific aeromap.
+
+ 2. A longitudinal, directional and lateral html plot of the Pitch, Yaw and Roll moments wrt AoA, AoS and AoS respectively.
## Installation or requirements
@@ -32,9 +38,8 @@ Depending how much points are stored in the aeromap, the different stability cou
## Limitations
-`StaticStability` module only uses a simple linear regression to check the sign of the slope, depending how the data are distributed it could leads to errors.
+`StaticStability` module makes the assumption that the pitch, roll and yaw moments are a linear function of the angle of attack and sidelip angle.
## More information
--
-
diff --git a/ceasiompy/StaticStability/ToolInput/.keep b/ceasiompy/StaticStability/ToolInput/.keep
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/ceasiompy/StaticStability/ToolInput/.keep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ceasiompy/StaticStability/ToolOutput/.keep b/ceasiompy/StaticStability/ToolOutput/.keep
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/ceasiompy/StaticStability/ToolOutput/.keep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/ceasiompy/StaticStability/__init__.py b/ceasiompy/StaticStability/__init__.py
index e69de29bb..c3c470f91 100644
--- a/ceasiompy/StaticStability/__init__.py
+++ b/ceasiompy/StaticStability/__init__.py
@@ -0,0 +1,48 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for StaticStability module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+from ceasiompy import log
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = True
+
+# ===== Include GUI =====
+include_gui = True
+
+# ===== Add a Results Directory =====
+RES_DIR = True
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+# Specific to StaticStability module
+STABILITY_DICT = {True: "Stable", False: "Unstable"}
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/StaticStability/__specs__.py b/ceasiompy/StaticStability/__specs__.py
index 7fc01b604..0b78b6ba8 100644
--- a/ceasiompy/StaticStability/__specs__.py
+++ b/ceasiompy/StaticStability/__specs__.py
@@ -1,80 +1,48 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-
-from pathlib import Path
-from ceasiompy.utils.moduleinterfaces import CPACSInOut
-from ceasiompy.utils.commonxpath import (
- CHECK_DIRECTIONAL_STABILITY_XPATH,
- CHECK_LATERAL_STABILITY_XPATH,
- CHECK_LONGITUDINAL_STABILITY_XPATH,
- STABILITY_AEROMAP_TO_ANALYZE_XPATH,
-)
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-# ===== Module Status =====
-# True if the module is active
-# False if the module is disabled (not working or not ready)
-module_status = True
+GUI Interface of StaticStability.
-# ===== Results directory path =====
-RESULTS_DIR = Path("Results", "Stability")
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+"""
-# ===== CPACS inputs and outputs =====
-
-cpacs_inout = CPACSInOut()
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+from ceasiompy.utils.moduleinterfaces import CPACSInOut
-# ===== Input =====
+from ceasiompy import log
-cpacs_inout.add_input(
- var_name="aeromap_to_analyze",
- var_type=list,
- default_value=None,
- unit=None,
- descr="Name of the aero map to evaluate",
- xpath=STABILITY_AEROMAP_TO_ANALYZE_XPATH,
- gui=True,
- gui_name="__AEROMAP_CHECKBOX",
- gui_group="Inputs",
-)
+from ceasiompy.StaticStability import include_gui
+from ceasiompy.utils.commonxpath import STATICSTABILITY_LR_XPATH
-cpacs_inout.add_input(
- var_name="longitudinal_stability",
- var_type=bool,
- default_value=True,
- unit="1",
- descr="Whether the longitudinal stability should the check or not.",
- xpath=CHECK_LONGITUDINAL_STABILITY_XPATH,
- gui=True,
- gui_name="Longitudinal",
- gui_group="Stability",
-)
+# ==============================================================================
+# VARIABLE
+# ==============================================================================
-cpacs_inout.add_input(
- var_name="directional_stability",
- var_type=bool,
- default_value=False,
- unit="1",
- descr="Whether the directional stability should the check or not.",
- xpath=CHECK_DIRECTIONAL_STABILITY_XPATH,
- gui=True,
- gui_name="Directional",
- gui_group="Stability",
-)
+cpacs_inout = CPACSInOut()
cpacs_inout.add_input(
- var_name="lateral_stability",
+ var_name="static_stability_linear_regression_bool",
var_type=bool,
default_value=False,
- unit="1",
- descr="Whether the lateral stability should the check or not.",
- xpath=CHECK_LATERAL_STABILITY_XPATH,
- gui=True,
- gui_name="Lateral",
- gui_group="Stability",
+ unit=None,
+ descr="Either use linear regression or directly the derivative's values",
+ xpath=STATICSTABILITY_LR_XPATH,
+ gui=include_gui,
+ gui_name="Linear Regression",
+ gui_group="Static Stability Settings",
)
+# =================================================================================================
+# MAIN
+# =================================================================================================
-# ===== Output =====
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/StaticStability/func/extractdata.py b/ceasiompy/StaticStability/func/extractdata.py
new file mode 100644
index 000000000..542a3a024
--- /dev/null
+++ b/ceasiompy/StaticStability/func/extractdata.py
@@ -0,0 +1,185 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Data extraction from pyAVL for stability analysis
+
+
+| Author: Leon Deligny
+| Creation: 2025-01-27
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+from ceasiompy.StaticStability.func.plot import plot_stability
+from ceasiompy.StaticStability.func.stabilitystatus import (
+ check_stability_lr,
+ check_stability_tangent,
+)
+
+from typing import List
+from pathlib import Path
+from pandas import DataFrame
+from tixi3.tixi3wrapper import Tixi3
+from cpacspy.cpacspy import (
+ CPACS,
+ AeroMap,
+)
+
+from ceasiompy import log
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def generate_stab_df(cpacs: CPACS, aeromap_uid: str, lr_bool: bool) -> DataFrame:
+ """
+ Generate the Markdownpy Table for
+ longitudinal/directional/lateral stability
+ to show in the results directory.
+
+ Args:
+ cpacs (CPACS object): CPACS file.
+ aeromap (AeroMap object): Chosen Aeromap.
+ lr_bool (bool): True if using Linear Regression.
+
+ Returns:
+ (DataFrame): Contains stability data for each combination of mach, alt, aoa, and aos.
+
+ """
+
+ tixi: Tixi3 = cpacs.tixi
+ aeromap: AeroMap = cpacs.get_aeromap_by_uid(aeromap_uid)
+
+ # Access correct xpath in CPACS file
+ increment_map_xpath = f"{aeromap.xpath}/incrementMaps/incrementMap"
+
+ # Create a DataFrame from the aeromap data
+ grouped = aeromap.df.groupby(["machNumber", "altitude", "angleOfAttack", "angleOfSideslip"])
+
+ # Check for more than two distinct lines in each group
+ for name, group in grouped:
+ if len(group) > 2:
+ raise ValueError(f"More than two distinct lines found for group: {name}")
+
+ df = grouped.first().reset_index()
+
+ # Rename columns
+ df.rename(columns={
+ "machNumber": "mach",
+ "altitude": "alt",
+ "angleOfAttack": "aoa",
+ "angleOfSideslip": "aos"
+ }, inplace=True)
+
+ # Extract values from CPACS
+ if not lr_bool:
+ log.info("Looking at the slopes of the stability derivatives.")
+
+ clb_values = tixi.getTextElement(f"{increment_map_xpath}/dcmd").split(";")
+ clb = [float(value) for value in clb_values]
+ cma_values = tixi.getTextElement(f"{increment_map_xpath}/dcms").split(";")
+ cma = [float(value) for value in cma_values]
+ cnb_values = tixi.getTextElement(f"{increment_map_xpath}/dcml").split(";")
+ cnb = [float(value) for value in cnb_values]
+
+ if not clb or not cma or not cnb:
+ raise ValueError("One of the stability derivative lists is empty.")
+
+ # Add the extracted values to the DataFrame
+ df["cma"] = cma
+ df["cnb"] = cnb
+ df["clb"] = clb
+
+ # Check stability for each row
+ (
+ df["long_stab"],
+ df["dir_stab"],
+ df["lat_stab"],
+ df["comment"],
+
+ ) = zip(*df.apply(
+ lambda row: check_stability_tangent(row["cma"], row["cnb"], row["clb"]),
+ axis=1,
+ )
+ )
+
+ return df[[
+ "mach", "alt", "aoa", "aos",
+ "cms", "cml", "cmd",
+ "cma", "clb", "cnb",
+ "long_stab", "dir_stab", "lat_stab",
+ "comment",
+ ]]
+
+ else:
+ log.info("Using Linear Regression to compute the stability derivatives.")
+ df = check_stability_lr(df)
+
+ return df[[
+ "mach", "alt", "aoa", "aos",
+ "cms", "cml", "cmd",
+ "lr_cma", "lr_cma_intercept",
+ "lr_clb", "lr_clb_intercept",
+ "lr_cnb", "lr_cnb_intercept",
+ "long_stab", "dir_stab", "lat_stab",
+ "comment",
+ ]]
+
+
+def generate_stab_table(
+ cpacs: CPACS,
+ aeromap_uid: str,
+ results_dir: Path,
+ lr_bool: bool,
+) -> List[List[str]]:
+ """
+ Generate the Markdownpy Table for the longitudinal/directional/lateral
+ stability to show in the results.
+
+ Args:
+ cpacs (CPACS object): CPACS file.
+
+ Returns:
+ (List[List[str]]): Contains stability data for each combination of mach, alt, aoa, and aos.
+
+ """
+
+ # Define what the stability table will contain
+ stability_table = [[
+ "mach", "alt", "aoa", "aos",
+ "long_stab",
+ "dir_stab",
+ "lat_stab",
+ "comment"
+ ]]
+
+ # Generate dataframe with necessary info
+ df = generate_stab_df(cpacs, aeromap_uid, lr_bool)
+
+ # Plot different stabilities
+ plot_stability(results_dir, df, lr_bool)
+
+ # Append the results to the stability_table
+ stability_table.extend(df[[
+ "mach", "alt", "aoa", "aos",
+ "long_stab",
+ "dir_stab",
+ "lat_stab",
+ "comment"
+ ]].values.tolist())
+
+ return stability_table
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/StaticStability/func/plot.py b/ceasiompy/StaticStability/func/plot.py
new file mode 100644
index 000000000..0cd7a1f84
--- /dev/null
+++ b/ceasiompy/StaticStability/func/plot.py
@@ -0,0 +1,256 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Scripts for plotting the results
+
+
+| Author: Leon Deligny
+| Creation: 2025-01-27
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+import secrets
+
+from pathlib import Path
+from pandas import DataFrame
+import plotly.graph_objects as go
+
+from typing import (
+ Dict,
+ Tuple,
+)
+
+from ceasiompy import log
+
+# =================================================================================================
+# CONSTANTS
+# =================================================================================================
+
+X_Y_DICT = {
+ "longitudinal_x": "aoa",
+ "longitudinal_x_prime": "aos",
+ "longitudinal_x_prime_short": "AoS",
+ "longitudinal_y": "cms",
+ "longitudinal_tangent": "cma",
+
+ "directional_x": "aos",
+ "directional_x_prime": "aoa",
+ "directional_x_prime_short": "AoA",
+ "directional_y": "cml",
+ "directional_tangent": "cnb",
+
+ "lateral_x": "aos",
+ "lateral_x_prime": "aoa",
+ "lateral_x_prime_short": "AoA",
+ "lateral_y": "cmd",
+ "lateral_tangent": "clb",
+}
+
+TITLES_DICT = {
+ "longitudinal_title": "Longitudinal Stability Plot with tangent",
+ "longitudinal_x_title": "Angle of Attack (deg)",
+ "longitudinal_y_title": "Pitch Moment",
+
+ "directional_title": "Directional Stability Plot with tangent",
+ "directional_x_title": "Angle of Sideslip (deg)",
+ "directional_y_title": "Yaw Moment",
+
+ "lateral_title": "Lateral Stability Plot with tangent",
+ "lateral_x_title": "Angle of Sideslip (deg)",
+ "lateral_y_title": "Roll Moment (deg)",
+}
+
+LR_TITLES_DICT = {
+ "longitudinal_title": "Longitudinal Stability Plot with Linear Regression",
+ "longitudinal_x_title": "Angle of Attack (deg)",
+ "longitudinal_y_title": "Pitch Moment",
+
+ "directional_title": "Directional Stability Plot with Linear Regression",
+ "directional_x_title": "Angle of Sideslip (deg)",
+ "directional_y_title": "Yaw Moment",
+
+ "lateral_title": "Lateral Stability Plot with Linear Regression",
+ "lateral_x_title": "Angle of Sideslip (deg)",
+ "lateral_y_title": "Roll Moment (deg)",
+}
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def generate_random_color() -> str:
+ """
+ Generate a random color in hexadecimal format.
+
+ Returns:
+ str: A string representing a random color in hexadecimal format.
+
+ """
+
+ return f'#{secrets.randbelow(0xFFFFFF + 1):06x}'
+
+
+def set_html_plot(results_dir: Path, df: DataFrame, axis: str) -> Tuple[Path, Dict]:
+ # Access where to store the plot
+ plot_path = Path(results_dir, f"Stability_{axis}_plot.html")
+
+ # Create a color map for different categories
+ unique_combinations = df[[
+ "alt",
+ "mach",
+ f"{X_Y_DICT[f'{axis}_x_prime']}"
+ ]].drop_duplicates()
+
+ color_map = {
+ tuple(row):
+ generate_random_color()
+ for row in unique_combinations.values
+ }
+
+ # Assign colors based on altitude, mach, and AoS
+ df['color'] = df.apply(lambda row: color_map[(
+ row["alt"], row["mach"], row[f"{X_Y_DICT[f'{axis}_x_prime']}"])], axis=1)
+
+ return plot_path, color_map
+
+
+def add_stability_plot_tangent(results_dir: Path, df: DataFrame, axis: str) -> None:
+ """
+ Adds a stability plot for the Tangent case.
+ """
+
+ plot_path, color_map = set_html_plot(results_dir, df, axis)
+
+ # Initial plot
+ fig = go.Figure()
+
+ # Add traces for each unique combination
+ for combination, group in df.groupby(["alt", "mach", f"{X_Y_DICT[f'{axis}_x_prime']}"]):
+ color = color_map[combination]
+ scatter_x = []
+ scatter_y = []
+
+ for _, row in group.iterrows():
+ scatter_x.append(row[f"{X_Y_DICT[f'{axis}_x']}"])
+ scatter_y.append(row[f"{X_Y_DICT[f'{axis}_y']}"])
+ alpha = row[f"{X_Y_DICT[f'{axis}_x']}"]
+ cms = row[f"{X_Y_DICT[f'{axis}_y']}"]
+ cma = row[f"{X_Y_DICT[f'{axis}_tangent']}"]
+ tangent_x = [alpha - 0.1, alpha + 0.1]
+ tangent_y = [cms - 0.1 * cma, cms + 0.1 * cma]
+ fig.add_trace(
+ go.Scatter(x=tangent_x, y=tangent_y, mode='lines',
+ line=dict(color=color), showlegend=True)
+ )
+ axis_label = X_Y_DICT[f'{axis}_x_prime_short']
+
+ scatter = go.Scatter(
+ x=scatter_x,
+ y=scatter_y,
+ mode='markers',
+ marker=dict(color=color),
+ name=f"Alt: {combination[0]}, Mach: {combination[1]}, {axis_label}: {combination[2]}"
+ )
+ fig.add_trace(scatter)
+
+ fig.update_layout(
+ title=f'{TITLES_DICT[axis + "_title"]}',
+ xaxis_title=f'{TITLES_DICT[axis + "_x_title"]}',
+ yaxis_title=f'{TITLES_DICT[axis + "_y_title"]}',
+ )
+
+ # Save the plot as an HTML file
+ fig.write_html(plot_path)
+
+
+def add_stability_plot_lr(results_dir: Path, df: DataFrame, axis: str) -> None:
+ """
+ Adds a stability plot for Linear Regression case.
+ """
+
+ plot_path, color_map = set_html_plot(results_dir, df, axis)
+
+ # Initial plot
+ fig = go.Figure()
+
+ # Add traces for each unique combination
+ for combination, group in df.groupby(["alt", "mach", f"{X_Y_DICT[f'{axis}_x_prime']}"]):
+ color = color_map[combination]
+ scatter_x = group[f"{X_Y_DICT[f'{axis}_x']}"].tolist()
+ scatter_y = group[f"{X_Y_DICT[f'{axis}_y']}"].tolist()
+
+ # Plot the scatter points
+ scatter = go.Scatter(
+ x=scatter_x,
+ y=scatter_y,
+ mode='markers',
+ marker=dict(color=color),
+ name=f"Alt: {combination[0]}, Mach: {combination[1]}, "
+ f"{X_Y_DICT[f'{axis}_x_prime_short']}: {combination[2]}"
+ )
+ fig.add_trace(scatter)
+
+ # Plot the linear regression line
+ lr_cma = group[f"lr_{X_Y_DICT[f'{axis}_tangent']}"].iloc[0]
+ lr_cma_intercept = group[f"lr_{X_Y_DICT[f'{axis}_tangent']}_intercept"].iloc[0]
+ line_x = [min(scatter_x), max(scatter_x)]
+ line_y = [lr_cma * x + lr_cma_intercept for x in line_x]
+ line = go.Scatter(
+ x=line_x,
+ y=line_y,
+ mode='lines',
+ line=dict(color=color),
+ showlegend=False
+ )
+ fig.add_trace(line)
+
+ fig.update_layout(
+ title=f'{LR_TITLES_DICT[axis + "_title"]}',
+ xaxis_title=f'{LR_TITLES_DICT[axis + "_x_title"]}',
+ yaxis_title=f'{LR_TITLES_DICT[axis + "_y_title"]}',
+ )
+
+ # Save the plot as an HTML file
+ fig.write_html(plot_path)
+
+
+def plot_stability(results_dir: Path, df: DataFrame, tangent_bool: bool) -> None:
+ """
+ Generate and save stability plots for longitudinal, directional, and lateral stability.
+
+ This function calls the add_stability_plot function
+ for each type of stability (longitudinal, directional, and lateral)
+ to generate and save the corresponding stability plots.
+
+ Args:
+ df (DataFrame): DataFrame containing the stability data.
+ tangent_bool (bool):
+ False if using slope derivatives.
+ True if using Linear Regression to compute the stability derivatives.
+
+ """
+
+ # Adding each plots independently
+ if not tangent_bool:
+ add_stability_plot_tangent(results_dir, df, "longitudinal")
+ add_stability_plot_tangent(results_dir, df, "directional")
+ add_stability_plot_tangent(results_dir, df, "lateral")
+ else:
+ add_stability_plot_lr(results_dir, df, "longitudinal")
+ add_stability_plot_lr(results_dir, df, "directional")
+ add_stability_plot_lr(results_dir, df, "lateral")
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/StaticStability/func/stabilitystatus.py b/ceasiompy/StaticStability/func/stabilitystatus.py
new file mode 100644
index 000000000..1ac8a9bf4
--- /dev/null
+++ b/ceasiompy/StaticStability/func/stabilitystatus.py
@@ -0,0 +1,211 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Scripts for checking the slope of the pitch, roll and yaw moments with respect to a certain angle
+
+
+| Author: Leon Deligny
+| Creation: 2025-01-27
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+from typing import Tuple
+from sklearn.linear_model import LinearRegression
+
+from pandas import (
+ Series,
+ DataFrame,
+)
+
+from ceasiompy import log
+from ceasiompy.StaticStability import STABILITY_DICT
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def generate_message(row: Series) -> str:
+ """
+ Generates a message indicating the stability of the aircraft
+ based on the stability status of longitudinal, directional, and lateral directions.
+
+ Args:
+ row (Series): Stability status for longitudinal, directional, and lateral directions.
+
+ Returns:
+ (str): Message indicating the stability of the aircraft.
+
+ """
+
+ if (
+ (row["long_stab"] == "Stable")
+ and (row["dir_stab"] == "Stable")
+ and (row["lat_stab"] == "Stable")
+ ):
+ return "Aircraft is stable along all axes."
+ elif (
+ (row["long_stab"] == "Unstable")
+ and (row["dir_stab"] == "Unstable")
+ and (row["lat_stab"] == "Unstable")
+ ):
+ return "Aircraft is UN-stable along ALL axes."
+ else:
+ msg = ""
+ if (row["long_stab"] == "Unstable"):
+ msg += "Aircraft is unstable for Longitudinal axis i.e. Cma >=0. "
+ if (row["dir_stab"] == "Unstable"):
+ msg += "Aircraft is unstable for Directional axis i.e. Cnb <=0. "
+ if (row["lat_stab"] == "Unstable"):
+ msg += "Aircraft is unstable for Lateral axis i.e. Clb >=0. "
+
+ return msg
+
+
+def compute_stability_cma(group: DataFrame) -> DataFrame:
+ """
+ Computes the longitudinal stability (cma) of an aircraft,
+ for a given group using linear regression.
+
+ Args:
+ group (DataFrame): Contains aerodynamic coefficients.
+
+ Returns:
+ Series: A series containing the stability status for the longitudinal direction.
+
+ """
+ X_aoa = group[["aoa"]].values.reshape(-1, 1)
+ y_cms = group["cms"].values
+
+ reg_cma = LinearRegression().fit(X_aoa, y_cms)
+ lr_cma = reg_cma.coef_[0]
+ lr_cma_intercept = reg_cma.intercept_
+
+ cma_stable = (lr_cma < 0)
+
+ return Series({
+ "lr_cma": lr_cma,
+ "lr_cma_intercept": lr_cma_intercept,
+ "long_stab": STABILITY_DICT[cma_stable],
+ })
+
+
+def compute_stability_cnb_clb(group: DataFrame) -> DataFrame:
+ """
+ Computes the directional (cnb) and lateral (clb) stability of the aircraft
+ for a given group using linear regression.
+
+ Args:
+ group (DataFrame): Aerodynamic coefficients for mach, altitude, and angle of attack.
+
+ Returns:
+ Series: Stability status for the directional and lateral directions.
+
+ """
+ # Linear regression for cnb (slope of cml to aos)
+ X_aos = group[["aos"]].values.reshape(-1, 1)
+ y_cml = group["cml"].values
+ reg_cnb = LinearRegression().fit(X_aos, y_cml)
+ lr_cnb = reg_cnb.coef_[0]
+ lr_cnb_intercept = reg_cnb.intercept_
+
+ # Linear regression for clb (slope of cmd to aos)
+ y_cmd = group["cmd"].values
+ reg_clb = LinearRegression().fit(X_aos, y_cmd)
+ lr_clb = reg_clb.coef_[0]
+ lr_clb_intercept = reg_cnb.intercept_
+
+ cnb_stable = (-lr_cnb < 0)
+ clb_stable = (lr_clb < 0)
+
+ return Series({
+ "lr_cnb": lr_cnb,
+ "lr_cnb_intercept": lr_cnb_intercept,
+ "lr_clb": lr_clb,
+ "lr_clb_intercept": lr_clb_intercept,
+ "dir_stab": STABILITY_DICT[cnb_stable],
+ "lat_stab": STABILITY_DICT[clb_stable],
+ })
+
+
+def check_stability_lr(df: DataFrame) -> DataFrame:
+ """
+ Using linear regression to check if the aircraft is stable or not.
+
+ Args:
+ df (DataFrame): Aerodynamic coefficients for mach, alt, aoa and aos.
+
+ Returns:
+ (DataFrame): Stability status for mach, alt, aoa and aos.
+
+ """
+
+ grouped_cma = df.groupby([
+ "mach",
+ "alt",
+ "aos",
+ ]).apply(compute_stability_cma, include_groups=False).reset_index()
+
+ grouped_cnb_clb = df.groupby([
+ "mach",
+ "alt",
+ "aoa",
+ ]).apply(compute_stability_cnb_clb, include_groups=False).reset_index()
+
+ # Merge grouped_cma and grouped_cnb_clb with the original df
+ df = df.merge(grouped_cma, on=["mach", "alt", "aos"], how="left")
+ df = df.merge(grouped_cnb_clb, on=["mach", "alt", "aoa"], how="left")
+
+ df["comment"] = df.apply(generate_message, axis=1)
+
+ return df
+
+
+def check_stability_tangent(cma: float, cnb: float, clb: float) -> Tuple[str, str, str, str]:
+ """
+ We use tangents at the distinct points to check if the aircraft is stable or not.
+
+ Args:
+ cma (float): Derivative of pitching moment with respect to the angle of attack.
+ cnb (float): Derivative of yawing moment with respect to the sideslip angle.
+ clb (float): Derivative of rolling moment with respect to the sideslip angle.
+
+ Returns:
+ (Tuple[str, str, str, str]): Stability status messages.
+
+ """
+
+ if cma is None or cnb is None or clb is None:
+ return (None, None, None), "Stability parameters are not well defined"
+
+ cma_stable = (cma < 0)
+ cnb_stable = (-cnb < 0)
+ clb_stable = (clb < 0)
+
+ cma_stable_str = STABILITY_DICT[cma_stable]
+ cnb_stable_str = STABILITY_DICT[cnb_stable]
+ clb_stable_str = STABILITY_DICT[clb_stable]
+
+ stability_dict = {
+ "long_stab": cma_stable_str,
+ "dir_stab": cnb_stable_str,
+ "lat_stab": clb_stable_str
+ }
+
+ msg = generate_message(Series(stability_dict))
+
+ return cma_stable_str, cnb_stable_str, clb_stable_str, msg
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/StaticStability/func/utils.py b/ceasiompy/StaticStability/func/utils.py
new file mode 100644
index 000000000..38eee4319
--- /dev/null
+++ b/ceasiompy/StaticStability/func/utils.py
@@ -0,0 +1,51 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Utility functions for Static stability module.
+
+
+| Author: Leon Deligny
+| Creation: 2025-01-27
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+from typing import List
+
+from markdownpy.markdownpy import (
+ Table,
+ MarkdownDoc,
+)
+
+from ceasiompy import log
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+def markdownpy_to_markdown(md: MarkdownDoc, table: List[List[str]]) -> None:
+ """
+ Writes a Markdownpy Table to a Markdown object.
+
+ Args:
+ table (Markdownpy Table): Table generated from 'generate_stab_table' function.
+
+
+ """
+ if len(table) > 1:
+ md.p(Table(table).write())
+ md.line()
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute.")
diff --git a/ceasiompy/StaticStability/staticstability.py b/ceasiompy/StaticStability/staticstability.py
index 1d75f9dc5..fbe850a74 100644
--- a/ceasiompy/StaticStability/staticstability.py
+++ b/ceasiompy/StaticStability/staticstability.py
@@ -5,12 +5,9 @@
Static stability module
-Python version: >=3.8
-| Author: Aidan Jungo
-| Creation: 2022-11-14
-
-TODO:
+| Author: Leon Deligny
+| Creation: 2025-01-27
"""
@@ -20,160 +17,50 @@
from pathlib import Path
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.ceasiompyutils import get_aeromap_list_from_xpath, get_results_directory
-from ceasiompy.utils.commonxpath import (
- CHECK_DIRECTIONAL_STABILITY_XPATH,
- CHECK_LATERAL_STABILITY_XPATH,
- CHECK_LONGITUDINAL_STABILITY_XPATH,
- STABILITY_AEROMAP_TO_ANALYZE_XPATH,
-)
-from ceasiompy.utils.moduleinterfaces import (
- check_cpacs_input_requirements,
- get_toolinput_file_path,
- get_tooloutput_file_path,
-)
-from cpacspy.cpacsfunctions import get_value_or_default
-from cpacspy.cpacspy import CPACS
-from markdownpy.markdownpy import MarkdownDoc, Table
-
-log = get_logger()
+from cpacspy.cpacsfunctions import get_value
+from ceasiompy.utils.ceasiompyutils import call_main
+from ceasiompy.StaticStability.func.utils import markdownpy_to_markdown
+from ceasiompy.StaticStability.func.extractdata import generate_stab_table
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
+from cpacspy.cpacspy import CPACS
+from markdownpy.markdownpy import MarkdownDoc
-STABILITY_DICT = {True: "Stable", False: "Unstable", None: "Not define"}
+from ceasiompy import log
+from ceasiompy.StaticStability import MODULE_NAME
+from ceasiompy.utils.commonxpath import STATICSTABILITY_LR_XPATH
# =================================================================================================
-# FUNCTIONS
+# MAIN
# =================================================================================================
-def generate_longitudinal_stab_table(aeromap):
- """Generate the Markdownpy Table for the longitudinal stability to show in the results.
-
- Args:
- aeromap (aeromap object): cpacspy aeromap object
- """
-
- stability_table = [["mach", "alt", "aos", "Longitudinal stability", "Comment"]]
-
- for (mach, alt, aos), _ in aeromap.df.groupby(["machNumber", "altitude", "angleOfSideslip"]):
- stable, msg = aeromap.check_longitudinal_stability(alt=alt, mach=mach, aos=aos)
-
- if stable is None:
- continue
-
- stability_table.append([str(mach), str(alt), str(aos), STABILITY_DICT[stable], msg])
-
- return stability_table
-
-
-def generate_directional_stab_table(aeromap):
- """Generate the Markdownpy Table for the directional stability to show in the results.
-
- Args:
- aeromap (aeromap object): cpacspy aeromap object
- """
-
- stability_table = [["mach", "alt", "aoa", "Directional stability", "Comment"]]
-
- for (mach, alt, aoa), _ in aeromap.df.groupby(["machNumber", "altitude", "angleOfAttack"]):
- stable, msg = aeromap.check_directional_stability(alt=alt, mach=mach, aoa=aoa)
-
- if stable is None:
- continue
-
- stability_table.append([str(mach), str(alt), str(aoa), STABILITY_DICT[stable], msg])
-
- return stability_table
-
-
-def generate_lateral_stab_table(aeromap):
- """Generate the Markdownpy Table for the lateral stability to show in the results.
-
- Args:
- aeromap (aeromap object): cpacspy aeromap object
+def main(cpacs: CPACS, wkdir: Path) -> None:
"""
-
- stability_table = [["mach", "alt", "aoa", "Lateral stability", "Comment"]]
-
- for (mach, alt, aoa), _ in aeromap.df.groupby(["machNumber", "altitude", "angleOfAttack"]):
- stable, msg = aeromap.check_lateral_stability(alt=alt, mach=mach, aoa=aoa)
-
- if stable is None:
- continue
-
- stability_table.append([str(mach), str(alt), str(aoa), STABILITY_DICT[stable], msg])
-
- return stability_table
-
-
-def static_stability_analysis(cpacs_path, cpacs_out_path):
- """Function 'static_stability_analysis' analyses longitudinal, directional and lateral
- stability.
+ Analyses longitudinal, directional and lateral stability
+ from the data of a CPACS file.
Args:
- cpacs_path (str): Path to CPACS file
- cpacs_out_path (str):Path to CPACS output file
+ cpacs (CPACS): Input CPACS file.
+ wkdir (str): Results directory.
"""
- cpacs = CPACS(cpacs_path)
-
- results_dir = get_results_directory("StaticStability")
- md = MarkdownDoc(Path(results_dir, "Static_stability.md"))
- md.h2("Static stability")
-
- for aeromap_uid in get_aeromap_list_from_xpath(cpacs, STABILITY_AEROMAP_TO_ANALYZE_XPATH):
-
- md.h4(f"Static stability of '{aeromap_uid}' aeromap")
- aeromap = cpacs.get_aeromap_by_uid(aeromap_uid)
-
- if get_value_or_default(cpacs.tixi, CHECK_LONGITUDINAL_STABILITY_XPATH, True):
-
- table = generate_longitudinal_stab_table(aeromap)
- if len(table) > 1:
- md.p(Table(table).write())
- md.line()
-
- if get_value_or_default(cpacs.tixi, CHECK_DIRECTIONAL_STABILITY_XPATH, False):
-
- table = generate_directional_stab_table(aeromap)
- if len(table) > 1:
- md.p(Table(table).write())
- md.line()
+ tixi = cpacs.tixi
+ md = MarkdownDoc(Path(wkdir, f"{MODULE_NAME}.md"))
+ md.h2(MODULE_NAME)
- if get_value_or_default(cpacs.tixi, CHECK_LATERAL_STABILITY_XPATH, False):
+ for aeromap_uid in cpacs.get_aeromap_uid_list():
+ if not str(aeromap_uid) == "aeromap_empty":
+ log_msg = f"Static stability of '{aeromap_uid}' aeromap."
+ log.info(log_msg)
+ md.h4(log_msg)
- table = generate_lateral_stab_table(aeromap)
- if len(table) > 1:
- md.p(Table(table).write())
- md.line()
+ lr_bool = get_value(tixi, STATICSTABILITY_LR_XPATH)
+ table = generate_stab_table(cpacs, aeromap_uid, wkdir, lr_bool)
+ markdownpy_to_markdown(md, table)
md.save()
- cpacs.save_cpacs(cpacs_out_path, overwrite=True)
-
-
-# =================================================================================================
-# MAIN
-# =================================================================================================
-
-
-def main(cpacs_path, cpacs_out_path):
-
- log.info("----- Start of " + MODULE_NAME + " -----")
-
- check_cpacs_input_requirements(cpacs_path)
-
- static_stability_analysis(cpacs_path, cpacs_out_path)
-
- log.info("----- End of " + MODULE_NAME + " -----")
if __name__ == "__main__":
-
- cpacs_path = get_toolinput_file_path(MODULE_NAME)
- cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
-
- main(cpacs_path, cpacs_out_path)
+ call_main(main, MODULE_NAME)
diff --git a/ceasiompy/StaticStability/tests/test_staticstability.py b/ceasiompy/StaticStability/tests/test_staticstability.py
index e865095c1..6e5610968 100644
--- a/ceasiompy/StaticStability/tests/test_staticstability.py
+++ b/ceasiompy/StaticStability/tests/test_staticstability.py
@@ -3,14 +3,11 @@
Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
-Test functions for 'StabilityStatic/staticstability.py'
+Test functions for StaticStability module.
-Python version: >=3.8
-| Author: Aidan Jungo
-| Creation: 2022-11-14
-
-TODO:
+| Author: Leon Deligny
+| Creation: 21 March 2025
"""
@@ -18,142 +15,141 @@
# IMPORTS
# =================================================================================================
-from pathlib import Path
+import unittest
-from ceasiompy.StaticStability.staticstability import (
- generate_directional_stab_table,
- generate_lateral_stab_table,
- generate_longitudinal_stab_table,
- static_stability_analysis,
+from ceasiompy.utils.decorators import log_test
+from cpacspy.cpacsfunctions import create_branch
+from ceasiompy.StaticStability.staticstability import generate_stab_table
+
+from ceasiompy.utils.ceasiompyutils import (
+ current_workflow_dir,
+ get_results_directory,
)
-from ceasiompy.utils.ceasiompyutils import get_results_directory
-from ceasiompy.utils.commonpaths import CPACS_FILES_PATH
-from cpacspy.cpacspy import CPACS
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
-CPACS_IN = Path(CPACS_FILES_PATH, "D150_simple.xml")
-CPACS_OUT_PATH = Path(MODULE_DIR, "D150_simple_staticstability_test_output.xml")
+from pathlib import Path
+from ceasiompy.utils.ceasiompytest import CeasiompyTest
+from cpacspy.cpacspy import (
+ CPACS,
+ AeroMap,
+)
+from ceasiompy import log
+from ceasiompy.utils.commonpaths import CPACS_FILES_PATH
-# =================================================================================================
-# CLASSES
-# =================================================================================================
+from ceasiompy.PyAVL import MODULE_NAME
# =================================================================================================
-# FUNCTIONS
+# CLASSES
# =================================================================================================
-
-class TestGenerateTable:
-
- cpacs = CPACS(CPACS_IN)
- aeromap_empty = cpacs.get_aeromap_by_uid("aeromap_empty")
- aeromap = cpacs.get_aeromap_by_uid("test_apm")
-
- def test_generate_longitudinal_stab_table(self):
- """Test function 'generate_longitudinal_stab_table'"""
-
- table = generate_longitudinal_stab_table(self.aeromap_empty)
- assert len(table) == 1
-
- table = generate_longitudinal_stab_table(self.aeromap)
- assert table == [
- ["mach", "alt", "aos", "Longitudinal stability", "Comment"],
- ["0.3", "0.0", "0.0", "Unstable", ""],
- ["0.3", "0.0", "10.0", "Unstable", ""],
- ]
-
- self.aeromap.add_row(mach=0.3, alt=0, aos=5.0, aoa=0, cd=0.001, cl=1.1, cs=0.22, cms=0.22)
- self.aeromap.add_row(mach=0.3, alt=0, aos=5.0, aoa=4, cd=0.001, cl=1.1, cs=0.22, cms=0.12)
- table = generate_longitudinal_stab_table(self.aeromap)
- assert table == [
- ["mach", "alt", "aos", "Longitudinal stability", "Comment"],
- ["0.3", "0.0", "0.0", "Unstable", ""],
- ["0.3", "0.0", "5.0", "Stable", ""],
- ["0.3", "0.0", "10.0", "Unstable", ""],
- ]
-
- def test_generate_directional_stab_table(self):
- """Test function 'generate_directional_stab_table'"""
-
- table = generate_directional_stab_table(self.aeromap_empty)
- assert len(table) == 1
-
- table = generate_directional_stab_table(self.aeromap)
- assert table == [
- ["mach", "alt", "aoa", "Directional stability", "Comment"],
- ["0.3", "0.0", "0.0", "Unstable", ""],
- ["0.3", "0.0", "10.0", "Unstable", ""],
- ]
-
- self.aeromap.add_row(mach=0.3, alt=0, aos=0.0, aoa=5.0, cd=0.001, cl=1.1, cml=-0.22)
- self.aeromap.add_row(mach=0.3, alt=0, aos=5.0, aoa=5.0, cd=0.001, cl=1.1, cml=0.12)
- table = generate_directional_stab_table(self.aeromap)
- assert table == [
- ["mach", "alt", "aoa", "Directional stability", "Comment"],
- ["0.3", "0.0", "0.0", "Unstable", ""],
- ["0.3", "0.0", "5.0", "Stable", ""],
- ["0.3", "0.0", "10.0", "Unstable", ""],
- ]
-
- def test_generate_lateral_stab_table(self):
- """Test function 'generate_lateral_stab_table'"""
-
- table = generate_lateral_stab_table(self.aeromap_empty)
- assert len(table) == 1
-
- table = generate_lateral_stab_table(self.aeromap)
- assert table == [
- ["mach", "alt", "aoa", "Lateral stability", "Comment"],
- ["0.3", "0.0", "0.0", "Unstable", ""],
- ["0.3", "0.0", "5.0", "Unstable", ""],
- ["0.3", "0.0", "10.0", "Unstable", ""],
- ]
-
- self.aeromap.add_row(mach=0.3, alt=0, aos=0.0, aoa=15.0, cd=0.001, cl=1.1, cmd=0.22)
- self.aeromap.add_row(mach=0.3, alt=0, aos=5.0, aoa=15.0, cd=0.001, cl=1.1, cmd=-0.12)
- self.aeromap.add_row(mach=0.4, alt=0, aos=0.0, aoa=15.0, cd=0.001, cl=1.1, cmd=0.01)
- self.aeromap.add_row(mach=0.4, alt=0, aos=5.0, aoa=15.0, cd=0.001, cl=1.1, cmd=0.01)
- table = generate_lateral_stab_table(self.aeromap)
- assert table == [
- ["mach", "alt", "aoa", "Lateral stability", "Comment"],
- ["0.3", "0.0", "0.0", "Unstable", ""],
- ["0.3", "0.0", "5.0", "Unstable", ""],
- ["0.3", "0.0", "10.0", "Unstable", ""],
- ["0.3", "0.0", "15.0", "Stable", ""],
- ["0.4", "0.0", "15.0", "Unstable", "Neutral stability"],
- ]
-
-
-def test_static_stability_analysis():
- """Test Function 'static_stability_analysis'"""
-
- results_dir = get_results_directory("StaticStability")
- result_markdown_file = Path(results_dir, "Static_stability.md")
-
- if result_markdown_file.exists():
- result_markdown_file.unlink()
-
- if CPACS_OUT_PATH.exists():
- CPACS_OUT_PATH.unlink()
-
- static_stability_analysis(CPACS_IN, CPACS_OUT_PATH)
-
- assert CPACS_OUT_PATH.exists()
- assert result_markdown_file.exists()
-
- if CPACS_OUT_PATH.exists():
- CPACS_OUT_PATH.unlink()
-
+class TestStaticStability(CeasiompyTest):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.cpacs_path = Path(CPACS_FILES_PATH, "D150_simple_test_static.xml")
+ cls.cpacs = CPACS(cls.cpacs_path)
+ cls.wkdir = current_workflow_dir()
+
+ cls.aeromap_empty: AeroMap = cls.cpacs.get_aeromap_by_uid("aeromap_empty")
+ cls.aeromap: AeroMap = cls.cpacs.get_aeromap_by_uid("test_apm")
+ tixi = cls.cpacs.tixi
+ cls.results_dir = get_results_directory(MODULE_NAME, True, cls.wkdir)
+
+ log.info(f"cls.aeromap {cls.aeromap}")
+
+ # Test directly values from derivatives
+ # Access correct xpath in CPACS file
+ increment_map_xpath = f"{cls.aeromap.xpath}/incrementMaps/incrementMap"
+
+ # Ensure the incrementMap elements exist
+ create_branch(tixi, f"{increment_map_xpath}/dcmd")
+ create_branch(tixi, f"{increment_map_xpath}/dcms")
+ create_branch(tixi, f"{increment_map_xpath}/dcml")
+
+ # Add some rows in test_apm aeromap
+ tixi.updateTextElement(f"{increment_map_xpath}/dcmd", "-0.002;0.002;-0.002;-0.002")
+ tixi.updateTextElement(f"{increment_map_xpath}/dcms", "-0.002;-0.002;-0.002;0.002")
+ tixi.updateTextElement(f"{increment_map_xpath}/dcml", "0.002;0.002;-0.002;0.002")
+
+ @log_test
+ def test_generate_stab_table(self) -> None:
+ act = generate_stab_table(self.cpacs, "test_apm", self.wkdir, True)
+ print("Actual Output:", act) # Debugging: Print the actual output
+
+ # Test Linear Regression
+ self.assert_equal_function(
+ f=generate_stab_table,
+ input_args=(self.cpacs, "test_apm", self.wkdir, True, ),
+ expected=([
+ [
+ "mach", "alt", "aoa", "aos",
+ "long_stab", "dir_stab", "lat_stab",
+ "comment"
+ ],
+ [
+ 0.3, 0.0, 0.0, 0.0,
+ "Stable", "Stable", "Unstable",
+ "Aircraft is unstable for Lateral axis i.e. Clb >=0. "
+ ],
+ [
+ 0.3, 0.0, 0.0, 10.0,
+ "Stable", "Stable", "Unstable",
+ "Aircraft is unstable for Lateral axis i.e. Clb >=0. "
+ ],
+ [
+ 0.3, 0.0, 10.0, 0.0,
+ "Stable", "Unstable", "Unstable",
+ "Aircraft is unstable for Directional axis "
+ "i.e. Cnb <=0. Aircraft is unstable for Lateral axis i.e. Clb >=0. "
+ ],
+ [
+ 0.3, 0.0, 10.0, 10.0,
+ "Stable", "Unstable", "Unstable",
+ "Aircraft is unstable for Directional axis "
+ "i.e. Cnb <=0. Aircraft is unstable for Lateral axis i.e. Clb >=0. "
+ ]
+ ])
+ )
+
+ # Test data
+ self.assert_equal_function(
+ f=generate_stab_table,
+ input_args=(self.cpacs, "test_apm", self.wkdir, False,),
+ expected=([
+ [
+ "mach", "alt", "aoa", "aos",
+ "long_stab", "dir_stab", "lat_stab",
+ "comment"
+ ],
+ [
+ 0.3, 0.0, 0.0, 0.0,
+ "Stable", "Stable", "Stable",
+ "Aircraft is stable along all axes."
+ ],
+ [
+ 0.3, 0.0, 0.0, 10.0,
+ "Stable", "Stable", "Unstable",
+ "Aircraft is unstable for Lateral axis i.e. Clb >=0. "
+ ],
+ [
+ 0.3, 0.0, 10.0, 0.0,
+ "Stable", "Unstable", "Stable",
+ "Aircraft is unstable for Directional axis i.e. Cnb <=0. "
+ ],
+ [
+ 0.3, 0.0, 10.0, 10.0,
+ "Unstable", "Stable", "Stable",
+ "Aircraft is unstable for Longitudinal axis i.e. Cma >=0. "
+ ]
+ ])
+
+ )
# =================================================================================================
# MAIN
# =================================================================================================
-if __name__ == "__main__":
- print("Running test_staticstability")
- print("To run test use the following command:")
- print(">> pytest -v")
+if __name__ == "__main__":
+ unittest.main(verbosity=0)
diff --git a/ceasiompy/ThermoData/README.md b/ceasiompy/ThermoData/README.md
index a4bd6082f..7b15ec31d 100644
--- a/ceasiompy/ThermoData/README.md
+++ b/ceasiompy/ThermoData/README.md
@@ -11,27 +11,27 @@
`ThermoData` is a module to provide the outlet conditions of a given engine. It can calculate different operating conditions and save the results in a text file. This module is derived starting from the OpenSource code [pyCycle](https://github.com/OpenMDAO/pycycle) developed by Eric S. Hendricks and Justin S. Gray. It can perform calculations on both turbojet and turbofan engines, with the possibility to customize the parameters. The main parameters that can be modified are: the rotational speed of the shaft/s, the temperature at the inlet of the turbine/s, the efficiency of compressor/s and turbine/s (HP and LP) and the compressor/s pressure ratio. The results are automatically written inside the configuration file of the module `SU2Run` to be able to perform the CFD calculations if needed.
## Inputs
-`ThermoData` can be run on is own by giving an Aeromap that contains Altitude and Mach number with the addition of the Net force that needs to be chosen. Otherwise it can take as an input the values from `CPACS2GMSH` module.
+`ThermoData` can be run on is own by giving an Aeromap that contains Altitude and Mach number with the addition of the Net force that needs to be chosen. Otherwise it can take as an input the values from `CPACS2GMSH` module.
## Analyses
-`ThermoData` compute the values obtained at the engine outlet giving a "EngineBC.dat" file as an output. If the workflow continues with the `SU2Run` run module the results are added to the config file of SU2 to perform the simulation.
+`ThermoData` compute the values obtained at the engine outlet giving a "EngineBC.dat" file as an output. If the workflow continues with the `SU2Run` run module the results are added to the config file of SU2 to perform the simulation.
## Outputs
`ThermoData` output is the "EngineBC.dat" file with stored inside for the turbojet engine:
-* T_tot_out= Nozzle total temperature outlet[K],
-* T_stat_out= Nozzle static temperature outlet[K],
+* T_tot_out= Nozzle total temperature outlet[K],
+* T_stat_out= Nozzle static temperature outlet[K],
* P_tot_out= Nozzle total pressure outlet [Pa],
-* P_stat_out= Nozzle static pressure outlet [Pa].
+* P_stat_out= Nozzle static pressure outlet [Pa].
* V_stat_out= Nozzle static velocity outlet[m/s],
-* MN_out= Nozzle Mach number outlet [adim],
+* MN_out= Nozzle Mach number outlet [adim],
* massflow_stat_out= Nozzle massflow outlet [Kg/s]\\
-For the turbofan engine are added also the values at the exit of the bypass nozzle.
+For the turbofan engine are added also the values at the exit of the bypass nozzle.
## Installation or requirements
-`ThermoData` needs the installation of the openMDAO and pycycle suite that are included in the python environment of CEASIOMpy.
+`ThermoData` needs the installation of the openMDAO and pycycle suite that are included in the python environment of CEASIOMpy.
## Limitations
diff --git a/ceasiompy/ThermoData/__init__.py b/ceasiompy/ThermoData/__init__.py
new file mode 100644
index 000000000..e86a52394
--- /dev/null
+++ b/ceasiompy/ThermoData/__init__.py
@@ -0,0 +1,42 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for ThermoData module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+from ceasiompy import log
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = True
+
+# ===== Include GUI =====
+include_gui = True
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/ThermoData/__specs__.py b/ceasiompy/ThermoData/__specs__.py
index 7fe6bb9b5..bce2d60d2 100644
--- a/ceasiompy/ThermoData/__specs__.py
+++ b/ceasiompy/ThermoData/__specs__.py
@@ -1,27 +1,41 @@
-from pathlib import Path
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+GUI Interface of ThermoData.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
from ceasiompy.utils.moduleinterfaces import CPACSInOut
+
+from ceasiompy import log
+from ceasiompy.ThermoData import include_gui
+
from ceasiompy.utils.commonxpath import (
+ RANGE_XPATH,
+ ENGINE_TYPE_XPATH,
SU2_FIXED_CL_XPATH,
SU2_TARGET_CL_XPATH,
- ENGINE_TYPE_XPATH,
- RANGE_XPATH,
)
-# ===== Module Status =====
-# True if the module is active
-# False if the module is disabled (not working or not ready)
-module_status = True
-
-# ===== Results directory path =====
-
-RESULTS_DIR = Path("Results", "Thermodata")
-
-# ===== CPACS inputs and outputs =====
+# ==============================================================================
+# VARIABLE
+# ==============================================================================
cpacs_inout = CPACSInOut()
-# ===== Input =====
-
+# ==============================================================================
+# GUI INPUTS
+# ==============================================================================
cpacs_inout.add_input(
var_name="net_force",
@@ -30,7 +44,7 @@
unit="N",
descr="Engine net force",
xpath=RANGE_XPATH + "/NetForce",
- gui=True,
+ gui=include_gui,
gui_name="NetForce",
gui_group="Cruise",
)
@@ -42,13 +56,15 @@
unit=None,
descr="0: TBJ, 1: TBF ",
xpath=ENGINE_TYPE_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="0 for Turbojet 1 for Turbofan",
gui_group="User inputs",
)
+# ==============================================================================
+# GUI OUTPUTS
+# ==============================================================================
-# ===== Output =====
cpacs_inout.add_output(
var_name="target_cl",
@@ -65,3 +81,10 @@
descr="FIXED_CL_MODE parameter for SU2",
xpath=SU2_FIXED_CL_XPATH,
)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/ThermoData/func/turbofan_func.py b/ceasiompy/ThermoData/func/turbofan.py
similarity index 97%
rename from ceasiompy/ThermoData/func/turbofan_func.py
rename to ceasiompy/ThermoData/func/turbofan.py
index 27481771f..0c18db446 100644
--- a/ceasiompy/ThermoData/func/turbofan_func.py
+++ b/ceasiompy/ThermoData/func/turbofan.py
@@ -5,23 +5,18 @@
Function to run the PyCycle code for the turbofan engine
-Python version: >=3.8
| Author: Francesco Marcucci
| Creation: 2023-12-12
"""
import sys
-
import openmdao.api as om
-
import pycycle.api as pyc
from scipy.constants import convert_temperature
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
+from ceasiompy import log
# =================================================================================================
# FUNCTIONS
@@ -278,7 +273,8 @@ def viewer(prob, pt, file=sys.stdout):
flush=True,
)
print(
- " %7.5f %7.1f %7.3f %7.1f %7.1f %7.1f %7.3f %7.5f %7.3f" % summary_data,
+ " %7.5f %7.1f %7.3f %7.1f %7.1f %7.1f %7.3f %7.5f %7.3f"
+ % summary_data,
file=file,
flush=True,
)
@@ -422,3 +418,11 @@ def write_hbtf_file(
log.info("hbtf.dat file generated!")
return file
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info('Nothing to execute.')
diff --git a/ceasiompy/ThermoData/func/turbojet_func.py b/ceasiompy/ThermoData/func/turbojet.py
similarity index 98%
rename from ceasiompy/ThermoData/func/turbojet_func.py
rename to ceasiompy/ThermoData/func/turbojet.py
index 1e02b3550..042039745 100644
--- a/ceasiompy/ThermoData/func/turbojet_func.py
+++ b/ceasiompy/ThermoData/func/turbojet.py
@@ -5,7 +5,6 @@
Function to run the PyCycle code for the turbojet engine
-Python version: >=3.8
| Author: Francesco Marcucci
| Creation: 2023-12-12
@@ -18,9 +17,8 @@
from scipy.constants import convert_temperature
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
-log = get_logger()
# =================================================================================================
# FUNCTIONS
diff --git a/ceasiompy/ThermoData/tests/test_thermodata.py b/ceasiompy/ThermoData/tests/test_thermodata.py
index 38ba491a7..c9e791f32 100644
--- a/ceasiompy/ThermoData/tests/test_thermodata.py
+++ b/ceasiompy/ThermoData/tests/test_thermodata.py
@@ -5,7 +5,6 @@
Test functions of 'ceasiompy/ThermoData/func/turbojet_func.py'
-Python version: >=3.8
| Author : Francesco Marcucci
@@ -16,28 +15,20 @@
# IMPORTS
# =================================================================================================
-from pathlib import Path
-
-import sys
-
import numpy as np
-from ceasiompy.ThermoData.func.turbojet_func import (
+from pathlib import Path
+
+from ceasiompy.ThermoData.func.turbojet import (
turbojet_analysis,
write_turbojet_file,
)
-from ceasiompy.ThermoData.func.turbofan_func import (
+from ceasiompy.ThermoData.func.turbofan import (
turbofan_analysis,
write_hbtf_file,
)
-sys.path.append("/home/cfse/Stage_Francesco/Thermodata")
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
# =================================================================================================
# FUNCTIONS
@@ -171,7 +162,6 @@ def test_write_hbtf_file(tmp_path):
# =================================================================================================
if __name__ == "__main__":
-
print("Test ThermoData")
print("To run test use the following command:")
print(">> pytest -v")
diff --git a/ceasiompy/ThermoData/thermodata.py b/ceasiompy/ThermoData/thermodata.py
index 9b31e3b5c..69f8b78cb 100644
--- a/ceasiompy/ThermoData/thermodata.py
+++ b/ceasiompy/ThermoData/thermodata.py
@@ -15,59 +15,47 @@
# =================================================================================================
import shutil
+
from pathlib import Path
-from ceasiompy.ThermoData.func.turbofan_func import (
- turbofan_analysis,
+from ceasiompy.utils.ceasiompyutils import call_main
+
+from ceasiompy.ThermoData.func.turbofan import (
write_hbtf_file,
+ turbofan_analysis,
)
-
-from ceasiompy.ThermoData.func.turbojet_func import (
+from ceasiompy.ThermoData.func.turbojet import (
turbojet_analysis,
write_turbojet_file,
)
+from cpacspy.cpacsfunctions import (
+ create_branch,
+ add_float_vector,
+ get_value_or_default,
+)
-from ceasiompy.utils.ceasiomlogger import get_logger
+from cpacspy.cpacspy import CPACS
+
+from ceasiompy import log
+from ceasiompy.utils.commonnames import ENGINE_BOUNDARY_CONDITIONS
-from ceasiompy.utils.moduleinterfaces import (
- get_toolinput_file_path,
- get_tooloutput_file_path,
-)
-from ceasiompy.utils.ceasiompyutils import get_results_directory
from ceasiompy.utils.commonxpath import (
- ENGINE_TYPE_XPATH,
ENGINE_BC,
RANGE_XPATH,
+ ENGINE_TYPE_XPATH,
SU2_AEROMAP_UID_XPATH,
+ RANGE_CRUISE_ALT_XPATH,
+ RANGE_CRUISE_MACH_XPATH,
)
-from ceasiompy.utils.commonnames import (
- ENGINE_BOUNDARY_CONDITIONS,
-)
-from cpacspy.cpacsfunctions import (
- get_value_or_default,
- add_float_vector,
- create_branch,
-)
-from cpacspy.cpacspy import CPACS
-
-
-log = get_logger()
-
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
-
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
+from ceasiompy.ThermoData import MODULE_NAME
# =================================================================================================
# FUNCTIONS
# =================================================================================================
-def thermo_data_run(cpacs_path, cpacs_out_path, wkdir):
+def main(cpacs: CPACS, wkdir: Path) -> None:
"""Running the PyCycle code by choosing between turbojet or turbofan engine
Args
@@ -76,23 +64,18 @@ def thermo_data_run(cpacs_path, cpacs_out_path, wkdir):
wkdir (str): Path to the working directory
"""
- if not wkdir.exists():
- raise OSError(f"The working directory : {wkdir} does not exit!")
-
- cpacs = CPACS(cpacs_path)
tixi = cpacs.tixi
-
Fn = get_value_or_default(tixi, RANGE_XPATH + "/NetForce", 2000)
default_aeromap = cpacs.create_aeromap("DefaultAeromap")
default_aeromap.description = "AeroMap created automatically"
- mach = get_value_or_default(cpacs.tixi, RANGE_XPATH + "/cruiseMach", 0.3)
- alt = get_value_or_default(cpacs.tixi, RANGE_XPATH + "/cruiseAltitude", 10000)
+ mach = get_value_or_default(tixi, RANGE_CRUISE_MACH_XPATH, 0.3)
+ alt = get_value_or_default(tixi, RANGE_CRUISE_ALT_XPATH, 10000)
default_aeromap.add_row(alt=alt, mach=mach, aos=0.0, aoa=0.0)
default_aeromap.save()
alt_list = [alt]
mach_list = [mach]
- aeromap_uid = get_value_or_default(cpacs.tixi, SU2_AEROMAP_UID_XPATH, "DefaultAeromap")
+ aeromap_uid = get_value_or_default(tixi, SU2_AEROMAP_UID_XPATH, "DefaultAeromap")
log.info(f"{aeromap_uid} has been created")
aeromap_list = cpacs.get_aeromap_uid_list()
@@ -100,7 +83,7 @@ def thermo_data_run(cpacs_path, cpacs_out_path, wkdir):
if aeromap_list:
aeromap_default = aeromap_list[0]
log.info(f"The aeromap is {aeromap_default}")
- aeromap_uid = get_value_or_default(cpacs.tixi, SU2_AEROMAP_UID_XPATH, aeromap_default)
+ aeromap_uid = get_value_or_default(tixi, SU2_AEROMAP_UID_XPATH, aeromap_default)
activate_aeromap = cpacs.get_aeromap_by_uid(aeromap_uid)
alt_list = activate_aeromap.get("altitude").tolist()
mach_list = activate_aeromap.get("machNumber").tolist()
@@ -121,7 +104,7 @@ def thermo_data_run(cpacs_path, cpacs_out_path, wkdir):
f = open(EngineBC, "w")
engine_type = get_value_or_default(tixi, ENGINE_TYPE_XPATH, 0)
- create_branch(cpacs.tixi, ENGINE_BC)
+ create_branch(tixi, ENGINE_BC)
if engine_type == 0:
(
@@ -186,32 +169,13 @@ def thermo_data_run(cpacs_path, cpacs_out_path, wkdir):
add_float_vector(tixi, ENGINE_BC + "/temperatureOutlet", T_tot_out_array)
add_float_vector(tixi, ENGINE_BC + "/pressureOutlet", P_tot_out_array)
- cpacs.save_cpacs(cpacs_out_path, overwrite=True)
+ folder_name = "reports"
+ shutil.rmtree(folder_name)
# =================================================================================================
# MAIN
# =================================================================================================
-
-def main(cpacs_path, cpacs_out_path):
-
- log.info("----- Start of " + MODULE_NAME + " -----")
-
- results_dir = get_results_directory("ThermoData")
-
- thermo_data_run(cpacs_path, cpacs_out_path, results_dir)
-
- folder_name = "reports"
-
- shutil.rmtree(folder_name)
-
- log.info("----- End of " + MODULE_NAME + " -----")
-
-
if __name__ == "__main__":
-
- cpacs_path = get_toolinput_file_path(MODULE_NAME)
- cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
-
- main(cpacs_path, cpacs_out_path)
+ call_main(main, MODULE_NAME)
diff --git a/ceasiompy/WeightConventional/README.md b/ceasiompy/WeightConventional/README.md
index 67ba96572..4d0453346 100644
--- a/ceasiompy/WeightConventional/README.md
+++ b/ceasiompy/WeightConventional/README.md
@@ -24,7 +24,7 @@ Example of workflow with the `WeightConventional` module:
```mermaid
graph LR;
CPACSCreator-->WeightConventional;
- WeightConventional-->PyTornado;
+ WeightConventional-->PyAVL;
```
## Inputs
diff --git a/ceasiompy/WeightConventional/ToolInput/.keep b/ceasiompy/WeightConventional/ToolInput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/WeightConventional/ToolInput/.keep
+++ b/ceasiompy/WeightConventional/ToolInput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/WeightConventional/ToolOutput/.keep b/ceasiompy/WeightConventional/ToolOutput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/WeightConventional/ToolOutput/.keep
+++ b/ceasiompy/WeightConventional/ToolOutput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/WeightConventional/__init__.py b/ceasiompy/WeightConventional/__init__.py
index e69de29bb..3269608b5 100644
--- a/ceasiompy/WeightConventional/__init__.py
+++ b/ceasiompy/WeightConventional/__init__.py
@@ -0,0 +1,45 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for WeightConventional module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+from ceasiompy import log
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = True
+
+# ===== Include GUI =====
+include_gui = True
+
+# ===== Add a Results Directory =====
+RES_DIR = False
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/WeightConventional/__specs__.py b/ceasiompy/WeightConventional/__specs__.py
index a3e6c68ae..c0a4378c3 100644
--- a/ceasiompy/WeightConventional/__specs__.py
+++ b/ceasiompy/WeightConventional/__specs__.py
@@ -1,48 +1,60 @@
-from pathlib import Path
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+GUI Interface of WeightConventional.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
from ceasiompy.utils.moduleinterfaces import CPACSInOut
+
+from ceasiompy import log
+from ceasiompy.WeightConventional import include_gui
+
from ceasiompy.utils.commonxpath import (
- FUEL_MASS_XPATH,
FUEL_XPATH,
GEOM_XPATH,
- MASS_CARGO_XPATH,
- MASSBREAKDOWN_XPATH,
MOEM_XPATH,
MTOM_XPATH,
MZFM_XPATH,
+ PROP_XPATH,
+ WB_ROW_NB_XPATH,
+ FUEL_MASS_XPATH,
+ WB_CREW_NB_XPATH,
+ MASS_CARGO_XPATH,
+ WB_TOILET_NB_XPATH,
PAYLOAD_MASS_XPATH,
+ WB_CREW_MASS_XPATH,
WB_ABREAST_NB_XPATH,
+ MASSBREAKDOWN_XPATH,
WB_CAB_CREW_NB_XPATH,
- WB_CREW_MASS_XPATH,
- WB_CREW_NB_XPATH,
- PROP_XPATH,
+ WB_MAX_PAYLOAD_XPATH,
+ WB_PEOPLE_MASS_XPATH,
WB_DOUBLE_FLOOR_XPATH,
WB_MAX_FUEL_VOL_XPATH,
- WB_MAX_PAYLOAD_XPATH,
- WB_PASSENGER_MASS_XPATH,
WB_PASSENGER_NB_XPATH,
- WB_PEOPLE_MASS_XPATH,
- WB_ROW_NB_XPATH,
- WB_TOILET_NB_XPATH,
+ WB_PASSENGER_MASS_XPATH,
)
-# ===== Module Status =====
-# True if the module is active
-# False if the module is disabled (not working or not ready)
-module_status = True
-
-# ===== Results directory path =====
-
-RESULTS_DIR = Path("Results", "WeightAndBalance")
-
-# ===== CPACS inputs and outputs =====
+# ==============================================================================
+# VARIABLE
+# ==============================================================================
cpacs_inout = CPACSInOut()
+# ==============================================================================
+# GUI INPUTS
+# ==============================================================================
-# ----- Input -----
-
-# User inputs ----
cpacs_inout.add_input(
var_name="is_double_floor",
var_type=list,
@@ -50,7 +62,7 @@
unit=None,
descr="0: no 2nd floor, 1: full 2nd floor (A380), 2: half 2nd floor (B747)",
xpath=WB_DOUBLE_FLOOR_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Double deck",
gui_group="User inputs",
)
@@ -62,7 +74,7 @@
unit="[kg]",
descr="Maximum payload allowed, set 0 if equal to max passenger mass.",
xpath=WB_MAX_PAYLOAD_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Max payload",
gui_group="User inputs",
)
@@ -74,7 +86,7 @@
unit="[l]",
descr="Maximum fuel volume allowed [l]",
xpath=WB_MAX_FUEL_VOL_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Max Fuel volum",
gui_group="User inputs",
)
@@ -86,7 +98,7 @@
unit="[kg]",
descr="Cargo mass [kg]",
xpath=MASS_CARGO_XPATH,
- gui=True,
+ gui=include_gui,
gui_name="Mass cargo",
gui_group="User inputs",
)
@@ -98,7 +110,7 @@
unit="[kg/m^3]",
descr="Fuel density [kg/m^3]",
xpath=FUEL_XPATH + "/density",
- gui=True,
+ gui=include_gui,
gui_name="Fuel density",
gui_group="User inputs",
)
@@ -110,7 +122,7 @@
unit=None,
descr='"True" only if the aircraft is a turboprop',
xpath=PROP_XPATH + "/turboprop",
- gui=True,
+ gui=include_gui,
gui_name="Turboprop",
gui_group="User inputs",
)
@@ -122,14 +134,12 @@
unit="[%]",
descr="Fuselage thickness, percentage of fuselage width",
xpath=GEOM_XPATH + "/fuseThick",
- gui=True,
+ gui=include_gui,
gui_name="Fuselage thickness",
gui_group="Fuselage",
)
-
# InsideDimensions ---
-
cpacs_inout.add_input(
var_name="seat_length",
var_type=float,
@@ -137,7 +147,7 @@
unit="[m]",
descr="Seats length",
xpath=GEOM_XPATH + "/seatLength",
- gui=True,
+ gui=include_gui,
gui_name="Seat length",
gui_group="Inside dimension",
)
@@ -149,7 +159,7 @@
unit="[m]",
descr="Seats width",
xpath=GEOM_XPATH + "/seatWidth",
- gui=True,
+ gui=include_gui,
gui_name="Seat width",
gui_group="Inside dimension",
)
@@ -161,7 +171,7 @@
unit="[m]",
descr="Aisles width",
xpath=GEOM_XPATH + "/aisleWidth",
- gui=True,
+ gui=include_gui,
gui_name="Aisles width",
gui_group="Inside dimension",
)
@@ -173,7 +183,7 @@
unit="[m]",
descr="Common space length",
xpath=GEOM_XPATH + "/toiletLength",
- gui=True,
+ gui=include_gui,
gui_name="Toilet length",
gui_group="Inside dimension",
)
@@ -232,7 +242,9 @@
# Mass of a single mounted engine (Dry Weight) Belongs to the EngineData class
-# ----- Output -----
+# ==============================================================================
+# GUI OUTPUTS
+# ==============================================================================
cpacs_inout.add_output(
var_name="mtom",
@@ -274,7 +286,6 @@
xpath=PAYLOAD_MASS_XPATH,
)
-
cpacs_inout.add_output(
var_name="mass_fuel_max",
default_value=None,
@@ -283,14 +294,6 @@
xpath=MASSBREAKDOWN_XPATH + "/fuel/massDescription/mass",
)
-# cpacs_inout.add_output(
-# var_name="mass_cargo",
-# default_value=None,
-# unit="[kg]",
-# descr="xtra payload mass in case of max fuel and total mass less than MTOM",
-# xpath=MASSBREAKDOWN_XPATH + "/mCargo/massDescription/massCargo",
-# )
-
cpacs_inout.add_output(
var_name="passenger_nb",
default_value=None,
@@ -362,3 +365,10 @@
descr="Number of toilets",
xpath=WB_TOILET_NB_XPATH,
)
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/WeightConventional/files/Master_Thesis_report_Stefano_Piccini.pdf b/ceasiompy/WeightConventional/files/Master_Thesis_report_Stefano_Piccini.pdf
index bdd531f4d..b90655798 100644
Binary files a/ceasiompy/WeightConventional/files/Master_Thesis_report_Stefano_Piccini.pdf and b/ceasiompy/WeightConventional/files/Master_Thesis_report_Stefano_Piccini.pdf differ
diff --git a/ceasiompy/WeightConventional/func/cabin.py b/ceasiompy/WeightConventional/func/cabin.py
index 0be86fa8e..629df5ce4 100644
--- a/ceasiompy/WeightConventional/func/cabin.py
+++ b/ceasiompy/WeightConventional/func/cabin.py
@@ -5,7 +5,6 @@
This script estimates all the cabin related parameters (Crew, passenger, equipment, etc.)
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2022-06-01
@@ -18,7 +17,7 @@
import math
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from ceasiompy.utils.commonxpath import (
WB_ABREAST_NB_XPATH,
WB_AISLE_WIDTH_XPATH,
@@ -42,8 +41,6 @@
)
from cpacspy.cpacsfunctions import add_value, get_value_or_default
-log = get_logger()
-
# =================================================================================================
# CLASSES
@@ -238,4 +235,4 @@ def stringed_seat_row(abreast_nb):
if __name__ == "__main__":
- print("Nothing to execute!")
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/WeightConventional/func/masses.py b/ceasiompy/WeightConventional/func/masses.py
index 9174d28e8..66d75912b 100644
--- a/ceasiompy/WeightConventional/func/masses.py
+++ b/ceasiompy/WeightConventional/func/masses.py
@@ -5,7 +5,6 @@
This script store all the aircraft masses ...
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2022-06-01
@@ -19,7 +18,7 @@
from ceasiompy.WeightConventional.func.mtom import estimate_mtom
from ceasiompy.WeightConventional.func.oem import estimate_oem
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from ceasiompy.utils.commonxpath import (
FUEL_MASS_XPATH,
MASS_CARGO_XPATH,
@@ -33,8 +32,6 @@
from cpacspy.cpacsfunctions import add_value, get_value_or_default
-log = get_logger()
-
# =================================================================================================
# CLASSES
@@ -166,22 +163,17 @@ def write_masses_output(self, masses_output_file):
lines.write(f"\n- Maximum fuel mass with no passengers : {int(self.mass_fuel_max)} [kg]")
lines.write(
"\n- Maximum fuel volume with no passengers:"
- f"{int(self.mass_fuel_max / self.fuel_density *1000)} [l]"
+ f"{int(self.mass_fuel_max / self.fuel_density * 1000)} [l]"
)
lines.write(f"\n- Wing loading: {int(self.wing_loading)} [kg/m^2]")
lines.close()
-
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
-
-
# =================================================================================================
# MAIN
# =================================================================================================
+
if __name__ == "__main__":
- print("Nothing to execute!")
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/WeightConventional/func/mtom.py b/ceasiompy/WeightConventional/func/mtom.py
index 589c6dd5c..53c63741b 100644
--- a/ceasiompy/WeightConventional/func/mtom.py
+++ b/ceasiompy/WeightConventional/func/mtom.py
@@ -6,7 +6,6 @@
This script estimates the maximum take of mass from a database
of conventional aircraft using the k-nearest neighbors regression method.
-Python version: >=3.8
| Author : Aidan Jungo
| Date of creation: 2022-11-08
@@ -22,12 +21,11 @@
import matplotlib.pyplot as plt
import pandas as pd
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from ceasiompy.utils.commonnames import MTOM_FIGURE_NAME
from ceasiompy.utils.commonpaths import MODULES_DIR_PATH
from sklearn.neighbors import KNeighborsRegressor
-log = get_logger()
# =================================================================================================
# CLASSES
@@ -121,4 +119,4 @@ def estimate_mtom(fuselage_length, fuselage_width, wing_area, wing_span, results
if __name__ == "__main__":
- print("Nothing to execute!")
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/WeightConventional/func/oem.py b/ceasiompy/WeightConventional/func/oem.py
index dd3ffc256..fc02c9bef 100644
--- a/ceasiompy/WeightConventional/func/oem.py
+++ b/ceasiompy/WeightConventional/func/oem.py
@@ -5,7 +5,6 @@
Function to evaluate the Oprating Empty Mass (OEM) from the maximum take of mass.
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-09-27
@@ -17,9 +16,7 @@
# IMPORTS
# =============================================================================
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
+from ceasiompy import log
# =================================================================================================
@@ -85,4 +82,4 @@ def estimate_oem(mtom, fuse_length, wing_span, turboprop):
if __name__ == "__main__":
- print("Nothing to execute!")
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/WeightConventional/func/weightutils.py b/ceasiompy/WeightConventional/func/weightutils.py
index 28dd92da0..d6ed0756b 100644
--- a/ceasiompy/WeightConventional/func/weightutils.py
+++ b/ceasiompy/WeightConventional/func/weightutils.py
@@ -5,7 +5,6 @@
Functions and variables for CPACS2GMSH
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2022-05-24
diff --git a/ceasiompy/WeightConventional/tests/test_cabin.py b/ceasiompy/WeightConventional/tests/test_cabin.py
index 21b44cd23..27d75083c 100644
--- a/ceasiompy/WeightConventional/tests/test_cabin.py
+++ b/ceasiompy/WeightConventional/tests/test_cabin.py
@@ -5,7 +5,6 @@
Test functions for 'ceasiompy/WeightConventional/func/cabin.py'
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2022-06-01
diff --git a/ceasiompy/WeightConventional/tests/test_mtom.py b/ceasiompy/WeightConventional/tests/test_mtom.py
index 4c39ac2e3..2951955e8 100644
--- a/ceasiompy/WeightConventional/tests/test_mtom.py
+++ b/ceasiompy/WeightConventional/tests/test_mtom.py
@@ -5,7 +5,6 @@
Test functions for 'ceasiompy/WeightConventional/func/mtom.py'
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2022-05-30
diff --git a/ceasiompy/WeightConventional/tests/test_oem.py b/ceasiompy/WeightConventional/tests/test_oem.py
index 74c53f06c..78838e490 100644
--- a/ceasiompy/WeightConventional/tests/test_oem.py
+++ b/ceasiompy/WeightConventional/tests/test_oem.py
@@ -5,7 +5,6 @@
Test functions for 'ceasiompy/WeightConventional/func/mtom.py'
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2022-05-30
diff --git a/ceasiompy/WeightConventional/weightconventional.py b/ceasiompy/WeightConventional/weightconventional.py
index 96b73b41c..daad7139f 100644
--- a/ceasiompy/WeightConventional/weightconventional.py
+++ b/ceasiompy/WeightConventional/weightconventional.py
@@ -5,7 +5,6 @@
Weight conventional module for preliminary design of conventional aircraft
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-09-27
@@ -29,38 +28,26 @@
from ceasiompy.WeightConventional.func.masses import AircfaftMasses
from ceasiompy.utils.InputClasses.Conventional.weightconvclass import InsideDimensions
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from ceasiompy.utils.ceasiompyutils import get_results_directory
from ceasiompy.utils.commonxpath import TURBOPROP_XPATH, WB_DOUBLE_FLOOR_XPATH
-from ceasiompy.utils.moduleinterfaces import (
- check_cpacs_input_requirements,
- get_toolinput_file_path,
- get_tooloutput_file_path,
-)
from ceasiompy.utils.WB.ConvGeometry import geometry
from ceasiompy.WeightConventional.func.weightutils import PILOT_NB
from cpacspy.cpacsfunctions import get_value_or_default
from cpacspy.cpacspy import CPACS
+from ceasiompy.utils.ceasiompyutils import call_main
-log = get_logger()
-
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
+from ceasiompy.WeightConventional import MODULE_NAME
# =================================================================================================
-# FUNCTIONS
+# MAIN
# =================================================================================================
-def get_weight_estimations(cpacs_path, cpacs_out_path):
+def main(cpacs: CPACS) -> None:
"""Function to estimate the all weights for a conventional aircraft.
Function 'get_weight_estimations' use available information in the CPACS file to estimate the
@@ -75,7 +62,6 @@ def get_weight_estimations(cpacs_path, cpacs_out_path):
"""
- cpacs = CPACS(cpacs_path)
result_dir = get_results_directory("WeightConventional")
turboprop = get_value_or_default(cpacs.tixi, TURBOPROP_XPATH, False)
@@ -156,28 +142,6 @@ def get_weight_estimations(cpacs_path, cpacs_out_path):
log.info(f"Pilots: {PILOT_NB}")
log.info(f"Cabin crew members: {cabin.cabin_crew_nb}")
- cpacs.save_cpacs(cpacs_out_path, overwrite=True)
-
-
-# =================================================================================================
-# MAIN
-# =================================================================================================
-
-
-def main(cpacs_path, cpacs_out_path):
-
- log.info("----- Start of " + MODULE_NAME + " -----")
-
- check_cpacs_input_requirements(cpacs_path)
-
- get_weight_estimations(cpacs_path, cpacs_out_path)
-
- log.info("----- End of " + MODULE_NAME + " -----")
-
if __name__ == "__main__":
-
- cpacs_path = get_toolinput_file_path(MODULE_NAME)
- cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
-
- main(cpacs_path, cpacs_out_path)
+ call_main(main, MODULE_NAME)
diff --git a/ceasiompy/WeightUnconventional/ToolInput/.keep b/ceasiompy/WeightUnconventional/ToolInput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/WeightUnconventional/ToolInput/.keep
+++ b/ceasiompy/WeightUnconventional/ToolInput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/WeightUnconventional/ToolOutput/.keep b/ceasiompy/WeightUnconventional/ToolOutput/.keep
index 8d1c8b69c..8b1378917 100644
--- a/ceasiompy/WeightUnconventional/ToolOutput/.keep
+++ b/ceasiompy/WeightUnconventional/ToolOutput/.keep
@@ -1 +1 @@
-
+
diff --git a/ceasiompy/WeightUnconventional/__init__.py b/ceasiompy/WeightUnconventional/__init__.py
index e69de29bb..a231ee1b0 100644
--- a/ceasiompy/WeightUnconventional/__init__.py
+++ b/ceasiompy/WeightUnconventional/__init__.py
@@ -0,0 +1,32 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for WeightUnconventional module.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Module Status =====
+module_status = False
+
+# ===== Include GUI =====
+include_gui = False
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
diff --git a/ceasiompy/WeightUnconventional/func/AinFunc/getinput.py b/ceasiompy/WeightUnconventional/func/AinFunc/getinput.py
index 6cef53feb..1c64167b7 100644
--- a/ceasiompy/WeightUnconventional/func/AinFunc/getinput.py
+++ b/ceasiompy/WeightUnconventional/func/AinFunc/getinput.py
@@ -6,7 +6,6 @@
The script obtain all the informations required for the Unconventional
weight analysis from the CPACS file
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-11-21
@@ -41,17 +40,7 @@
WB_MAX_PAYLOAD_XPATH,
)
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
-
-
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-"""All classes are defined inside the classes folder and in the
- InputClasses/Unconventional folder."""
+from ceasiompy import log
# =============================================================================
diff --git a/ceasiompy/WeightUnconventional/func/AoutFunc/cpacsweightupdate.py b/ceasiompy/WeightUnconventional/func/AoutFunc/cpacsweightupdate.py
index a6f6b014f..d77ec40c8 100644
--- a/ceasiompy/WeightUnconventional/func/AoutFunc/cpacsweightupdate.py
+++ b/ceasiompy/WeightUnconventional/func/AoutFunc/cpacsweightupdate.py
@@ -6,7 +6,6 @@
Output text and plot generation function for unconventional
aircraft with fuselage.
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-11-21
@@ -19,26 +18,14 @@
from cpacspy.cpacsfunctions import add_uid, create_branch, open_tixi
from ceasiompy.utils.commonxpath import CREW_XPATH, MASSBREAKDOWN_XPATH, PASS_XPATH
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
-
-
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-"""All classes are defined inside the classes folder and in the
- Input
- classes/Unconventional folder."""
-
+from ceasiompy import log
# =============================================================================
# FUNCTIONS
# =============================================================================
-def toolspecific_update(fus_nb, awg, mw, out, cpacs_out_path):
+def toolspecific_update(mw, out, cpacs_out_path):
"""The function that update the cpacs file after the Weight_unc_main
program.
diff --git a/ceasiompy/WeightUnconventional/func/AoutFunc/outputweightgen.py b/ceasiompy/WeightUnconventional/func/AoutFunc/outputweightgen.py
index 393093093..0e0e1f181 100644
--- a/ceasiompy/WeightUnconventional/func/AoutFunc/outputweightgen.py
+++ b/ceasiompy/WeightUnconventional/func/AoutFunc/outputweightgen.py
@@ -5,7 +5,6 @@
Output text and plot generation function for unconventional aircraft analysis.
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-11-21
@@ -19,14 +18,6 @@
from ceasiompy.WeightConventional.func.weightutils import PILOT_NB
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-"""All classes are defined inside the classes folder and in the
- InputClasses/Unconventional folder."""
-
-
# =============================================================================
# FUNCTIONS
# =============================================================================
diff --git a/ceasiompy/WeightUnconventional/func/Engines/enginesanalysis.py b/ceasiompy/WeightUnconventional/func/Engines/enginesanalysis.py
index 092c93f95..df8cc0ebd 100644
--- a/ceasiompy/WeightUnconventional/func/Engines/enginesanalysis.py
+++ b/ceasiompy/WeightUnconventional/func/Engines/enginesanalysis.py
@@ -5,7 +5,6 @@
Evaluation of the mass of the aircraft engines.
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-12-19
@@ -19,17 +18,7 @@
import numpy as np
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
-
-
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-"""All classes are defined inside the classes folder and in the
- InputClasses/Unconventional folder."""
+from ceasiompy import log
# =============================================================================
diff --git a/ceasiompy/WeightUnconventional/func/Fuel/fuelmass.py b/ceasiompy/WeightUnconventional/func/Fuel/fuelmass.py
index f558853ee..3dee8f83c 100644
--- a/ceasiompy/WeightUnconventional/func/Fuel/fuelmass.py
+++ b/ceasiompy/WeightUnconventional/func/Fuel/fuelmass.py
@@ -6,7 +6,6 @@
Evaluation of the fuel mass in the fuselages and in the wings for
an unconventional aircraft.
-Python version: >=3.8
| Author: Stefano Piccini
| Date of creation: 2018-12-19
@@ -19,17 +18,7 @@
import numpy as np
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
-
-
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-"""All classes are defined inside the classes folder and in the
- InputClasses/Unconventional folder."""
+from ceasiompy import log
# =============================================================================
diff --git a/ceasiompy/WeightUnconventional/func/People/crewmembers.py b/ceasiompy/WeightUnconventional/func/People/crewmembers.py
index dd04acae1..0a8516fa7 100644
--- a/ceasiompy/WeightUnconventional/func/People/crewmembers.py
+++ b/ceasiompy/WeightUnconventional/func/People/crewmembers.py
@@ -5,7 +5,6 @@
Evaluation of the number of crew members expected for an unconventional aircraft.
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-12-19
@@ -18,17 +17,7 @@
# =============================================================================
from ceasiompy.WeightConventional.func.weightutils import CABIN_CREW_MASS, PILOT_MASS, PILOT_NB
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
-
-
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-"""All classes are defined inside the classes folder and in the
- InputClasses/Unconventional folder."""
+from ceasiompy import log
# =============================================================================
diff --git a/ceasiompy/WeightUnconventional/func/People/passengers.py b/ceasiompy/WeightUnconventional/func/People/passengers.py
index 5dc54cabb..6baa79d31 100644
--- a/ceasiompy/WeightUnconventional/func/People/passengers.py
+++ b/ceasiompy/WeightUnconventional/func/People/passengers.py
@@ -5,7 +5,6 @@
Evaluation of the number of passengers expected for an unconventional aircraft.
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-12-19
@@ -18,17 +17,7 @@
# =============================================================================
from ceasiompy.WeightConventional.func.weightutils import PASSENGER_MASS, PASSENGER_PER_TOILET
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
-
-
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-"""All classes are defined inside the classes folder and in the
- InputClasses/Unconventional folder."""
+from ceasiompy import log
# =============================================================================
diff --git a/ceasiompy/WeightUnconventional/func/Systems/systemsmass.py b/ceasiompy/WeightUnconventional/func/Systems/systemsmass.py
index 0344bcfc9..93b1dc84a 100644
--- a/ceasiompy/WeightUnconventional/func/Systems/systemsmass.py
+++ b/ceasiompy/WeightUnconventional/func/Systems/systemsmass.py
@@ -5,7 +5,6 @@
Evaluation of the mass of the unconventional aircraft systems.
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-12-19
@@ -19,17 +18,7 @@
import numpy as np
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
-
-
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-"""All classes are defined inside the classes folder and in the
- InputClasses/Unconventional folder."""
+from ceasiompy import log
# =============================================================================
@@ -149,6 +138,6 @@ def estimate_system_mass(
if __name__ == "__main__":
- print("########################################################")
- print("# ERROR NOT A STANDALONE PROGRAM, RUN weightuncmain.py #")
- print("########################################################")
+ log.info("########################################################")
+ log.info("# ERROR NOT A STANDALONE PROGRAM, RUN weightuncmain.py #")
+ log.info("########################################################")
diff --git a/ceasiompy/WeightUnconventional/weightuncmain.py b/ceasiompy/WeightUnconventional/weightuncmain.py
index 07c511c89..85e7c4ce0 100644
--- a/ceasiompy/WeightUnconventional/weightuncmain.py
+++ b/ceasiompy/WeightUnconventional/weightuncmain.py
@@ -5,7 +5,6 @@
Weight unconventional module for preliminary design of unconventional aircraft
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-12-07
@@ -31,7 +30,7 @@
PILOT_NB,
UNUSABLE_FUEL_RATIO,
)
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from ceasiompy.utils.ceasiompyutils import aircraft_name
from ceasiompy.utils.InputClasses.Unconventional.engineclass import EngineData
from ceasiompy.utils.InputClasses.Unconventional.weightuncclass import (
@@ -60,19 +59,7 @@
)
from ceasiompy.WeightUnconventional.func.Systems.systemsmass import estimate_system_mass
-log = get_logger()
-
-MODULE_DIR = Path(__file__).parent
-MODULE_NAME = MODULE_DIR.name
-
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-"""All classes are defined inside the classes and into
- the InputClasses/Unconventional folder."""
-
+from ceasiompy.WeightUnconventional import MODULE_NAME
# =================================================================================================
# FUNCTIONS
@@ -130,7 +117,7 @@ def get_weight_unc_estimations(cpacs_path, cpacs_out_path):
log.warning("Aircraft does not have wings")
raise Exception("Aircraft does not have wings")
elif not fus_nb:
- (awg, wing_nodes) = uncgeomanalysis.no_fuse_geom_analysis(
+ (awg, _) = uncgeomanalysis.no_fuse_geom_analysis(
cpacs_out_path, ui.FLOORS_NB, wing_nb, h_min, ui.FUEL_ON_CABIN, name, ed.turboprop
)
else:
@@ -341,7 +328,7 @@ def get_weight_unc_estimations(cpacs_path, cpacs_out_path):
# Outptu writting
log.info("Generating output text file")
cpacsweightupdate.cpacs_weight_update(out, mw, ui, cpacs_out_path)
- cpacsweightupdate.toolspecific_update(fus_nb, awg, mw, out, cpacs_out_path)
+ cpacsweightupdate.toolspecific_update(mw, out, cpacs_out_path)
cpacsweightupdate.cpacs_engine_update(ui, ed, mw, cpacs_out_path)
if not fus_nb:
@@ -355,20 +342,18 @@ def get_weight_unc_estimations(cpacs_path, cpacs_out_path):
# =================================================================================================
-def main(cpacs_path, cpacs_out_path):
-
- log.info("----- Start of " + MODULE_NAME + " -----")
-
- check_cpacs_input_requirements(cpacs_path)
+def main(cpacs_path: Path, cpacs_out_path: Path) -> None:
+ module_name = MODULE_NAME
+ log.info("----- Start of " + module_name + " -----")
get_weight_unc_estimations(cpacs_path, cpacs_out_path)
- log.info("----- End of " + MODULE_NAME + " -----")
+ log.info("----- End of " + module_name + " -----")
if __name__ == "__main__":
-
cpacs_path = get_toolinput_file_path(MODULE_NAME)
cpacs_out_path = get_tooloutput_file_path(MODULE_NAME)
+ check_cpacs_input_requirements(cpacs_path)
main(cpacs_path, cpacs_out_path)
diff --git a/ceasiompy/__init__.py b/ceasiompy/__init__.py
index e69de29bb..4ce5f18bc 100644
--- a/ceasiompy/__init__.py
+++ b/ceasiompy/__init__.py
@@ -0,0 +1,134 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for CEASIOMpy.
+ 1. Log initialization.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+import sys
+import logging
+import builtins
+
+from pathlib import Path
+from logging import Logger
+
+from pydantic import (
+ BaseModel,
+ ConfigDict,
+)
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+# /CEASIOMpy/
+CEASIOMPY_PATH = Path(__file__).parents[1]
+
+# /CEASIOMpy/ceasiompy.log
+LOGFILE = Path(CEASIOMPY_PATH, "ceasiompy.log")
+
+# =================================================================================================
+# CLASSES
+# =================================================================================================
+
+
+class IgnoreSpecificError(logging.Filter):
+ def filter(self, record):
+ # List of error messages to ignore
+ ignore_errors = [
+ "QWidget::repaint: Recursive repaint detected",
+ "Can not add element to document. Document already saved.",
+ "Info : Increasing process stack size (8192 kB < 16 MB)",
+ ]
+ # Check if the log message contains any of the ignore errors
+ return not any(error in record.getMessage() for error in ignore_errors)
+
+
+class CustomConfig(BaseModel):
+ model_config = ConfigDict(arbitrary_types_allowed=True)
+
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def get_logger() -> Logger:
+ """
+ Creates a logger, sets format and level of logfile and console log.
+ """
+
+ logger = logging.getLogger("CEASIOMpy")
+ logger.setLevel(logging.DEBUG)
+
+ # Check if the logger already has handlers to avoid duplicates
+ if len(logger.handlers) > 0:
+ return logger
+
+ # Prevent propagation to root logger to avoid duplicates
+ logger.propagate = False
+
+ # Add file handler
+ file_formatter = logging.Formatter(
+ "%(asctime)s - %(levelname)8s - %(module)18s - %(message)s")
+ file_handler = logging.FileHandler(filename=LOGFILE, mode="w")
+ file_handler.setLevel(logging.DEBUG)
+ file_handler.setFormatter(file_formatter)
+ file_handler.addFilter(IgnoreSpecificError()) # Add the custom filter
+ logger.addHandler(file_handler)
+
+ # Add console handler regardless of environment
+ # (we need output to be visible in both terminal and Streamlit)
+ console_formatter = logging.Formatter(
+ "%(levelname)8s - %(module)18s - %(message)s")
+ console_handler = logging.StreamHandler(sys.stdout)
+ console_handler.setLevel(logging.INFO)
+ console_handler.setFormatter(console_formatter)
+ console_handler.addFilter(IgnoreSpecificError()) # Add the custom filter
+ logger.addHandler(console_handler)
+
+ # Ignore root logger error messages
+ root_logger = logging.getLogger()
+ root_logger.addFilter(IgnoreSpecificError())
+
+ return logger
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+
+# Log
+log = get_logger()
+
+
+# Override the built-in print function to use the logger
+def custom_print(*args, **kwargs):
+ log.info(' '.join(map(str, args)))
+
+
+builtins.print = custom_print
+
+# Constants
+NO_YES_LIST = ["NO", "YES"]
+
+# Ignore arbitrary types
+ceasiompy_cfg = CustomConfig.model_config
+
+# ==============================================================================
+# MAIN
+# ==============================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/utils/InputClasses/Conventional/weightconvclass.py b/ceasiompy/utils/InputClasses/Conventional/weightconvclass.py
index f80604d65..c03eaad04 100644
--- a/ceasiompy/utils/InputClasses/Conventional/weightconvclass.py
+++ b/ceasiompy/utils/InputClasses/Conventional/weightconvclass.py
@@ -38,9 +38,7 @@
WB_TOILET_LENGTH_XPATH,
)
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
+from ceasiompy import log
# =================================================================================================
@@ -192,4 +190,4 @@ def get_inside_dim(self, cpacs):
if __name__ == "__main__":
- print("Nothing to execute!")
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/utils/InputClasses/Unconventional/aircraftgeometryclass.py b/ceasiompy/utils/InputClasses/Unconventional/aircraftgeometryclass.py
index 206d1daac..47a77281a 100644
--- a/ceasiompy/utils/InputClasses/Unconventional/aircraftgeometryclass.py
+++ b/ceasiompy/utils/InputClasses/Unconventional/aircraftgeometryclass.py
@@ -6,20 +6,11 @@
The script contains all the geometrical value required for the
weight unconventional analysis.
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-11-26
"""
-
-# =============================================================================
-# IMPORTS
-# =============================================================================
-
-""" No import """
-
-
# =============================================================================
# CLASSES
# =============================================================================
diff --git a/ceasiompy/utils/InputClasses/Unconventional/balanceuncclass.py b/ceasiompy/utils/InputClasses/Unconventional/balanceuncclass.py
index 09791bc20..fea1ecc6f 100644
--- a/ceasiompy/utils/InputClasses/Unconventional/balanceuncclass.py
+++ b/ceasiompy/utils/InputClasses/Unconventional/balanceuncclass.py
@@ -6,7 +6,6 @@
The script contains the user inputs required for the
balance unconventional analysis.
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-09-27
diff --git a/ceasiompy/utils/InputClasses/Unconventional/engineclass.py b/ceasiompy/utils/InputClasses/Unconventional/engineclass.py
index e91915268..0268824ae 100644
--- a/ceasiompy/utils/InputClasses/Unconventional/engineclass.py
+++ b/ceasiompy/utils/InputClasses/Unconventional/engineclass.py
@@ -5,7 +5,6 @@
The script contains the user inputs required for the engine analysis.
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-12-11
diff --git a/ceasiompy/utils/InputClasses/Unconventional/weightuncclass.py b/ceasiompy/utils/InputClasses/Unconventional/weightuncclass.py
index 6f20b2554..ee1d83702 100644
--- a/ceasiompy/utils/InputClasses/Unconventional/weightuncclass.py
+++ b/ceasiompy/utils/InputClasses/Unconventional/weightuncclass.py
@@ -6,7 +6,6 @@
The script contains the user inputs required for the
weight and balance unconventional analysis.
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-09-27
diff --git a/ceasiompy/utils/WB/ConvGeometry/Fuselage/fusegeom.py b/ceasiompy/utils/WB/ConvGeometry/Fuselage/fusegeom.py
index 2a4ede855..37da409c3 100644
--- a/ceasiompy/utils/WB/ConvGeometry/Fuselage/fusegeom.py
+++ b/ceasiompy/utils/WB/ConvGeometry/Fuselage/fusegeom.py
@@ -18,9 +18,7 @@
import numpy as np
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
+from ceasiompy import log
# =================================================================================================
@@ -107,7 +105,7 @@ def fuselage_check_segment_connection(fus_nb, fuse_seg_nb, tigl):
nbmax = nb[0]
print(nbmax, fus_nb)
print("--------------========================-------")
- sec_index.resize(nbmax, fus_nb, refcheck=False)
+ sec_index.resize((nbmax, fus_nb), refcheck=False)
sec_index[0 : nb[0], fus_nb - 1] = fuse_sec_index[0 : nb[0]]
sec_nb.append(nb[0])
@@ -164,4 +162,4 @@ def rel_dist(fus_nb, sec_nb, seg_nb, tigl, seg_sec, start_index):
if __name__ == "__main__":
- print("Nothing to execute!")
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/utils/WB/ConvGeometry/geometry.py b/ceasiompy/utils/WB/ConvGeometry/geometry.py
index a39eccfca..cce6dddce 100644
--- a/ceasiompy/utils/WB/ConvGeometry/geometry.py
+++ b/ceasiompy/utils/WB/ConvGeometry/geometry.py
@@ -25,9 +25,7 @@
from ceasiompy.utils.ceasiompyutils import get_results_directory
from ceasiompy.utils.commonxpath import FUSELAGES_XPATH, WINGS_XPATH
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
+from ceasiompy import log
# =============================================================================
@@ -653,7 +651,7 @@ def wing_check_segment_connection(self, wing_plt_area_xz, wing_plt_area_yz, tigl
nb = np.shape(wing_sec_index)
if nb[0] > nbmax:
nbmax = nb[0]
- sec_index.resize(nbmax, self.w_nb, refcheck=False)
+ sec_index.resize((nbmax, self.w_nb), refcheck=False)
sec_index[0 : nb[0], i - 1] = wing_sec_index[0 : nb[0]]
sec_nb.append(nb[0])
@@ -830,4 +828,4 @@ def produce_output_txt(self):
if __name__ == "__main__":
- print("Nothing to execute!")
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/utils/WB/UncGeometry/NoFuseGeom/bwbwingsanalysis.py b/ceasiompy/utils/WB/UncGeometry/NoFuseGeom/bwbwingsanalysis.py
index 17fee4ca0..ce9fdb2f0 100644
--- a/ceasiompy/utils/WB/UncGeometry/NoFuseGeom/bwbwingsanalysis.py
+++ b/ceasiompy/utils/WB/UncGeometry/NoFuseGeom/bwbwingsanalysis.py
@@ -6,7 +6,6 @@
The script evaluate the wings geometry for an unconventional
aircraft without fuselage.
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-12-07
@@ -22,17 +21,7 @@
import math
from cpacspy.cpacsfunctions import open_tigl, open_tixi
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
-
-
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-"""All classes are defined inside the classes folder and in the
- InputClasses/Unconventional folder."""
+from ceasiompy import log
# =============================================================================
diff --git a/ceasiompy/utils/WB/UncGeometry/NoFuseGeom/volumesdefinition.py b/ceasiompy/utils/WB/UncGeometry/NoFuseGeom/volumesdefinition.py
index 5900456e3..040b35921 100644
--- a/ceasiompy/utils/WB/UncGeometry/NoFuseGeom/volumesdefinition.py
+++ b/ceasiompy/utils/WB/UncGeometry/NoFuseGeom/volumesdefinition.py
@@ -5,7 +5,6 @@
The script evaluates the unconventional aircraft wings geometry .
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-12-07
@@ -22,17 +21,7 @@
from cpacspy.cpacsfunctions import open_tigl, open_tixi
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
-
-
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-"""All classes are defined inside the classes folder and in the
- InputClasses/Unconventional folder."""
+from ceasiompy import log
# =============================================================================
@@ -87,10 +76,10 @@ def wing_check_thickness(h_min, awg, cpacs_in, TP, FUEL_ON_CABIN=0):
subd_l = 1
eta = 1.0 / subd_l
et = 0.0
- (xc, yc, zc) = awg.wing_center_seg_point[int(i) - 1][w][:]
+ # (xc, yc, zc) = awg.wing_center_seg_point[int(i) - 1][w][:]
for j in range(0, int(subd_l) - 1):
- (xle, yle, zle) = tigl.wingGetLowerPoint(w + 1, int(i), et, 0.0)
- (xle2, yle2, zle2) = tigl.wingGetLowerPoint(w + 1, int(i), et, 1.0)
+ (xle, _, _) = tigl.wingGetLowerPoint(w + 1, int(i), et, 0.0)
+ (xle2, _, _) = tigl.wingGetLowerPoint(w + 1, int(i), et, 1.0)
if xle < xle2:
ZLE = 0.0
ze = 0.0
@@ -112,7 +101,7 @@ def wing_check_thickness(h_min, awg, cpacs_in, TP, FUEL_ON_CABIN=0):
else:
wing_nodes = np.concatenate((wing_nodes, w_temp), axis=0)
et = j * eta
- (rows, columns, pages) = wing_nodes.shape
+ (rows, columns, _) = wing_nodes.shape
# wing_nodes 3D matrix: the even rows and the zero row correspond
# to the upper profile of the wing, while all the odd rows correspond
diff --git a/ceasiompy/utils/WB/UncGeometry/Output/outputgeom.py b/ceasiompy/utils/WB/UncGeometry/Output/outputgeom.py
index 792d35f3d..428058887 100644
--- a/ceasiompy/utils/WB/UncGeometry/Output/outputgeom.py
+++ b/ceasiompy/utils/WB/UncGeometry/Output/outputgeom.py
@@ -6,7 +6,6 @@
The script generates the output txt file from the geometry analysis
of an unconventional aircraft.
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-09-27
@@ -20,14 +19,6 @@
""" No imports """
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-"""All classes are defined inside the classes and into
- the InputClasses/Unconventional folder."""
-
-
# =============================================================================
# FUNCTIONS
# =============================================================================
@@ -76,8 +67,10 @@ def produce_wing_output_txt(awg, NAME):
OutputTextFile.write("\n-------------------------------------------------")
OutputTextFile.write("\nNumber of Wings [-]: " + str(awg.wing_nb))
OutputTextFile.write("\nWing symmetry plane [-]: " + str(awg.wing_sym))
- OutputTextFile.write("\nNumber of wing sections [-]: " + str(awg.wing_sec_nb))
- OutputTextFile.write("\nNumber of wing segments [-]: " + str(awg.wing_seg_nb))
+ OutputTextFile.write(
+ "\nNumber of wing sections [-]: " + str(awg.wing_sec_nb))
+ OutputTextFile.write(
+ "\nNumber of wing segments [-]: " + str(awg.wing_seg_nb))
OutputTextFile.write("\nWingd Span [m]: " + str(awg.wing_span))
OutputTextFile.write(
"\nWing MAC length [m]: "
@@ -95,27 +88,40 @@ def produce_wing_output_txt(awg, NAME):
]
)
)
- OutputTextFile.write("\nWings sections thickness [m]:\n" + str(awg.wing_sec_thickness))
- OutputTextFile.write("\nWings sections mean thickness [m]:\n" + str(awg.wing_sec_mean_thick))
- OutputTextFile.write("\nWing segments length [m]:\n " + str(awg.wing_seg_length))
- OutputTextFile.write("\nWing max chord length [m]: " + str(awg.wing_max_chord))
- OutputTextFile.write("\nWing min chord length [m]: " + str(awg.wing_min_chord))
- OutputTextFile.write("\nWings planform area [m^2]: " + str(awg.wing_plt_area))
- OutputTextFile.write("\nMain wing wetted surface [m^2]: " + str(awg.main_wing_surface))
- OutputTextFile.write("\nTail wings wetted surface [m^2]: " + str(awg.tail_wings_surface))
- OutputTextFile.write("\nMain wing planform area [m^2]: " + str(awg.wing_plt_area_main))
+ OutputTextFile.write(
+ "\nWings sections thickness [m]:\n" + str(awg.wing_sec_thickness))
+ OutputTextFile.write(
+ "\nWings sections mean thickness [m]:\n" + str(awg.wing_sec_mean_thick))
+ OutputTextFile.write(
+ "\nWing segments length [m]:\n " + str(awg.wing_seg_length))
+ OutputTextFile.write(
+ "\nWing max chord length [m]: " + str(awg.wing_max_chord))
+ OutputTextFile.write(
+ "\nWing min chord length [m]: " + str(awg.wing_min_chord))
+ OutputTextFile.write(
+ "\nWings planform area [m^2]: " + str(awg.wing_plt_area))
+ OutputTextFile.write(
+ "\nMain wing wetted surface [m^2]: " + str(awg.main_wing_surface))
+ OutputTextFile.write(
+ "\nTail wings wetted surface [m^2]: " + str(awg.tail_wings_surface))
+ OutputTextFile.write(
+ "\nMain wing planform area [m^2]: " + str(awg.wing_plt_area_main))
OutputTextFile.write("\nCabin area [m^2]: " + str(awg.cabin_area))
OutputTextFile.write("\nVolume of each wing [m^3]: " + str(awg.wing_vol))
OutputTextFile.write("\nTotal wing volume [m^3]: " + str(awg.wing_tot_vol))
OutputTextFile.write("\n-------------------------------------------------")
OutputTextFile.write("\nWINGS VOLUMES -----------------------------------")
OutputTextFile.write("\nCabin volume [m^3]: " + str(awg.cabin_vol))
- OutputTextFile.write("\nVolume of the wing as fuselage [m^3]: " + str(awg.fuse_vol))
OutputTextFile.write(
- "\nVolume of the remaining portion of the wing [m^3]: " + str(awg.wing_vol)
+ "\nVolume of the wing as fuselage [m^3]: " + str(awg.fuse_vol))
+ OutputTextFile.write(
+ "\nVolume of the remaining portion of the wing [m^3]: " + str(
+ awg.wing_vol)
)
- OutputTextFile.write("\nFuel volume in the fuselage wing [m^3]: " + str(awg.fuse_fuel_vol))
- OutputTextFile.write("\nFuel volume in the wing [m^3]: " + str(awg.wing_fuel_vol))
+ OutputTextFile.write(
+ "\nFuel volume in the fuselage wing [m^3]: " + str(awg.fuse_fuel_vol))
+ OutputTextFile.write(
+ "\nFuel volume in the wing [m^3]: " + str(awg.wing_fuel_vol))
OutputTextFile.write("\nTotal fuel Volume [m^3]: " + str(awg.fuel_vol_tot))
OutputTextFile.write("\n-------------------------------------------------")
OutputTextFile.write("\n-------------------------------------------------")
@@ -168,16 +174,22 @@ def produce_geom_output_txt(afg, awg, NAME):
OutputTextFile.write("\nFUSELAGES ---------------------------------------")
OutputTextFile.write("\n-------------------------------------------------")
OutputTextFile.write("\nNumber of fuselages [-]: " + str(afg.fuse_nb))
- OutputTextFile.write("\nNumber of fuselage sections [-]: " + str(afg.fuse_sec_nb))
- OutputTextFile.write("\nNumber of fuselage segments [-]: " + str(afg.fuse_seg_nb))
+ OutputTextFile.write(
+ "\nNumber of fuselage sections [-]: " + str(afg.fuse_sec_nb))
+ OutputTextFile.write(
+ "\nNumber of fuselage segments [-]: " + str(afg.fuse_seg_nb))
OutputTextFile.write("\nCabin segments array [-]:\n" + str(afg.cabin_seg))
OutputTextFile.write("\nFuse Length [m]: " + str(afg.fuse_length))
- OutputTextFile.write("\nFuse nose Length [m]: " + str(afg.fuse_nose_length))
- OutputTextFile.write("\nFuse cabin Length [m]: " + str(afg.fuse_cabin_length))
- OutputTextFile.write("\nFuse tail Length [m]: " + str(afg.fuse_tail_length))
+ OutputTextFile.write(
+ "\nFuse nose Length [m]: " + str(afg.fuse_nose_length))
+ OutputTextFile.write(
+ "\nFuse cabin Length [m]: " + str(afg.fuse_cabin_length))
+ OutputTextFile.write(
+ "\nFuse tail Length [m]: " + str(afg.fuse_tail_length))
OutputTextFile.write("\nAircraft Length [m]: " + str(afg.tot_length))
OutputTextFile.write(
- "\nPerimeter of each section of each fuselage [m]: \n" + str(afg.fuse_sec_per)
+ "\nPerimeter of each section of each fuselage [m]: \n" + str(
+ afg.fuse_sec_per)
)
OutputTextFile.write(
"\nRelative distance of each section of the fuselage, \
@@ -185,33 +197,42 @@ def produce_geom_output_txt(afg, awg, NAME):
+ str(afg.fuse_sec_rel_dist)
)
OutputTextFile.write(
- "\nLength of each section of each fuselage [m]: \n" + str(afg.fuse_seg_length)
+ "\nLength of each section of each fuselage [m]: \n" + str(
+ afg.fuse_seg_length)
)
- OutputTextFile.write("\nMean fuselage width [m]: " + str(afg.fuse_mean_width))
+ OutputTextFile.write(
+ "\nMean fuselage width [m]: " + str(afg.fuse_mean_width))
OutputTextFile.write(
"\nWidth of each section of each fuselage [m]: \n" + str(afg.fuse_sec_width)
)
OutputTextFile.write(
- "\nHeight of each section of each fuselage [m]: \n" + str(afg.fuse_sec_height)
+ "\nHeight of each section of each fuselage [m]: \n" + str(
+ afg.fuse_sec_height)
)
OutputTextFile.write("\nCabin area [m^2]: " + str(afg.cabin_area))
- OutputTextFile.write("\nFuselages wetted surface [m^2]: " + str(afg.fuse_surface))
OutputTextFile.write(
- "\nVolume of all the segmetns of each fuselage " + "[m^3]: \n" + str(afg.fuse_seg_vol)
+ "\nFuselages wetted surface [m^2]: " + str(afg.fuse_surface))
+ OutputTextFile.write(
+ "\nVolume of all the segmetns of each fuselage [m^3]: \n" + str(afg.fuse_seg_vol)
)
- OutputTextFile.write("\nVolume of each cabin [m^3]: " + str(afg.fuse_cabin_vol))
- OutputTextFile.write("\nVolume of each fuselage [m^3]: " + str(afg.fuse_vol))
- OutputTextFile.write("\nFuel Volume in each fuselage [m^3]: " + str(afg.fuse_fuel_vol))
+ OutputTextFile.write(
+ "\nVolume of each cabin [m^3]: " + str(afg.fuse_cabin_vol))
+ OutputTextFile.write(
+ "\nVolume of each fuselage [m^3]: " + str(afg.fuse_vol))
+ OutputTextFile.write(
+ "\nFuel Volume in each fuselage [m^3]: " + str(afg.fuse_fuel_vol))
OutputTextFile.write("\n-------------------------------------------------")
OutputTextFile.write("\nWINGS -------------------------------------------")
OutputTextFile.write("\n-------------------------------------------------")
OutputTextFile.write("\nNumber of Wings [-]: " + str(awg.wing_nb))
OutputTextFile.write("\nWing symmetry plane [-]: " + str(awg.wing_sym))
OutputTextFile.write(
- "\nNumber of wing sections (not counting symmetry) [-]: " + str(awg.wing_sec_nb)
+ "\nNumber of wing sections (not counting symmetry) [-]: " + str(
+ awg.wing_sec_nb)
)
OutputTextFile.write(
- "\nNumber of wing segments (not counting symmetry) [-]: " + str(awg.wing_seg_nb)
+ "\nNumber of wing segments (not counting symmetry) [-]: " + str(
+ awg.wing_seg_nb)
)
OutputTextFile.write("\nWingd Span [m]: " + str(awg.wing_span))
OutputTextFile.write(
@@ -230,20 +251,30 @@ def produce_geom_output_txt(afg, awg, NAME):
]
)
)
- OutputTextFile.write("\nWings sections thickness [m]:\n" + str(awg.wing_sec_thickness))
- OutputTextFile.write("\nWings sections mean thickness [m]:\n" + str(awg.wing_sec_mean_thick))
- OutputTextFile.write("\nWing segments length [m]:\n " + str(awg.wing_seg_length))
- OutputTextFile.write("\nWing max chord length [m]: " + str(awg.wing_max_chord))
- OutputTextFile.write("\nWing min chord length [m]: " + str(awg.wing_min_chord))
- OutputTextFile.write("\nWings planform area [m^2]: " + str(awg.wing_plt_area))
- OutputTextFile.write("\nMain wing wetted surface [m^2]: " + str(awg.main_wing_surface))
- OutputTextFile.write("\nTail wings wetted surface [m^2]: " + str(awg.tail_wings_surface))
- OutputTextFile.write("\nMain wing planform area [m^2]: " + str(awg.wing_plt_area_main))
+ OutputTextFile.write(
+ "\nWings sections thickness [m]:\n" + str(awg.wing_sec_thickness))
+ OutputTextFile.write(
+ "\nWings sections mean thickness [m]:\n" + str(awg.wing_sec_mean_thick))
+ OutputTextFile.write(
+ "\nWing segments length [m]:\n " + str(awg.wing_seg_length))
+ OutputTextFile.write(
+ "\nWing max chord length [m]: " + str(awg.wing_max_chord))
+ OutputTextFile.write(
+ "\nWing min chord length [m]: " + str(awg.wing_min_chord))
+ OutputTextFile.write(
+ "\nWings planform area [m^2]: " + str(awg.wing_plt_area))
+ OutputTextFile.write(
+ "\nMain wing wetted surface [m^2]: " + str(awg.main_wing_surface))
+ OutputTextFile.write(
+ "\nTail wings wetted surface [m^2]: " + str(awg.tail_wings_surface))
+ OutputTextFile.write(
+ "\nMain wing planform area [m^2]: " + str(awg.wing_plt_area_main))
OutputTextFile.write("\n-------------------------------------------------")
OutputTextFile.write("\nWINGS VOLUMES -----------------------------------")
OutputTextFile.write("\nVolume of each wing [m^3]: " + str(awg.wing_vol))
OutputTextFile.write("\nTotal wing volume [m^3]: " + str(awg.wing_tot_vol))
- OutputTextFile.write("\nWing volume for fuel storage [m^3]: " + str(awg.wing_fuel_vol))
+ OutputTextFile.write(
+ "\nWing volume for fuel storage [m^3]: " + str(awg.wing_fuel_vol))
OutputTextFile.write("\n-------------------------------------------------")
OutputTextFile.write("\n-------------------------------------------------")
diff --git a/ceasiompy/utils/WB/UncGeometry/WithFuseGeom/Fuselages/fusegeom.py b/ceasiompy/utils/WB/UncGeometry/WithFuseGeom/Fuselages/fusegeom.py
index a0cd056b9..cf7913dd1 100644
--- a/ceasiompy/utils/WB/UncGeometry/WithFuseGeom/Fuselages/fusegeom.py
+++ b/ceasiompy/utils/WB/UncGeometry/WithFuseGeom/Fuselages/fusegeom.py
@@ -6,7 +6,6 @@
The scrpt will analyse the fuselage geometry from cpacs file for an
unconventional aircraft.
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-09-27
@@ -22,24 +21,15 @@
from cpacspy.cpacsfunctions import open_tigl, open_tixi
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
-
-
-# ==============================================================================
-# CLASSES
-# ==============================================================================
-
-"""All classes are defined inside the classes folder and into the
- InputClasses/Uconventional folder"""
-
+from ceasiompy import log
# ==============================================================================
# FUNCTIONS
# ==============================================================================
# TODO: change name and maybe reuse conventional aircraft function
+
+
def check_segment_connection(fus_nb, fuse_seg_nb, fuse_sec_nb, tigl):
"""The function checks for each segment the start and end section index
and to reorder them.
@@ -478,4 +468,4 @@ def fuse_geom_eval(fus_nb, h_min, fuse_thick, F_FUEL, afg, cpacs_in):
if __name__ == "__main__":
- print("Nothing to execute!")
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/utils/WB/UncGeometry/WithFuseGeom/Wings/wingsgeom.py b/ceasiompy/utils/WB/UncGeometry/WithFuseGeom/Wings/wingsgeom.py
index 48ec5a86c..a4a86623b 100644
--- a/ceasiompy/utils/WB/UncGeometry/WithFuseGeom/Wings/wingsgeom.py
+++ b/ceasiompy/utils/WB/UncGeometry/WithFuseGeom/Wings/wingsgeom.py
@@ -6,7 +6,6 @@
The script evaluate the wings geometry from cpacs file for an
unconventional aircraft with fuselage.
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-09-27
@@ -22,24 +21,15 @@
from cpacspy.cpacsfunctions import open_tigl, open_tixi
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
-
-
-# ==============================================================================
-# CLASSES
-# ==============================================================================
-
-"""All classes are defined inside the classes folder and in the
- InputClasses/Unconventional folder."""
-
+from ceasiompy import log
# ==============================================================================
# FUNCTIONS
# ==============================================================================
# TODO: change name and maybe reuse conventional aircraft function
+
+
def check_segment_connection(wing_plt_area_xz, wing_plt_area_yz, awg, tigl):
"""The function checks for each segment the start and end section index
and to reorder them.
@@ -113,7 +103,8 @@ def check_segment_connection(wing_plt_area_xz, wing_plt_area_yz, awg, tigl):
for j in range(2, awg.wing_seg_nb[i - 1] + 1):
end_sec = seg_sec_reordered[j - 2, i - 1, 1]
start_next = np.where(seg_sec[:, i - 1, 0] == end_sec)
- seg_sec_reordered[j - 1, i - 1, :] = seg_sec[start_next[0], i - 1, :]
+ seg_sec_reordered[j - 1, i - 1,
+ :] = seg_sec[start_next[0], i - 1, :]
wing_sec_index.append(seg_sec_reordered[0, 0, 0])
for j in range(2, awg.wing_seg_nb[i - 1] + 1):
if seg_sec_reordered[j - 1, i - 1, 0] not in wing_sec_index:
@@ -124,7 +115,7 @@ def check_segment_connection(wing_plt_area_xz, wing_plt_area_yz, awg, tigl):
if nb[0] > nbmax:
nbmax = nb[0]
sec_index.resize(nbmax, awg.w_nb)
- sec_index[0 : nb[0], i - 1] = wing_sec_index[0 : nb[0]]
+ sec_index[0: nb[0], i - 1] = wing_sec_index[0: nb[0]]
sec_nb.append(nb[0])
return (sec_nb, start_index, seg_sec_reordered, sec_index)
@@ -254,7 +245,8 @@ def wing_geom_eval(w_nb, TP, awg, cpacs_in):
# WING ANALYSIS --------------------------------------------------------------
# Main wing plantform area
- awg.wing_plt_area_main = round(awg.wing_plt_area[awg.main_wing_index - 1], 3)
+ awg.wing_plt_area_main = round(
+ awg.wing_plt_area[awg.main_wing_index - 1], 3)
# Wing: MAC,chords,thicknes,span,plantform area ------------------------------
for i in range(1, awg.w_nb + 1):
@@ -264,8 +256,10 @@ def wing_geom_eval(w_nb, TP, awg, cpacs_in):
awg.wing_max_chord.append(
np.sqrt((wpx2 - wpx) ** 2 + (wpy2 - wpy) ** 2 + (wpz2 - wpz) ** 2)
)
- (wpx, wpy, wpz) = tigl.wingGetChordPoint(i, awg.wing_seg_nb[i - 1], 1.0, 0.0)
- (wpx2, wpy2, wpz2) = tigl.wingGetChordPoint(i, awg.wing_seg_nb[i - 1], 1.0, 1.0)
+ (wpx, wpy, wpz) = tigl.wingGetChordPoint(
+ i, awg.wing_seg_nb[i - 1], 1.0, 0.0)
+ (wpx2, wpy2, wpz2) = tigl.wingGetChordPoint(
+ i, awg.wing_seg_nb[i - 1], 1.0, 1.0)
awg.wing_min_chord.append(
np.sqrt((wpx2 - wpx) ** 2 + (wpy2 - wpy) ** 2 + (wpz2 - wpz) ** 2)
)
@@ -294,16 +288,22 @@ def wing_geom_eval(w_nb, TP, awg, cpacs_in):
(wpux - wplx) ** 2 + (wpuy - wply) ** 2 + (wpuz - wplz) ** 2
)
j = int(seg_sec[awg.wing_seg_nb[i - 1] - 1, i - 1, 2])
- (wplx, wply, wplz) = tigl.wingGetLowerPoint(i, awg.wing_seg_nb[i - 1], 1.0, L)
- (wpux, wpuy, wpuz) = tigl.wingGetUpperPoint(i, awg.wing_seg_nb[i - 1], 1.0, U)
+ (wplx, wply, wplz) = tigl.wingGetLowerPoint(
+ i, awg.wing_seg_nb[i - 1], 1.0, L)
+ (wpux, wpuy, wpuz) = tigl.wingGetUpperPoint(
+ i, awg.wing_seg_nb[i - 1], 1.0, U)
awg.wing_sec_thickness[j][i - 1] = np.sqrt(
(wpux - wplx) ** 2 + (wpuy - wply) ** 2 + (wpuz - wplz) ** 2
)
- wing_center_section_point[awg.wing_seg_nb[i - 1]][i - 1][0] = (wplx + wpux) / 2
- wing_center_section_point[awg.wing_seg_nb[i - 1]][i - 1][1] = (wply + wpuy) / 2
- wing_center_section_point[awg.wing_seg_nb[i - 1]][i - 1][2] = (wplz + wpuz) / 2
+ wing_center_section_point[awg.wing_seg_nb[i - 1]
+ ][i - 1][0] = (wplx + wpux) / 2
+ wing_center_section_point[awg.wing_seg_nb[i - 1]
+ ][i - 1][1] = (wply + wpuy) / 2
+ wing_center_section_point[awg.wing_seg_nb[i - 1]
+ ][i - 1][2] = (wplz + wpuz) / 2
awg.wing_sec_mean_thick.append(
- np.mean(awg.wing_sec_thickness[0 : awg.wing_seg_nb[i - 1] + 1, i - 1])
+ np.mean(
+ awg.wing_sec_thickness[0: awg.wing_seg_nb[i - 1] + 1, i - 1])
)
# Evaluating wing fuel tank volume and if the wing is horizontal or vertical
@@ -319,11 +319,14 @@ def wing_geom_eval(w_nb, TP, awg, cpacs_in):
if PLT == 1:
corr += 0.05
if tp_ratio * awg.wing_plt_area[i - 1] > 80:
- awg.wing_fuel_vol += round(awg.wing_vol[i - 1] * (0.7 + corr), 2)
+ awg.wing_fuel_vol += round(awg.wing_vol[i - 1]
+ * (0.7 + corr), 2)
elif tp_ratio * awg.wing_plt_area[i - 1] > 40:
- awg.wing_fuel_vol += round(awg.wing_vol[i - 1] * (0.65 + corr), 2)
+ awg.wing_fuel_vol += round(awg.wing_vol[i - 1]
+ * (0.65 + corr), 2)
elif tp_ratio * awg.wing_plt_area[i - 1] > 10:
- awg.wing_fuel_vol += round(awg.wing_vol[i - 1] * (0.55 + corr), 2)
+ awg.wing_fuel_vol += round(awg.wing_vol[i - 1]
+ * (0.55 + corr), 2)
else:
awg.wing_fuel_vol += 0
for j in seg_sec[:, i - 1, 2]:
@@ -395,9 +398,12 @@ def wing_geom_eval(w_nb, TP, awg, cpacs_in):
symy = 1
symx = -1
symz = 1
- awg.wing_center_seg_point[:, i, 0] = awg.wing_center_seg_point[:, i - 1, 0] * symx
- awg.wing_center_seg_point[:, i, 1] = awg.wing_center_seg_point[:, i - 1, 1] * symy
- awg.wing_center_seg_point[:, i, 2] = awg.wing_center_seg_point[:, i - 1, 2] * symz
+ awg.wing_center_seg_point[:, i,
+ 0] = awg.wing_center_seg_point[:, i - 1, 0] * symx
+ awg.wing_center_seg_point[:, i,
+ 1] = awg.wing_center_seg_point[:, i - 1, 1] * symy
+ awg.wing_center_seg_point[:, i,
+ 2] = awg.wing_center_seg_point[:, i - 1, 2] * symz
c = True
a += 1
@@ -413,8 +419,10 @@ def wing_geom_eval(w_nb, TP, awg, cpacs_in):
)
log.info("Number of Wings [-]: " + str(awg.wing_nb))
log.info("Wing symmetry plane [-]: " + str(awg.wing_sym))
- log.info("Number of wing sections (not counting symmetry) [-]: " + str(awg.wing_sec_nb))
- log.info("Number of wing segments (not counting symmetry) [-]: " + str(awg.wing_seg_nb))
+ log.info(
+ "Number of wing sections (not counting symmetry) [-]: " + str(awg.wing_sec_nb))
+ log.info(
+ "Number of wing segments (not counting symmetry) [-]: " + str(awg.wing_seg_nb))
log.info("Wing Span (counting symmetry)[m]: \n" + str(awg.wing_span))
log.info(
"Wing MAC length [m]: "
@@ -433,13 +441,15 @@ def wing_geom_eval(w_nb, TP, awg, cpacs_in):
)
)
log.info("Wings sections thicknes [m]: \n" + str(awg.wing_sec_thickness))
- log.info("Wings sections mean thicknes [m]: \n" + str(awg.wing_sec_mean_thick))
+ log.info(
+ "Wings sections mean thicknes [m]: \n" + str(awg.wing_sec_mean_thick))
log.info("Wing segments length [m]: \n" + str(awg.wing_seg_length))
log.info("Wing max chord length [m]: \n" + str(awg.wing_max_chord))
log.info("Wing min chord length [m]: \n" + str(awg.wing_min_chord))
log.info("Main wing plantform area [m^2]: " + str(awg.wing_plt_area_main))
log.info("Main wing wetted surface [m^2]: " + str(awg.main_wing_surface))
- log.info("Tail wings wetted surface [m^2]: \n" + str(awg.tail_wings_surface))
+ log.info(
+ "Tail wings wetted surface [m^2]: \n" + str(awg.tail_wings_surface))
log.info("Wings plantform area [m^2]: \n" + str(awg.wing_plt_area))
log.info("Volume of each wing [m^3]: " + str(awg.wing_vol))
log.info("Total wing volume [m^3]: " + str(awg.wing_tot_vol))
diff --git a/ceasiompy/utils/WB/UncGeometry/uncgeomanalysis.py b/ceasiompy/utils/WB/UncGeometry/uncgeomanalysis.py
index 296ac41e1..aefbc441b 100644
--- a/ceasiompy/utils/WB/UncGeometry/uncgeomanalysis.py
+++ b/ceasiompy/utils/WB/UncGeometry/uncgeomanalysis.py
@@ -6,7 +6,6 @@
Main geometry evaluation script, it connects the wing and fuselage
geometry analysis for unconventional aircraft.
-Python version: >=3.8
| Author : Stefano Piccini
| Date of creation: 2018-12-07
@@ -34,17 +33,7 @@
from cpacspy.cpacsfunctions import open_tixi
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
-
-
-# =============================================================================
-# CLASSES
-# =============================================================================
-
-"""All classes are defined inside the classes and into
- the InputClasses/Unconventional folder."""
+from ceasiompy import log
# =============================================================================
@@ -67,12 +56,14 @@ def get_number_of_parts(cpacs_in):
tixi = open_tixi(cpacs_in)
if tixi.checkElement("/cpacs/vehicles/aircraft/model/fuselages"):
- fus_nb = tixi.getNamedChildrenCount("/cpacs/vehicles/aircraft/model/fuselages", "fuselage")
+ fus_nb = tixi.getNamedChildrenCount(
+ "/cpacs/vehicles/aircraft/model/fuselages", "fuselage")
else:
fus_nb = 0
if tixi.checkElement("/cpacs/vehicles/aircraft/model/wings"):
- wing_nb = tixi.getNamedChildrenCount("/cpacs/vehicles/aircraft/model/wings", "wing")
+ wing_nb = tixi.getNamedChildrenCount(
+ "/cpacs/vehicles/aircraft/model/wings", "wing")
else:
wing_nb = 0
@@ -102,7 +93,8 @@ def no_fuse_geom_analysis(cpacs_in, FLOOR_NB, wing_nb, h_min, FUEL_ON_CABIN, NAM
"""
awg = AircraftWingGeometry()
awg = geom_eval(wing_nb, awg, cpacs_in)
- (awg, wing_nodes) = wing_check_thickness(h_min, awg, cpacs_in, TP, FUEL_ON_CABIN)
+ (awg, wing_nodes) = wing_check_thickness(
+ h_min, awg, cpacs_in, TP, FUEL_ON_CABIN)
produce_wing_output_txt(awg, NAME)
return (awg, wing_nodes)
diff --git a/ceasiompy/utils/__init__.py b/ceasiompy/utils/__init__.py
index e69de29bb..ec1f69636 100644
--- a/ceasiompy/utils/__init__.py
+++ b/ceasiompy/utils/__init__.py
@@ -0,0 +1,42 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialization for UTILS.
+
+
+| Author: Leon Deligny
+| Creation: 18-Mar-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+from pathlib import Path
+
+from ceasiompy import log
+
+# ==============================================================================
+# INITIALIZATION
+# ==============================================================================
+
+# ===== Include Module's name =====
+MODULE_DIR = Path(__file__).parent
+MODULE_NAME = MODULE_DIR.name
+
+# Aeromap list
+AEROMAP_LIST = [
+ "__AEROMAP_SELECTION",
+ "__AEROMAP_CHECKBOX",
+]
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to be executed.")
diff --git a/ceasiompy/utils/ceasiomlogger.py b/ceasiompy/utils/ceasiompylogger.py
similarity index 52%
rename from ceasiompy/utils/ceasiomlogger.py
rename to ceasiompy/utils/ceasiompylogger.py
index 971f0c35b..2841cf9bf 100644
--- a/ceasiompy/utils/ceasiomlogger.py
+++ b/ceasiompy/utils/ceasiompylogger.py
@@ -6,14 +6,11 @@
Logging method use by other CEASIOMpy modules
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2018-09-26
-
-TODO:
-
- * Do we want one big logfile (global) or many small (local for each module)?
+| Modified: Leon Deligny
+| Date: 25 March 2025
"""
@@ -21,54 +18,17 @@
# IMPORTS
# =================================================================================================
-import logging
-from datetime import datetime
from pathlib import Path
+from datetime import datetime
-from ceasiompy.utils.commonpaths import LOGFILE, RUNWORKFLOW_HISTORY_PATH
+from ceasiompy import log
+from ceasiompy.utils.commonpaths import RUNWORKFLOW_HISTORY_PATH
# =================================================================================================
# FUNCTIONS
# =================================================================================================
-def get_logger():
- """Function to create a logger
-
- Function 'get_logger' create a logger, it sets the format and the level of
- the logfile and console log.
-
- Returns:
- logger (logger): Logger
- """
-
- logger = logging.getLogger("CEASIOMpy")
-
- # NOTE: Multiple calls to getLogger() with the same name will return a
- # reference to the same logger object. However, there can be any number of
- # handlers (!) If a logger already as one or more handlers, none will be added
- if len(logger.handlers) > 0:
- return logger
-
- logger.setLevel(logging.DEBUG)
-
- # Write logfile
- file_formatter = logging.Formatter("%(asctime)s - %(levelname)8s - %(module)18s - %(message)s")
- file_handler = logging.FileHandler(filename=LOGFILE, mode="w")
- file_handler.setLevel(logging.DEBUG) # Level for the logfile
- file_handler.setFormatter(file_formatter)
- logger.addHandler(file_handler)
-
- # Write log messages on the console
- console_formatter = logging.Formatter("%(levelname)-8s - %(message)s")
- console_handler = logging.StreamHandler()
- console_handler.setLevel(logging.DEBUG) # Level for the console log
- console_handler.setFormatter(console_formatter)
- logger.addHandler(console_handler)
-
- return logger
-
-
def add_to_runworkflow_history(working_dir: Path, comment: str = "") -> None:
"""Add a line to the runworkflow history"""
@@ -102,5 +62,4 @@ def get_last_runworkflow() -> Path:
# =================================================================================================
if __name__ == "__main__":
-
- print("Nothing to execute!")
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/utils/ceasiompytest.py b/ceasiompy/utils/ceasiompytest.py
new file mode 100644
index 000000000..3d249a9d1
--- /dev/null
+++ b/ceasiompy/utils/ceasiompytest.py
@@ -0,0 +1,59 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed for CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Functions utils to run ceasiompy workflows
+
+| Author : Leon Deligny
+| Creation: 21 March 2025
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+import unittest
+
+from pytest import approx
+
+from pathlib import Path
+from typing import Tuple
+from typing import Callable
+from cpacspy.cpacspy import CPACS
+
+from ceasiompy import log
+from ceasiompy.utils.commonpaths import CPACS_FILES_PATH
+
+
+# =================================================================================================
+# CLASSES
+# =================================================================================================
+
+class CeasiompyTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cpacs_in = Path(CPACS_FILES_PATH, "D150_simple.xml")
+ cls.test_cpacs = CPACS(cpacs_in)
+
+ def assert_equal_function(self, f: Callable, input_args: Tuple, expected: Tuple) -> None:
+ actual = f(*input_args)
+ # When the function to test f returns a single value
+ # then it is not of type tuple.
+ # We make the following adjustment.
+ if not isinstance(actual, tuple) and isinstance(expected, tuple):
+ actual = (actual,)
+
+ self.assertEqual(len(actual), len(expected), "Number of outputs does not match.")
+ for act, exp in zip(actual, expected):
+ self.assertEqual(act, approx(exp))
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/utils/ceasiompyutils.py b/ceasiompy/utils/ceasiompyutils.py
index 35ddbacdb..a51bbbed3 100644
--- a/ceasiompy/utils/ceasiompyutils.py
+++ b/ceasiompy/utils/ceasiompyutils.py
@@ -4,70 +4,158 @@
Developed for CFS ENGINEERING, 1015 Lausanne, Switzerland
Functions utils to run ceasiompy workflows
-
-Python version: >=3.8
-
-| Author : Aidan Jungo
-| Creation: 2022-02-04
-
-TODO:
-
"""
# =================================================================================================
# IMPORTS
# =================================================================================================
-import importlib
-import math
+import re
import os
+import sys
+import math
import shutil
+import importlib
import subprocess
-import sys
-from contextlib import contextmanager
-from pathlib import Path
-from typing import List
+import streamlit as st
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.commonxpath import AIRCRAFT_NAME_XPATH
+from pydantic import validate_call
+from contextlib import contextmanager
from ceasiompy.utils.moduleinterfaces import get_module_list
+from ceasiompy.utils.moduleinterfaces import (
+ get_specs_for_module,
+ get_toolinput_file_path,
+ get_tooloutput_file_path,
+ check_cpacs_input_requirements,
+)
from cpacspy.cpacsfunctions import (
- add_string_vector,
+ get_value,
+ open_tixi,
create_branch,
+ add_string_vector,
get_string_vector,
get_value_or_default,
- open_tixi,
)
-log = get_logger()
+from pathlib import Path
+from cpacspy.cpacspy import CPACS, AeroMap
+from unittest.mock import MagicMock
+from tixi3.tixi3wrapper import Tixi3
+from ceasiompy.utils.moduleinterfaces import CPACSInOut
+from typing import (
+ List,
+ Tuple,
+ TextIO,
+ Optional,
+ Callable,
+)
+
+from ceasiompy.utils import AEROMAP_LIST
+from ceasiompy import (
+ log,
+ ceasiompy_cfg,
+)
+from ceasiompy.utils.commonpaths import (
+ WKDIR_PATH,
+ CPACS_FILES_PATH,
+)
+from ceasiompy.utils.moduleinterfaces import (
+ MODNAME_INIT,
+ MODNAME_SPECS,
+)
+from ceasiompy.utils.commonxpath import (
+ AIRCRAFT_NAME_XPATH,
+ RANGE_CRUISE_ALT_XPATH,
+ RANGE_CRUISE_MACH_XPATH,
+)
# =================================================================================================
-# CLASSES
+# FUNCTIONS
# =================================================================================================
-class SoftwareNotInstalled(Exception):
- """Raised when the a required software is not installed"""
-
- pass
+def update_cpacs_from_specs(cpacs: CPACS, module_name: str) -> None:
+ tixi = cpacs.tixi
+ st.session_state.cpacs = cpacs
+ cpacsin_out: CPACSInOut = get_specs_for_module(module_name).cpacs_inout
+ inputs = cpacsin_out.get_gui_dict()
+ for name, default_value, var_type, _, xpath, _, _ in inputs.values():
+ parts = xpath.strip('/').split('/')
+ for i in range(1, len(parts) + 1):
+ path = '/' + '/'.join(parts[:i])
+ if not tixi.checkElement(path):
+ tixi.createElement('/' + '/'.join(parts[:i - 1]), parts[i - 1])
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
+ # Check if the name or var_type is in the dictionary and call the corresponding function
+ if name in AEROMAP_LIST:
+ aeromap_uid_list = cpacs.get_aeromap_uid_list()
+ if not len(aeromap_uid_list):
+ log.error("You must create an aeromap in order to use this module !")
+ else:
+ # Use first aeromap
+ tixi.updateTextElement(xpath, aeromap_uid_list[0])
+
+ elif var_type == str:
+ tixi.updateTextElement(xpath, default_value)
+ elif var_type == float:
+ tixi.updateDoubleElement(xpath, default_value, format="%g")
+ elif var_type == bool:
+ tixi.updateBooleanElement(xpath, default_value)
+ elif var_type == int:
+ tixi.updateIntegerElement(xpath, default_value, format="%d")
+ elif var_type == list:
+ tixi.updateTextElement(xpath, str(default_value[0]))
+ elif var_type == "DynamicChoice":
+ create_branch(tixi, xpath + "type")
+ tixi.updateTextElement(xpath + "type", str(default_value[0]))
+ elif var_type == "multiselect":
+ str_value = ";".join(str(ele) for ele in default_value)
+ tixi.updateTextElement(xpath, str_value)
+ else:
+ tixi.updateTextElement(xpath, default_value)
@contextmanager
def change_working_dir(working_dir):
"""Context manager to change the working directory just before the execution of a function."""
+ cwd = None
try:
cwd = os.getcwd()
os.chdir(working_dir)
yield
-
finally:
- os.chdir(cwd)
+ if cwd is not None:
+ os.chdir(cwd)
+
+
+def ensure_and_append_text_element(
+ tixi: Tixi3,
+ parent_xpath: str,
+ element_name: str,
+ text: str
+) -> None:
+ """
+ Ensures element (element_name) exists at xpath (parent_xpath),
+ and appends data (text) to this element.
+
+ Args:
+ tixi (Tixi3): Tixi HANDLE of CPACS file.
+ parent_xpath (str): xPath to the element.
+ element_name (str): Name of element.
+ text (str): Data to append.
+
+ """
+
+ element_xpath = f"{parent_xpath}/{element_name}"
+ if not tixi.checkElement(element_xpath):
+ tixi.addTextElement(parent_xpath, element_name, text)
+ tixi.addTextAttribute(element_xpath, "mapType", "vector")
+ else:
+ existing_text = tixi.getTextElement(element_xpath)
+ new_text = existing_text + ";" + text
+ tixi.updateTextElement(element_xpath, new_text)
def get_aeromap_list_from_xpath(cpacs, aeromap_to_analyze_xpath, empty_if_not_found=False):
@@ -80,35 +168,123 @@ def get_aeromap_list_from_xpath(cpacs, aeromap_to_analyze_xpath, empty_if_not_fo
empty_if_not_found (bool): If true, return an empty list if the xpath did not exist.
"""
+ tixi = cpacs.tixi
aeromap_uid_list = []
try:
- aeromap_uid_list = get_string_vector(cpacs.tixi, aeromap_to_analyze_xpath)
- except ValueError: # if aeroMapToPlot is not define, select all aeromaps
+ aeromap_uid_list = get_string_vector(tixi, aeromap_to_analyze_xpath)
+ except ValueError: # if aeroMapToPlot is not defined, select all aeromaps
if not empty_if_not_found:
aeromap_uid_list = cpacs.get_aeromap_uid_list()
- create_branch(cpacs.tixi, aeromap_to_analyze_xpath)
- add_string_vector(cpacs.tixi, aeromap_to_analyze_xpath, aeromap_uid_list)
+ create_branch(tixi, aeromap_to_analyze_xpath)
+ add_string_vector(tixi, aeromap_to_analyze_xpath, aeromap_uid_list)
return aeromap_uid_list
-def get_results_directory(module_name: str) -> Path:
- """Create (if not exists) and return the results directory for a module"""
+def get_results_directory(module_name: str, create: bool = True, wkflow_dir: Path = None) -> Path:
+ """
+ Returns the results directory of a module.
+
+ Args:
+ module_name (str): Name of the module's result directory.
+ create (bool): If you need to create it.
+ wkflow_dir (Path): Path to the workflow of the module.
+
+ """
if module_name not in get_module_list(only_active=False):
- raise ValueError(f"Module '{module_name}' did not exit!")
+ raise ValueError(f"Module '{module_name}' does not exist.")
- specs = importlib.import_module(f"ceasiompy.{module_name}.__specs__")
- results_dir = Path(Path.cwd(), specs.RESULTS_DIR)
+ init = importlib.import_module(f"ceasiompy.{module_name}.{MODNAME_INIT}")
+ if wkflow_dir is None:
+ wkflow_dir = Path.cwd()
+ results_dir = Path(wkflow_dir, "Results", init.MODULE_NAME)
- if not results_dir.is_dir():
+ if create and not results_dir.is_dir():
results_dir.mkdir(parents=True)
return results_dir
-def run_module(module, wkdir=Path.cwd(), iteration=0):
+def get_wkdir_status(module_name: str) -> bool:
+ init = importlib.import_module(f"ceasiompy.{module_name}.{MODNAME_INIT}")
+ return init.RES_DIR
+
+
+def current_workflow_dir() -> Path:
+ """
+ Get the current workflow directory.
+ """
+
+ # Ensure WKDIR_PATH exists
+ WKDIR_PATH.mkdir(parents=True, exist_ok=True)
+
+ # Change the current working directory
+ os.chdir(WKDIR_PATH)
+
+ # Check index of the last workflow directory to set the next one
+ wkflow_list = [int(dir.stem.split("_")[-1]) for dir in WKDIR_PATH.glob("Workflow_*")]
+ if wkflow_list:
+ wkflow_idx = str(max(wkflow_list) + 1).rjust(3, "0")
+ else:
+ wkflow_idx = "001"
+
+ current_wkflow_dir = Path.joinpath(WKDIR_PATH, "Workflow_" + wkflow_idx)
+ current_wkflow_dir.mkdir()
+
+ return current_wkflow_dir
+
+
+@validate_call(config=ceasiompy_cfg)
+def call_main(main: Callable, module_name: str, cpacs_path: Path = None) -> None:
+ """
+ Calls main with input/output CPACS of module named module_name.
+ #TODO: Add Args and Returns.
+ """
+ st.session_state = MagicMock()
+ wkflow_dir = current_workflow_dir()
+
+ log.info(f"Workflow's working directory: {wkflow_dir} \n")
+ log.info("----- Start of " + module_name + " -----")
+
+ if cpacs_path is None:
+ xml_file = "D150_simple.xml"
+ cpacs_path = Path(CPACS_FILES_PATH, xml_file)
+ else:
+ xml_file = cpacs_path.name
+
+ with change_working_dir(wkflow_dir):
+ cpacs = CPACS(cpacs_path)
+ log.info(f"Upload default values from {MODNAME_SPECS}.")
+ update_cpacs_from_specs(cpacs, module_name)
+
+ new_cpacs_path = wkflow_dir / xml_file
+ cpacs.save_cpacs(new_cpacs_path, overwrite=True)
+ cpacs = CPACS(new_cpacs_path)
+
+ log.info(f"Finished uploading default values from {MODNAME_SPECS}.")
+
+ if get_wkdir_status(module_name):
+ results_dir = get_results_directory(module_name, create=True, wkflow_dir=wkflow_dir)
+ main(cpacs, results_dir)
+ else:
+ main(cpacs)
+
+ cpacs.save_cpacs(new_cpacs_path, overwrite=True)
+
+ log.info("----- End of " + module_name + " -----")
+
+
+def initialize_cpacs(module_name: str) -> Tuple[CPACS, Path]:
+ cpacs_in = get_toolinput_file_path(module_name)
+ cpacs_out = get_tooloutput_file_path(module_name)
+ check_cpacs_input_requirements(cpacs_in)
+ cpacs = CPACS(cpacs_in)
+ return cpacs, cpacs_out
+
+
+def run_module(module, wkdir=Path.cwd(), iteration=0, test=False):
"""Run a 'ModuleToRun' object in a specific wkdir.
Args:
@@ -116,28 +292,43 @@ def run_module(module, wkdir=Path.cwd(), iteration=0):
wkdir (Path, optional): Path of the working directory. Defaults to Path.cwd().
"""
- log.info("#" * 99)
- log.info("# Run module: " + module.name)
- log.info("#" * 99)
+ module_name = module.name
+ cpacs_in = module.cpacs_in
+ cpacs_out = module.cpacs_out
- log.info("Working directory: " + str(wkdir))
- log.info("CPACS input file: " + str(module.cpacs_in))
- log.info("CPACS output file: " + str(module.cpacs_out))
+ if iteration == 0:
+ log.info(f"Workflow's working directory: {wkdir} \n")
+
+ log.info("---------- Start of " + module_name + " ----------")
if module.name == "Optimisation" and iteration > 0:
- log.info("Optimisation module is only run at first iteration!")
+ log.warning("Optimisation module is only run at first iteration.")
else:
+ # Find main python file for module
for file in module.module_dir.iterdir():
if file.name.endswith(".py") and not file.name.startswith("__"):
python_file = file.stem
+ break
+ else:
+ log.warning(f"No python files found for module {module_name}.")
- # Import the main function from the module
- my_module = importlib.import_module(f"ceasiompy.{module.name}.{python_file}")
+ # Import the main function from the module's python file
+ my_module = importlib.import_module(f"ceasiompy.{module_name}.{python_file}")
# Run the module
with change_working_dir(wkdir):
- my_module.main(module.cpacs_in, module.cpacs_out)
+ cpacs = CPACS(cpacs_in)
+ if test:
+ log.info("Updating CPACS from __specs__")
+ update_cpacs_from_specs(cpacs, module_name)
+ if module.results_dir is None:
+ my_module.main(cpacs)
+ else:
+ my_module.main(cpacs, module.results_dir)
+ cpacs.save_cpacs(cpacs_out, overwrite=True)
+
+ log.info("---------- End of " + module_name + " ---------- \n")
def get_install_path(software_name: str, raise_error: bool = False) -> Path:
@@ -158,13 +349,56 @@ def get_install_path(software_name: str, raise_error: bool = False) -> Path:
log.warning(f"{software_name} is not installed on your computer!")
if raise_error:
- raise SoftwareNotInstalled(f"{software_name} is not installed on your computer!")
+ log.warning(f"{software_name} is not installed on your computer!")
else:
return None
+def check_version(software_name: str, required_version: str) -> Tuple[bool, str]:
+ """
+ Check if the version is greater than or equal to the required version.
+ """
+
+ version = get_version(software_name)
+ if version is None:
+ return False, ""
+
+ version_tuple = tuple(map(int, version.split('.')))
+ required_version_tuple = tuple(map(int, required_version.split('.')))
+
+ return version_tuple >= required_version_tuple, version
+
+
+def get_version(software_name: str) -> str:
+ """
+ Return the installed version of a software.
+ Looks at X.X.X pattern in installation path.
+ """
+
+ version_file = get_install_path(software_name)
+
+ if not version_file.exists():
+ log.warning(f"The version file for {software_name} does not exist!")
+ return ""
+
+ version_pattern = re.compile(r'\d+\.\d+\.\d+')
+ match = version_pattern.search(str(version_file))
+
+ if match:
+ return match.group(0)
+ else:
+ log.warning(f"Version information not found in the path: {version_file}.")
+ return ""
+
+
def run_software(
- software_name: str, arguments: List[str], wkdir: Path, with_mpi: bool = False, nb_cpu: int = 1
+ software_name: str,
+ arguments: List[str],
+ wkdir: Path,
+ with_mpi: bool = False,
+ nb_cpu: int = 1,
+ stdin: Optional[TextIO] = None,
+ log_bool: bool = True,
) -> None:
"""Run a software with the given arguments in a specific wkdir. If the software is compatible
with MPI, 'with_mpi' can be set to True and the number of processors can be specified. A
@@ -173,15 +407,23 @@ def run_software(
Args:
software_name (str): Name of the software to run.
arguments (str): Arguments to pass to the software.
- wkdir (Path): Working directory where the software will be run.
- with_mpi (bool, optional): If True, run the software with MPI. Defaults to False.
- nb_cpu (int, optional): Number of processors to use. Defaults to 1. If with_mpi is True,
-
+ wkdir (Path, optional): Working directory where the software will be run.
+ with_mpi (bool = False): If True, run the software with MPI. Defaults to False.
+ nb_cpu (int = 1): Number of processors to use. Defaults to 1. If with_mpi is True,
+ #TODO: Add rest of documentation
"""
- log.info(f"{int(nb_cpu)} cpu over {os.cpu_count()} will be used for this calculation.")
+ # Check nb_cpus
+ nb_cpu = nb_cpu if with_mpi else 1
+
+ if nb_cpu > 1:
+ check_nb_cpu(nb_cpu)
+
+ log.info(
+ f"{int(nb_cpu)} cpu{'s' if nb_cpu > 1 else ''} "
+ f"over {os.cpu_count()} will be used for this calculation."
+ )
- logfile = Path(wkdir, f"logfile_{software_name}.log")
install_path = get_install_path(software_name)
command_line = []
@@ -195,36 +437,109 @@ def run_software(
# Use xvfb to run sumo to avoid problems with X11 (e.g. when running test on Github actions)
if software_name in ["sumo", "dwfsumo"] and sys.platform == "linux":
- command_line = ["xvfb-run"] + command_line
+ if shutil.which("xvfb-run"):
+ command_line = ["xvfb-run", "--auto-servernum"] + command_line
+ else:
+ log.warning("xvfb-run not found. Proceeding without it.")
log.info(f">>> Running {software_name} on {int(nb_cpu)} cpu(s)")
log.info(f"Working directory: {wkdir}")
- log.info(f"Logfile: {logfile}")
log.info("Command line that will be run is:")
log.info(" ".join(map(str, command_line)))
with change_working_dir(wkdir):
- with open(logfile, "w") as logfile:
- subprocess.call(command_line, stdout=logfile)
+ if log_bool:
+ logfile = Path(wkdir, f"logfile_{software_name}.log")
+ with open(logfile, "w") as logfile:
+ if stdin is None:
+ subprocess.run(command_line, stdout=logfile, cwd=wkdir)
+ else:
+ subprocess.run(command_line, stdin=stdin, stdout=logfile, cwd=wkdir)
+ else:
+ if stdin is None:
+ subprocess.run(command_line, cwd=wkdir)
+ else:
+ subprocess.run(command_line, stdin=stdin, cwd=wkdir)
log.info(f">>> {software_name} End")
def get_reasonable_nb_cpu() -> int:
- """Get a reasonable number of processors depending on the total number of processors on
+ """
+ Get a reasonable number of processors depending on the total number of processors on
the host machine. Approximately 1/4 of the total number of processors will be used.
This function is generally used to set up a default value for the number of processors,
- the user can then override this value with the settings."""
+ the user can then override this value with the settings.
+ """
cpu_count = os.cpu_count()
if cpu_count is None:
+ log.warning(
+ "Could not figure out the number of CPU(s) on your machine. "
+ "This might be an issue with the OS you use."
+ )
return 1
return math.ceil(cpu_count / 4)
-def aircraft_name(tixi_or_cpacs):
+def check_nb_cpu(nb_proc: int) -> None:
+ """
+ Check if input nb_cpu from GUI is reasonable.
+ """
+ if (not os.cpu_count() > nb_proc):
+ log.warning(f"{nb_proc} CPUs is too much for your engine.")
+ nb_proc = get_reasonable_nb_cpu()
+ log.info(f"Using by default {nb_proc} CPUs.")
+
+
+def get_conditions_from_aeromap(aeromap: AeroMap) -> Tuple[List, List, List, List]:
+ alt_list = aeromap.get("altitude").tolist()
+ mach_list = aeromap.get("machNumber").tolist()
+ aoa_list = aeromap.get("angleOfAttack").tolist()
+ aos_list = aeromap.get("angleOfSideslip").tolist()
+ return alt_list, mach_list, aoa_list, aos_list
+
+
+def get_aeromap_conditions(cpacs: CPACS, uid_xpath: str) -> Tuple[List, List, List, List]:
+ """
+ Reads the flight conditions from the aeromap.
+ """
+ tixi = cpacs.tixi
+
+ # Get the first aeroMap as default one or create automatically one
+ aeromap_list = cpacs.get_aeromap_uid_list()
+
+ if aeromap_list:
+ aeromap_default = aeromap_list[0]
+
+ aeromap_uid = get_value_or_default(tixi, uid_xpath, aeromap_default)
+ log.info(f"Used aeromap: {aeromap_uid}.")
+ aeromap = cpacs.get_aeromap_by_uid(aeromap_uid)
+ alt_list, mach_list, aoa_list, aos_list = get_conditions_from_aeromap(aeromap)
+ else:
+ default_aeromap = cpacs.create_aeromap("DefaultAeromap")
+ default_aeromap.description = "Automatically created AeroMap."
+
+ mach = get_value(tixi, RANGE_CRUISE_MACH_XPATH)
+ alt = get_value(tixi, RANGE_CRUISE_ALT_XPATH)
+
+ default_aeromap.add_row(alt=alt, mach=mach, aos=0.0, aoa=0.0)
+ default_aeromap.save()
+
+ alt_list = [alt]
+ mach_list = [mach]
+ aoa_list = [0.0]
+ aos_list = [0.0]
+
+ aeromap_uid = get_value_or_default(tixi, uid_xpath, "DefaultAeromap")
+ log.info(f"{aeromap_uid} has been created.")
+
+ return alt_list, mach_list, aoa_list, aos_list
+
+
+def aircraft_name(tixi_or_cpacs) -> str:
"""The function get the name of the aircraft from the cpacs file or add a
default one if non-existant.
@@ -249,7 +564,7 @@ def aircraft_name(tixi_or_cpacs):
name = name.replace(" ", "_")
log.info(f"The name of the aircraft is : {name}")
- return name
+ return str(name)
def get_part_type(tixi, part_uid: str) -> str:
@@ -323,9 +638,16 @@ def remove_file_type_in_dir(directory: Path, file_type_list: List[str]) -> None:
file.unlink()
+def bool_(value) -> bool:
+ if str(value) in ["false", "False"]:
+ return False
+ else:
+ return True
+
# =================================================================================================
# MAIN
# =================================================================================================
+
if __name__ == "__main__":
log.info("Nothing to execute!")
diff --git a/ceasiompy/utils/commonnames.py b/ceasiompy/utils/commonnames.py
index 0de79f1c1..c6e8b8eb6 100644
--- a/ceasiompy/utils/commonnames.py
+++ b/ceasiompy/utils/commonnames.py
@@ -5,16 +5,23 @@
File to store the common names of files used in CEASIOMpy.
-Python version: >=3.8
| Author: Aidan jungo
| Creation: 2022-05-13
+| Modified: Leon Deligny
+| Date: 25 March 2025
-TODO:
+"""
- *
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
-"""
+from ceasiompy import log
+
+# =================================================================================================
+# CSTS
+# =================================================================================================
# CEASIOMpy Colors
CEASIOMPY_BLACK = "#000000"
@@ -29,7 +36,10 @@
# SU2
CONFIG_CFD_NAME = "ConfigCFD.cfg"
+CONFIG_DYNSTAB_NAME = "ConfigDYNSTAB.cfg"
+
SU2_FORCES_BREAKDOWN_NAME = "forces_breakdown.dat"
+SU2_DYNSTAB_FORCES_BREAKDOWN_NAME = "forces_breakdown_00000.dat"
SURFACE_FLOW_FILE_NAME = "surface_flow.vtu"
SURFACE_FLOW_FORCE_FILE_NAME = "surface_flow_forces.vtu"
FORCE_FILE_NAME = "forces.csv"
@@ -46,3 +56,10 @@
# PYCYCLE
ENGINE_BOUNDARY_CONDITIONS = "EngineBC.dat"
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/utils/commonpaths.py b/ceasiompy/utils/commonpaths.py
index 0aae18f49..1a85da3f0 100644
--- a/ceasiompy/utils/commonpaths.py
+++ b/ceasiompy/utils/commonpaths.py
@@ -6,26 +6,44 @@
List of paths which are used in CEASIOMpy, if possible base paths must be
called only from here to avoid mistakes.
-Python version: >=3.8
| Author: Aidan jungo
| Creation: 2022-04-28
+| Modified: Leon Deligny
+| Date: 25 March 2025
-TODO:
+"""
- *
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
-"""
+import ceasiompy.__init__
from pathlib import Path
-import ceasiompy.__init__
+from ceasiompy import log
+
+# =================================================================================================
+# CSTS
+# =================================================================================================
# /CEASIOMpy/
-CEASIOMPY_PATH = Path(ceasiompy.__init__.__file__).parents[1]
+CEASIOMPY_PATH = Path(ceasiompy.__file__).parents[1]
# /CEASIOMpy/ceasiompy/
-MODULES_DIR_PATH = Path(ceasiompy.__init__.__file__).parent
+MODULES_DIR_PATH = Path(ceasiompy.__file__).parent
+
+# /CEASIOMpy/ceasiompy/Database/databases/ceasiompy.db
+CEASIOMPY_DB_PATH = Path(MODULES_DIR_PATH, "Database", "databases", "ceasiompy.db")
+
+# /CEASIOMpy/ceasiompy/Database/tests/databases/testceasiompy.db
+TESTCEASIOMPY_DB_PATH = Path(
+ MODULES_DIR_PATH,
+ "Database",
+ "tests",
+ "databases",
+ "testceasiompy.db")
# /CEASIOMpy/documents/logos/CEASIOMpy_main_logos.png
CEASIOMPY_LOGO_PATH = Path(CEASIOMPY_PATH, "documents", "logos", "CEASIOMpy_512px.png")
@@ -39,13 +57,13 @@
# /CEASIOMpy/src/streamlit
STREAMLIT_PATH = Path(CEASIOMPY_PATH, "src", "streamlit")
-# /CEASIOMpy/ceasiompy/test_cases/
+# /CEASIOMpy/test_cases/
TEST_CASES_PATH = Path(CEASIOMPY_PATH, "test_cases")
-# /CEASIOMpy/ceasiompy/test_files/CPACSfiles/
+# /CEASIOMpy/test_files/CPACSfiles/
CPACS_FILES_PATH = Path(CEASIOMPY_PATH, "test_files", "CPACSfiles")
-# /CEASIOMpy/ceasiompy/test_files/ResultsFiles/
+# /CEASIOMpy/test_files/ResultsFiles/
TEST_RESULTS_FILES_PATH = Path(CEASIOMPY_PATH, "test_files", "ResultsFiles")
# /CEASIOMpy/ceasiompy/WKDIR/
@@ -53,3 +71,11 @@
# /CEASIOMpy/ceasiompy/SU2Run/files/default_paraview_state.pvsm
DEFAULT_PARAVIEW_STATE = Path(MODULES_DIR_PATH, "SU2Run", "files", "default_paraview_state.pvsm")
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/utils/commonxpath.py b/ceasiompy/utils/commonxpath.py
index 3fec34264..914bbae11 100644
--- a/ceasiompy/utils/commonxpath.py
+++ b/ceasiompy/utils/commonxpath.py
@@ -8,28 +8,40 @@
XPath may be changed from here to avoid mistakes, but if it is the case, be careful to also change
xpath and field name in all the test CPACS files, which are in the CEASIOMpy repository.
-Python version: >=3.8
| Author: Aidan jungo
| Creation: 2021-10-21
-| Last modifiction: 2024-01-05 (Mengmeng Zhang, added M-Edge XPATHs )
+| modification: 2024-01-05 (Mengmeng Zhang, added M-Edge XPATHs )
+| Modified: Leon Deligny
+| Date: 25 March 2025
-TODO:
-
- *
+# TODO: Someome please Organize these paths
"""
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+from ceasiompy import log
+
+# =================================================================================================
+# CSTS
+# =================================================================================================
+
# Header
AIRCRAFT_NAME_XPATH = "/cpacs/header/name"
# model
REF_XPATH = "/cpacs/vehicles/aircraft/model/reference"
+AREA_XPATH = "/cpacs/vehicles/aircraft/model/reference/area"
+LENGTH_XPATH = "/cpacs/vehicles/aircraft/model/reference/length"
+
FUSELAGES_XPATH = "/cpacs/vehicles/aircraft/model/fuselages"
WINGS_XPATH = "/cpacs/vehicles/aircraft/model/wings"
PYLONS_XPATH = "/cpacs/vehicles/aircraft/model/enginePylons"
ENGINES_XPATH = "/cpacs/vehicles/aircraft/model/engines"
-
+AIRFOILS_XPATH = "/cpacs/vehicles/profiles/wingAirfoils"
# analyses
AEROPERFORMANCE_XPATH = "/cpacs/vehicles/aircraft/model/analyses/aeroPerformance"
@@ -57,11 +69,18 @@
MESH_XPATH = CEASIOMPY_XPATH + "/mesh"
OPTIM_XPATH = CEASIOMPY_XPATH + "/optimisation"
PROP_XPATH = CEASIOMPY_XPATH + "/propulsion"
+PROPELLER_THRUST_XPATH = PROP_XPATH + "/propeller/thrust"
+PROPELLER_BLADE_LOSS_XPATH = PROP_XPATH + "/propeller/blade/loss"
+AEROMAP_TO_EXPORT_XPATH = CEASIOMPY_XPATH + "/export/aeroMapToExport"
+
RANGE_XPATH = CEASIOMPY_XPATH + "/ranges"
+RANGE_CRUISE_MACH_XPATH = RANGE_XPATH + "/CruiseMach"
+RANGE_CRUISE_ALT_XPATH = RANGE_XPATH + "/CruiseAltitude"
WEIGHT_XPATH = CEASIOMPY_XPATH + "/weight"
CLCALC_XPATH = CEASIOMPY_XPATH + "/aerodynamics/clCalculation"
PLOT_XPATH = CEASIOMPY_XPATH + "/aerodynamics/plotAeroCoefficient"
+AEROMAP_TO_PLOT_XPATH = PLOT_XPATH + "/aeroMapToPlot"
SF_XPATH = CEASIOMPY_XPATH + "/aerodynamics/skinFriction"
@@ -72,15 +91,14 @@
SMTRAIN_XPATH = CEASIOMPY_XPATH + "/surrogateModel"
SMUSE_XPATH = CEASIOMPY_XPATH + "/surrogateModelUse"
-STABILITY_STATIC_XPATH = CEASIOMPY_XPATH + "/stability/static"
-STABILITY_DYNAMIC_XPATH = CEASIOMPY_XPATH + "/stability/dynamic"
+STATICSTABILITY_XPATH = CEASIOMPY_XPATH + "/StaticStability"
+STATICSTABILITY_LR_XPATH = STATICSTABILITY_XPATH + "/LinearRegression"
OPTWKDIR_XPATH = CEASIOMPY_XPATH + "/filesPath/optimPath"
SMFILE_XPATH = CEASIOMPY_XPATH + "/filesPath/SMpath"
SU2MESH_XPATH = CEASIOMPY_XPATH + "/filesPath/su2Mesh"
EDGE_MESH_XPATH = CEASIOMPY_XPATH + "/filesPath/edgeMesh"
-SUMOFILE_XPATH = CEASIOMPY_XPATH + "/filesPath/sumoFilePath"
WKDIR_XPATH = CEASIOMPY_XPATH + "/filesPath/wkdirPath"
# Propulsion
@@ -89,9 +107,15 @@
# SUMO
SUMO_OUTPUT_MESH_FORMAT_XPATH = MESH_XPATH + "sumoOptions/format"
SUMO_REFINE_LEVEL_XPATH = MESH_XPATH + "/sumoOptions/refinementLevel"
-SUMO_INCLUDE_PYLON_XPATH = CEASIOMPY_XPATH + "/engine/includePylon"
-SUMO_INCLUDE_ENGINE_XPATH = CEASIOMPY_XPATH + "/engine/includeEngine"
+SPECIFIED_SUMOFILE_XPATH = CEASIOMPY_XPATH + "/SUMOAutoMesh" + "/specifiedSumoPath"
+
+# CPACS2SUMO
+CPACS2SUMO_XPATH = CEASIOMPY_XPATH + "/CPACS2SUMO"
+CPACS2SUMO_SUMO_GUI_XPATH = CPACS2SUMO_XPATH + "/GUI"
+SUMO_INCLUDE_PYLON_XPATH = CPACS2SUMO_XPATH + "/engine/includePylon"
+SUMO_INCLUDE_ENGINE_XPATH = CPACS2SUMO_XPATH + "/engine/includeEngine"
+SUMOFILE_XPATH = CPACS2SUMO_XPATH + "/filesPath/sumoFilePath"
# GMSH
GMSH_XPATH = MESH_XPATH + "/gmshOptions"
GMSH_OPEN_GUI_XPATH = GMSH_XPATH + "/open_gui"
@@ -105,6 +129,7 @@
GMSH_MESH_SIZE_FUSELAGE_XPATH = GMSH_XPATH + "/mesh_size/fuselage/value"
GMSH_MESH_SIZE_FACTOR_FUSELAGE_XPATH = GMSH_XPATH + "/mesh_size/fuselage/factor"
GMSH_MESH_SIZE_WINGS_XPATH = GMSH_XPATH + "/mesh_size/wings/value"
+GMSH_MESH_SIZE_CTRLSURFS_XPATH = GMSH_XPATH + "/mesh_size/controlsurfaces/value"
GMSH_MESH_SIZE_FACTOR_WINGS_XPATH = GMSH_XPATH + "/mesh_size/wings/factor"
GMSH_MESH_SIZE_ENGINES_XPATH = GMSH_XPATH + "/mesh_size/engines"
GMSH_MESH_SIZE_PROPELLERS_XPATH = GMSH_XPATH + "/mesh_size/propellers"
@@ -121,6 +146,7 @@
GMSH_GROWTH_RATIO_XPATH = GMSH_XPATH + "/growth_ratio"
GMSH_SURFACE_MESH_SIZE_XPATH = GMSH_XPATH + "min_max_mesh_factor"
GMSH_FEATURE_ANGLE_XPATH = GMSH_XPATH + "/feature_angle"
+GMSH_CTRLSURF_ANGLE_XPATH = GMSH_XPATH + "/DeflectionAngle"
# SU2
SU2_XPATH = CEASIOMPY_XPATH + "/aerodynamics/su2"
@@ -128,6 +154,7 @@
SU2_NB_CPU_XPATH = SU2_XPATH + "/settings/nbCPU"
SU2_EXTRACT_LOAD_XPATH = SU2_XPATH + "/results/extractLoads"
SU2_UPDATE_WETTED_AREA_XPATH = SU2_XPATH + "/results/updateWettedArea"
+USED_SU2_MESH_XPATH = SU2_XPATH + "/MeshPath"
SU2_MAX_ITER_XPATH = SU2_XPATH + "/settings/maxIter"
SU2_CFL_NB_XPATH = SU2_XPATH + "/settings/cflNumber/value"
@@ -147,12 +174,23 @@
SU2_DAMPING_DER_XPATH = SU2_XPATH + "/options/calculateDampingDerivatives"
SU2_ROTATION_RATE_XPATH = SU2_XPATH + "/options/rotationRate"
-SU2_CONTROL_SURF_XPATH = SU2_XPATH + "/options/calculateControlSurfacesDeflections"
+SU2_CONTROL_SURF_XPATH = SU2_XPATH + "/ControlSurfaces"
+SU2_CONTROL_SURF_BOOL_XPATH = SU2_CONTROL_SURF_XPATH + "/Bool"
+SU2_CONTROL_SURF_ANGLE_XPATH = SU2_CONTROL_SURF_XPATH + "/Angle"
SU2_DEF_MESH_XPATH = SU2_XPATH + "/availableDeformedMesh"
SU2_ACTUATOR_DISK_XPATH = SU2_XPATH + "/options/includeActuatorDisk"
SU2_CONFIG_RANS_XPATH = SU2_XPATH + "/options/config_type"
+SU2_DYNAMICDERIVATIVES_XPATH = SU2_XPATH + "/DynamicDerivatives"
+SU2_DYNAMICDERIVATIVES_BOOL_XPATH = SU2_DYNAMICDERIVATIVES_XPATH + "/Bool"
+SU2_DYNAMICDERIVATIVES_TIMESIZE_XPATH = SU2_DYNAMICDERIVATIVES_XPATH + "/TimeSize"
+SU2_DYNAMICDERIVATIVES_AMPLITUDE_XPATH = SU2_DYNAMICDERIVATIVES_XPATH + "/Amplitude"
+SU2_DYNAMICDERIVATIVES_FREQUENCY_XPATH = SU2_DYNAMICDERIVATIVES_XPATH + "/AngularFrequency"
+SU2_DYNAMICDERIVATIVES_INNERITER_XPATH = SU2_DYNAMICDERIVATIVES_XPATH + "/InnerIter"
+SU2_DYNAMICDERIVATIVES_DATA_XPATH = SU2_DYNAMICDERIVATIVES_XPATH + "/Data"
+SU2_CEASIOMPYDATA_XPATH = SU2_XPATH + "/CeasiompyData"
+
# EDGE
EDGE_XPATH = CEASIOMPY_XPATH + "/aerodynamics/medge"
EDGE_AEROMAP_UID_XPATH = EDGE_XPATH + "/aeroMapUID"
@@ -195,27 +233,39 @@
WB_TOILET_LENGTH_XPATH = GEOM_XPATH + "/toiletLength"
WB_DOUBLE_FLOOR_XPATH = GEOM_XPATH + "/isDoubleFloor"
-# pytornado
-PYTORNADO_XPATH = "/cpacs/toolspecific/pytornado"
-PYTORNADO_EXTRACT_LOAD_XPATH = PYTORNADO_XPATH + "/save_results/extractLoads"
-
# Stability
STABILITY_XPATH = CEASIOMPY_XPATH + "/stability"
STABILITY_AEROMAP_TO_ANALYZE_XPATH = STABILITY_XPATH + "/aeroMapToAnalyze"
-CHECK_LONGITUDINAL_STABILITY_XPATH = STABILITY_XPATH + "/stabilityToCheck/longitudinal"
-CHECK_DIRECTIONAL_STABILITY_XPATH = STABILITY_XPATH + "/stabilityToCheck/directional"
-CHECK_LATERAL_STABILITY_XPATH = STABILITY_XPATH + "/stabilityToCheck/lateral"
+CHECK_STABILITY_XPATH = STABILITY_XPATH + "/stabilityToCheck"
# PYCYCLE
ENGINE_TYPE_XPATH = CEASIOMPY_XPATH + "/ThermoData"
ENGINE_BC = CEASIOMPY_XPATH + "/BC"
-
-# AVL
-AVL_XPATH = CEASIOMPY_XPATH + "/aerodynamics/avl"
-AVL_AEROMAP_UID_XPATH = AVL_XPATH + "/aeroMapUID"
-AVL_PLOT_XPATH = AVL_XPATH + "/SavePlots"
-AVL_FUSELAGE_XPATH = AVL_XPATH + "/IntegrateFuselage"
-AVL_VORTEX_DISTR_XPATH = AVL_XPATH + "/VortexDistribution"
+ENGINE_BC_TEMPERATUREOUTLET_XPATH = ENGINE_BC + "/TemperatureOutlet"
+ENGINE_BC_PRESSUREOUTLET_XPATH = ENGINE_BC + "/PressureOutlet"
+
+# DYNAMICSTABILITY
+DYNAMICSTABILITY_XPATH = CEASIOMPY_XPATH + "/DynamicStability"
+DYNAMICSTABILITY_AIRCRAFT_XPATH = DYNAMICSTABILITY_XPATH + "/Aircraft"
+DYNAMICSTABILITY_CEASIOMPYDATA_XPATH = DYNAMICSTABILITY_XPATH + "/CeasiompyData"
+DYNAMICSTABILITY_AEROMAP_UID_XPATH = DYNAMICSTABILITY_XPATH + "/aeroMapUID"
+DYNAMICSTABILITY_NCHORDWISE_XPATH = DYNAMICSTABILITY_XPATH + "/NChordwise"
+DYNAMICSTABILITY_NSPANWISE_XPATH = DYNAMICSTABILITY_XPATH + "/NSpanwise"
+DYNAMICSTABILITY_WINGS_XPATH = DYNAMICSTABILITY_XPATH + "/WingSelection"
+DYNAMICSTABILITY_VISUALIZATION_XPATH = DYNAMICSTABILITY_XPATH + "/Visualization"
+DYNAMICSTABILITY_CGRID_XPATH = DYNAMICSTABILITY_XPATH + "/CGrid"
+DYNAMICSTABILITY_SOFTWARE_XPATH = DYNAMICSTABILITY_XPATH + "/Software"
+DYNAMICSTABILITY_MACHLIST_XPATH = DYNAMICSTABILITY_XPATH + "/Mach"
+
+# CPACSUPDATER
+CPACSUPDATER_XPATH = CEASIOMPY_XPATH + "/CPACSUpdater"
+CPACSUPDATER_CTRLSURF_XPATH = CPACSUPDATER_XPATH + "/CtrlSurf"
+CPACSUPDATER_ADD_CTRLSURFACES_XPATH = CEASIOMPY_XPATH + "/AddControlSurfaces"
+CPACSUPDATER_CPACSCREATOR_XPATH = CPACSUPDATER_XPATH + "/CPACSCreator"
+
+# DATABASE
+DATABASE_XPATH = CEASIOMPY_XPATH + "/Database"
+DATABASE_STOREDATA_XPATH = DATABASE_XPATH + "/StoreData"
# FramAT
FRAMAT_XPATH = CEASIOMPY_XPATH + "/structure/FramAT"
@@ -227,3 +277,10 @@
# AeroFrame
AEROFRAME_XPATH = CEASIOMPY_XPATH + "/aeroelasticity/AeroFrame"
AEROFRAME_SETTINGS = AEROFRAME_XPATH + "/Settings"
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/utils/configfiles.py b/ceasiompy/utils/configfiles.py
index 05d6cc416..3481b585d 100644
--- a/ceasiompy/utils/configfiles.py
+++ b/ceasiompy/utils/configfiles.py
@@ -5,14 +5,11 @@
Functions to simplify some file and data handling in CEASIOMpy
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2021-12-10
-
-TODO:
-
- *
+| Modified: Leon Deligny
+| Date: 25 March 2025
"""
@@ -22,10 +19,7 @@
from collections import OrderedDict
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
-
+from ceasiompy import log
# =================================================================================================
# CLASSES
@@ -104,6 +98,9 @@ def read_file(self, file):
def write_file(self, file, overwrite=False):
"""Write a .cfg configuration file."""
+ # Ensure the directory exists
+ file.parent.mkdir(parents=True, exist_ok=True)
+
if file.is_file():
if overwrite:
file.unlink()
@@ -123,9 +120,9 @@ def write_file(self, file, overwrite=False):
f.write(f"{key} = NONE\n")
elif isinstance(value, list):
if any("(" in str(val) for val in value):
- f.write(f"{key} = {'; '.join(map(str,value))}\n")
+ f.write(f"{key} = {'; '.join(map(str, value))}\n")
else:
- f.write(f"{key} = ( {', '.join(map(str,value))} )\n")
+ f.write(f"{key} = ( {', '.join(map(str, value))} )\n")
else:
f.write(f"{key} = {value}\n")
@@ -147,23 +144,17 @@ def __str__(self) -> str:
text_line.append(f"{key} = NONE\n")
elif isinstance(value, list):
if any("(" in str(val) for val in value):
- text_line.append(f"{key} = {'; '.join(map(str,value))}\n")
+ text_line.append(f"{key} = {'; '.join(map(str, value))}\n")
else:
- text_line.append(f"{key} = ( {', '.join(map(str,value))} )\n")
+ text_line.append(f"{key} = ( {', '.join(map(str, value))} )\n")
else:
text_line.append(f"{key} = {value}")
return ("\n").join(text_line)
-
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
-
-
# =================================================================================================
# MAIN
# =================================================================================================
-if __name__ == "__main__":
- print("Nothing to execute")
+if __name__ == "__main__":
+ log.info("Nothing to execute")
diff --git a/ceasiompy/utils/decorators.py b/ceasiompy/utils/decorators.py
new file mode 100644
index 000000000..9777bdedf
--- /dev/null
+++ b/ceasiompy/utils/decorators.py
@@ -0,0 +1,76 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Scripts to convert CPACS file geometry into any geometry.
+
+
+| Author : Leon Deligny
+| Creation: 2025-Feb-12
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+import time
+
+from functools import wraps
+
+from typing import (
+ Dict,
+ Tuple,
+ Callable,
+)
+
+from ceasiompy import log
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+# TODO: Implement CEASIOMpy's own validate function
+def validate_call():
+ pass
+
+
+def log_execution(f: Callable) -> Callable:
+ """
+ Debugging decorator.
+ Logs function f and adds time for the function to finish.
+ """
+ @wraps(f)
+ def wrapper(*args: Tuple, **kwargs: Dict):
+ start_time = time.time()
+ log.info(f"Executing {f.__name__}.")
+ result = f(*args, **kwargs)
+ end_time = time.time()
+ log.info(f"Finished {f.__name__} in {end_time - start_time:.4f} seconds.")
+ return result
+ return wrapper
+
+
+def log_test(f: Callable) -> Callable:
+ """
+ Log Test function decorator
+ """
+ @wraps(f)
+ def wrapper(*args: Tuple, **kwargs: Dict):
+ start_time = time.time()
+ log.info(f"Testing {f.__name__}.")
+ result = f(*args, **kwargs)
+ end_time = time.time()
+ log.info(f"Finished testing {f.__name__} in {end_time - start_time:.4f} seconds. \n")
+ return result
+ return wrapper
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute.")
diff --git a/ceasiompy/utils/doc/input_toolspecifics.xml b/ceasiompy/utils/doc/input_toolspecifics.xml
index a8800ce48..a580e9eb1 100644
--- a/ceasiompy/utils/doc/input_toolspecifics.xml
+++ b/ceasiompy/utils/doc/input_toolspecifics.xml
@@ -192,36 +192,6 @@
[2, 33, 444]
-
- No default value
- False
- 20
- 5
-
-
- False
- False
-
-
- False
- False
-
-
- False
- False
-
-
- False
- False
-
-
-
- False
- False
- False
- False
-
-
diff --git a/ceasiompy/utils/generalclasses.py b/ceasiompy/utils/generalclasses.py
index eb97c735f..ccf1055d2 100644
--- a/ceasiompy/utils/generalclasses.py
+++ b/ceasiompy/utils/generalclasses.py
@@ -5,30 +5,20 @@
General classes to get transformation from any part of a CPACS file
-Python version: >=3.8
| Author: Aidan Jungo
| Creation: 2021-02-25
-TODO:
-
- *
-
"""
# =================================================================================================
# IMPORTS
# =================================================================================================
-from pathlib import Path
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from tixi3.tixi3wrapper import Tixi3Exception
-log = get_logger()
-
-MODULE_DIR = Path(__file__).parent
-
# =================================================================================================
# CLASSES
# =================================================================================================
@@ -96,7 +86,7 @@ def get_cpacs_points(self, tixi, xpath):
class Transformation:
"""
- The Class "Transformation" store scaling, rotation and tanlsation by
+ The Class "Transformation" store scaling, rotation and translation by
calling the class "Point"
Attributes:
@@ -113,8 +103,9 @@ def __init__(self):
self.translation = Point()
def get_cpacs_transf(self, tixi, xpath):
- """Get scaling,rotation and translation from a given path in the
- CPACS file
+ """
+ Get scaling, rotation and translation
+ from a given path in the CPACS file
Args:
tixi (handles): TIXI Handle of the CPACS file
@@ -165,4 +156,4 @@ def get_parent_transformation(self):
if __name__ == "__main__":
- print("Nothing to execute!")
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/utils/geometryfunctions.py b/ceasiompy/utils/geometryfunctions.py
new file mode 100644
index 000000000..af44a2a45
--- /dev/null
+++ b/ceasiompy/utils/geometryfunctions.py
@@ -0,0 +1,758 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed by CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Scripts to convert CPACS file geometry into any geometry.
+
+
+| Author : Leon Deligny
+| Creation: 12-Feb-2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+import math
+import sqlite3
+import numpy as np
+
+from math import prod
+from numpy import array
+from cpacspy.cpacsfunctions import get_float_vector
+from ceasiompy.utils.mathsfunctions import (
+ euler2fix,
+ rotate_points,
+)
+
+from numpy import ndarray
+from tixi3.tixi3wrapper import Tixi3
+from ceasiompy.Database.func.storing import CeasiompyDb
+from typing import (
+ List,
+ Tuple,
+)
+from ceasiompy.utils.generalclasses import (
+ Transformation,
+ Point,
+)
+
+from ceasiompy import log
+from ceasiompy.Database.func import ALLOWED_TABLES
+from ceasiompy.utils.commonpaths import CEASIOMPY_DB_PATH
+from ceasiompy.utils.commonxpath import WINGS_XPATH
+
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def get_aircrafts_list() -> List:
+ """
+ Access different aircraft names from "ceasiompy.db".
+
+ Returns:
+ (List): Aircraft names.
+
+ """
+ # Codacy: Table and column names are strictly validated against whitelisted values.
+ # Check if database exists
+ if CEASIOMPY_DB_PATH.exists():
+ # Go look in database for all different aircraft names among all different tables
+ aircrafts = set()
+ db = CeasiompyDb()
+
+ try:
+ # Get all table names
+ db.cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
+ tables = db.cursor.fetchall()
+
+ for table in tables:
+ table_name = table[0]
+
+ # Validate table name
+ if table_name not in ALLOWED_TABLES + ["sqlite_sequence"]:
+ raise ValueError(f"Invalid table name: {table_name}")
+
+ # Check if the table has an "aircraft" column
+ db.cursor.execute(f"PRAGMA table_info({table_name})")
+ columns = db.cursor.fetchall()
+ if any(column[1] == "aircraft" for column in columns):
+ # Query for distinct aircraft names from the table
+ db.cursor.execute(
+ f"SELECT DISTINCT aircraft FROM {table_name}") # nosec
+ rows = db.cursor.fetchall()
+ for row in rows:
+ aircrafts.add(row[0])
+
+ except sqlite3.Error as e:
+ log.warning(f"An error occurred: {e}.")
+
+ db.close()
+
+ return list(aircrafts)
+
+ else:
+ return ["Can't use ceasiompy.db"]
+
+
+def convert_fuselage_profiles(
+ tixi: Tixi3, sec_xpath: str,
+ i_sec: int, i_elem: int,
+ pos_y_list: List, pos_z_list: List,
+) -> Tuple[Transformation, float, float, ndarray, ndarray]:
+ elem_xpath = sec_xpath + "/elements/element[" + str(i_elem + 1) + "]"
+ elem_uid = get_uid(tixi, elem_xpath)
+ elem_transf = Transformation()
+ elem_transf.get_cpacs_transf(tixi, elem_xpath)
+ check_if_rotated(elem_transf.rotation, elem_uid)
+
+ prof_uid, prof_vect_x, prof_vect_y, prof_vect_z = get_profile_coord(
+ tixi,
+ elem_xpath + "/profileUID"
+ )
+
+ # Calculate profile sizes
+ prof_vect_x = np.max(prof_vect_x) - np.min(prof_vect_x)
+ if not prof_vect_x == 0.0:
+ log.warning(
+ f"Issue with profile {prof_uid} as prof_vect_x not equal to 0.0"
+ )
+
+ prof_size_y = (np.max(prof_vect_y) - np.min(prof_vect_y)) / 2
+ prof_size_z = (np.max(prof_vect_z) - np.min(prof_vect_z)) / 2
+
+ # Normalize profile vectors
+ prof_vect_y /= prof_size_y
+ prof_vect_z /= prof_size_z
+
+ # Shift profile vectors
+ prof_min_y = np.min(prof_vect_y)
+ prof_min_z = np.min(prof_vect_z)
+
+ prof_vect_y -= (1 + prof_min_y)
+ prof_vect_z -= (1 + prof_min_z)
+
+ # Could be a problem if they are less positionings than sections
+ # TODO: solve that!
+ pos_y_list[i_sec] += (1 + prof_min_y) * prof_size_y * elem_transf.scaling.y
+ pos_z_list[i_sec] += (1 + prof_min_z) * prof_size_z * elem_transf.scaling.z
+
+ return elem_transf, prof_size_y, prof_size_z, prof_vect_y, prof_vect_z
+
+
+def get_profile_coord(
+ tixi: Tixi3,
+ uid_xpath: str,
+) -> Tuple[ndarray, ndarray, ndarray]:
+ """
+ Get profile coordinate points.
+ """
+ prof_uid = tixi.getTextElement(uid_xpath)
+ pointlist_xpath = tixi.uIDGetXPath(prof_uid) + "/pointList"
+
+ if not tixi.checkElement(pointlist_xpath):
+ raise ValueError(f"xPath {pointlist_xpath} not found.")
+ prof_vect_x = array(get_float_vector(tixi, pointlist_xpath + "/x"))
+ prof_vect_y = array(get_float_vector(tixi, pointlist_xpath + "/y"))
+ prof_vect_z = array(get_float_vector(tixi, pointlist_xpath + "/z"))
+
+ return prof_uid, prof_vect_x, prof_vect_y, prof_vect_z
+
+
+def sum_points(*points: Point) -> Tuple[float, float, float]:
+ """
+ Adds points together by summing their x, y, z coordinates.
+ """
+ return (
+ sum(point.x for point in points),
+ sum(point.y for point in points),
+ sum(point.z for point in points),
+ )
+
+
+def prod_points(*points: Point) -> Tuple[float, float, float]:
+ """
+ Multiplies points together by summing their x, y, z coordinates.
+ """
+ return (
+ prod(point.x for point in points),
+ prod(point.y for point in points),
+ prod(point.z for point in points),
+ )
+
+
+def check_if_rotated(rotation: Point, elem_uid: str) -> None:
+ if rotation.x or rotation.y or rotation.z:
+ log.warning(
+ f"Element '{elem_uid}' is rotated, it is"
+ "not possible to take that into account in SUMO !"
+ )
+
+
+def find_wing_xpath(tixi: Tixi3, wing_name: str) -> str:
+ """
+ Find the XPath of a wing by its name in the CPACS file.
+
+ Raises:
+ ValueError: If the wing with the specified name is not found.
+ """
+ # Load constant
+ wings_xpath = WINGS_XPATH
+
+ # Get xPath from name
+ wing_cnt = elements_number(tixi, wings_xpath, "wing", logg=False)
+
+ for i in range(1, wing_cnt + 1):
+ current_wing_xpath = f"{wings_xpath}/wing[{i}]"
+ current_wing_name = tixi.getTextElement(f"{current_wing_xpath}/name")
+
+ if current_wing_name == wing_name:
+ return current_wing_xpath
+
+ raise ValueError(f"Wing with name '{wing_name}' not found.")
+
+
+def get_segments_wing(tixi: Tixi3, wing_name: str) -> List[Tuple[str, str, str]]:
+ # Define constants
+ wing_xpath = find_wing_xpath(tixi, wing_name)
+ segments_xpath = wing_xpath + "/segments"
+ wing_name = tixi.getTextElement(wing_xpath + "/name")
+ segment_cnt = elements_number(tixi, segments_xpath, "segment", logg=False)
+
+ # Define variable
+ segment_list = []
+
+ #
+ for i_seg in range(segment_cnt):
+ segment_xpath = segments_xpath + f"/segment[{i_seg + 1}]"
+ segment_name = tixi.getTextElement(segment_xpath + "/name")
+ segment_from_uid = tixi.getTextElement(segment_xpath + "/fromElementUID")
+ segment_to_uid = tixi.getTextElement(segment_xpath + "/toElementUID")
+
+ segment_list.append((segment_name, segment_from_uid, segment_to_uid))
+
+ return segment_list
+
+
+def get_segments(tixi: Tixi3) -> List[Tuple[str, str]]:
+ """
+ Retrieve and return a sorted list of
+ unique wing segment names from a Tixi3 document.
+
+ Args:
+ tixi (Tixi3): Tixi3 handle of the CPACS file.
+
+ Returns:
+ (List[Tuple[str, str]]): Sorted list of tuples, with (wing, segment) name.
+
+ """
+
+ wing_cnt = elements_number(tixi, WINGS_XPATH, "wing", logg=False)
+ segment_list = []
+
+ # Iterate through each wing and get its uID
+ for i_wing in range(wing_cnt):
+ wing_xpath = WINGS_XPATH + f"/wing[{i_wing + 1}]"
+ segments_xpath = wing_xpath + "/segments"
+ wing_name = tixi.getTextElement(wing_xpath + "/name")
+ segment_cnt = elements_number(tixi, segments_xpath, "segment", logg=False)
+
+ for i_seg in range(segment_cnt):
+ seg_xpath = segments_xpath + f"/segment[{i_seg + 1}]/name"
+ segment_name = tixi.getTextElement(seg_xpath)
+ segment_list.append((wing_name, segment_name))
+
+ unique_segment_list = list(set(segment_list))
+
+ if len(unique_segment_list) != len(segment_list):
+ log.warning("Some segments are identically defined.")
+
+ # Sort the segment_list by the first and then the second value of the tuples
+ unique_segment_list.sort(key=lambda x: (x[0], x[1]))
+
+ return unique_segment_list
+
+
+def corrects_airfoil_profile(
+ prof_vect_x: ndarray,
+ prof_vect_y: ndarray,
+ prof_vect_z: ndarray
+) -> float:
+ """
+ Process airfoil profiles correctly.
+
+ Args:
+ prof_vect_x (ndarray): x-th coordinate of airfoil's profile.
+ prof_vect_y (ndarray): y-th coordinate of airfoil's profile.
+ prof_vect_z (ndarray): z-th coordinate of airfoil's profile.
+
+ Returns:
+ wg_sec_chord (float): Wing's section chord length.
+
+ """
+ prof_size_x = np.max(prof_vect_x) - np.min(prof_vect_x)
+ prof_size_y = np.max(prof_vect_y) - np.min(prof_vect_y)
+ prof_size_z = np.max(prof_vect_z) - np.min(prof_vect_z)
+
+ default = 0.0
+ if prof_size_y != 0.0:
+ log.error("An airfoil profile is not defined correctly: prof_size_y should be 0.0")
+ return default
+
+ if prof_size_x == 0.0:
+ if prof_size_z == 0.0:
+ log.warning("Invalid airfoil profile: prof_size_x and prof_size_z are both 0.0")
+ else:
+ log.warning("Invalid airfoil profile: prof_size_x is 0.0")
+ return default
+
+ # Apply scaling
+ prof_vect_x /= prof_size_x
+ prof_vect_z /= prof_size_x
+
+ # Return the chord length
+ return prof_size_x
+
+
+def get_chord_span(tixi: Tixi3, wing_xpath: str) -> Tuple[float, float]:
+ """
+ Returns chord and span length of specific wing (wing_xpath)
+ from a CPACS file (tixi).
+
+ Args:
+ tixi (Tixi3): Tixi handle of CPACS file.
+ wing_xpath (str): xPath of wing.
+
+ Returns:
+ (Tuple[float, float]):
+ - c_ref (float): Chord length of wing.
+ - s_ref (float): Span length of wing.
+
+ """
+ # Wing Positionings
+ _, pos_x_list, pos_y_list, pos_z_list = get_positionings(tixi, wing_xpath, "wing")
+
+ # Wing Sections
+ le_list = wing_sections(
+ tixi,
+ wing_xpath,
+ pos_x_list,
+ pos_y_list,
+ pos_z_list,
+ list_type='first_n_last'
+ )
+
+ first_le = le_list[0]
+ last_le = le_list[1]
+
+ y1 = first_le[1]
+ d12 = first_le[3]
+
+ y4 = last_le[1]
+
+ s_ref = y4 - y1
+
+ return d12, s_ref
+
+
+def return_namewings(tixi: Tixi3) -> List:
+ """
+ Returns the names of all wings in the tixi handle of the CPACS file.
+ """
+
+ # Initialize list to store wing names
+ wing_names = []
+
+ wing_cnt = elements_number(tixi, WINGS_XPATH, "wing")
+
+ # Iterate through each wing and get its name
+ for i_wing in range(wing_cnt):
+ wing_xpath = WINGS_XPATH + "/wing[" + str(i_wing + 1) + "]"
+ wing_name = tixi.getTextElement(wing_xpath + "/name")
+ wing_names.append(wing_name)
+
+ return wing_names
+
+
+def return_uidwings(tixi: Tixi3) -> List:
+ """
+ Returns the uIDs of all wings in the tixi handle of the CPACS file.
+ """
+
+ # Initialize list to store wing uIDs
+ wing_uids = []
+
+ wing_cnt = elements_number(tixi, WINGS_XPATH, "wing")
+
+ # Iterate through each wing and get its uID
+ for i_wing in range(wing_cnt):
+ wing_xpath = WINGS_XPATH + "/wing[" + str(i_wing + 1) + "]"
+ uid_wing = tixi.getTextAttribute(wing_xpath, "uID")
+ wing_uids.append(uid_wing)
+
+ return wing_uids
+
+
+def get_uid(tixi: Tixi3, xpath: str) -> str:
+ return tixi.getTextAttribute(xpath, "uID")
+
+
+def elements_number(tixi: Tixi3, xpath: str, element: str, logg: bool = True) -> int:
+ """
+ Computes number of elements found in CPACS file.
+
+ Args:
+ tixi (Tixi3): TIXI Handle of the CPACS file.
+ xpath (str): xPath to the fuselage/wing/pylon element in the CPACS file.
+ element (str): Either "fuselage", "wing" or "enginePylon" etc.
+ logg (bool): Add log messages.
+
+ Returns:
+ ele_cnt (int): Number of elements found in CPACS file.
+
+ """
+
+ ele_cnt = 0
+ if tixi.checkElement(xpath):
+ ele_cnt = int(tixi.getNamedChildrenCount(xpath, element))
+ if logg:
+ if ele_cnt > 1:
+ log.info(str(ele_cnt) + " " + str(element) + "s have been found.")
+ elif ele_cnt == 1:
+ log.info(str(ele_cnt) + " " + str(element) + " has been found.")
+ elif ele_cnt == 0:
+ log.warning(f"No {element} has been found in this CPACS file.")
+ else:
+ log.warning(f"Incorrect element number {ele_cnt}.")
+
+ else:
+ log.warning(f"xPath {xpath} does not exist.")
+
+ return ele_cnt
+
+
+def get_section_uid(tixi: Tixi3, pos_xpath: str, uid_type: str) -> str:
+ """Helper to retrieve section UID."""
+ uid_xpath = f"{pos_xpath}/{uid_type}"
+ return tixi.getTextElement(uid_xpath) if tixi.checkElement(uid_xpath) else ""
+
+
+def get_positionings(tixi: Tixi3, xpath: str, element: str = "") -> Tuple[int, List, List, List]:
+ """
+ Retrieve and compute the positionings for an element from the CPACS file.
+
+ It computes the cumulative translations for each positioning and
+ returns the lists of x, y, and z translations from the reference point.
+
+ Args:
+ tixi (Tixi3): TIXI Handle of the CPACS file.
+ xpath (str): xPath to the fuselage/wing/pylon element in the CPACS file.
+
+ Returns:
+ sec_cnt (int): Number of sections found at xpath.
+ pos_x_list (list): List of x translations for each positioning.
+ pos_y_list (list): List of y translations for each positioning.
+ pos_z_list (list): List of z translations for each positioning.
+
+ """
+
+ positioning = "positioning"
+ positionings = positioning + "s"
+
+ # Sections
+ sec_cnt = elements_number(tixi, xpath + "/sections", "section", logg=False)
+
+ # Positionings list
+ pos_x_list, pos_y_list, pos_z_list = [], [], []
+
+ if tixi.checkElement(xpath + f"/{positionings}"):
+ pos_cnt = elements_number(tixi, xpath + f"/{positionings}", positioning, logg=False)
+ from_sec_list, to_sec_list = [], []
+
+ for i_pos in range(pos_cnt):
+ pos_xpath = xpath + f"/{positionings}/{positioning}[" + str(i_pos + 1) + "]"
+
+ length = tixi.getDoubleElement(pos_xpath + "/length")
+ sweep = math.radians(tixi.getDoubleElement(pos_xpath + "/sweepAngle"))
+ dihedral = math.radians(tixi.getDoubleElement(pos_xpath + "/dihedralAngle"))
+
+ # Get the corresponding translation of each positioning.
+ # This is correct. But why ? TODO: Add explaination.
+ pos_x_list.append(length * math.sin(sweep))
+ pos_y_list.append(length * math.cos(dihedral) * math.cos(sweep))
+ pos_z_list.append(length * math.sin(dihedral) * math.cos(sweep))
+
+ # Get which section are connected by the positioning
+ from_sec_list.append(get_section_uid(tixi, pos_xpath, "/fromSectionUID"))
+ to_sec_list.append(get_section_uid(tixi, pos_xpath, "/toSectionUID"))
+
+ # Re-loop though the positioning to re-order them
+ for j_pos in range(pos_cnt):
+ if from_sec_list[j_pos] == "":
+ prev_pos_x, prev_pos_y, prev_pos_z = 0, 0, 0
+
+ elif from_sec_list[j_pos] == to_sec_list[j_pos - 1]:
+ prev_pos_x = pos_x_list[j_pos - 1]
+ prev_pos_y = pos_y_list[j_pos - 1]
+ prev_pos_z = pos_z_list[j_pos - 1]
+
+ else:
+ index_prev = to_sec_list.index(from_sec_list[j_pos])
+ prev_pos_x = pos_x_list[index_prev]
+ prev_pos_y = pos_y_list[index_prev]
+ prev_pos_z = pos_z_list[index_prev]
+
+ pos_x_list[j_pos] += prev_pos_x
+ pos_y_list[j_pos] += prev_pos_y
+ pos_z_list[j_pos] += prev_pos_z
+
+ else:
+ log.warning(f'No "{positionings}" have been found in: {element}.')
+ zero_cnt = [0.0] * sec_cnt
+ pos_x_list = zero_cnt
+ pos_y_list = zero_cnt
+ pos_z_list = zero_cnt
+
+ return sec_cnt, pos_x_list, pos_y_list, pos_z_list
+
+
+def get_section_rotation(
+ tixi: Tixi3,
+ i_sec: int,
+ wing_sections_xpath: str,
+ wing_transf: Transformation,
+ wg_sk_transf: Transformation,
+) -> Tuple[Point, float, ndarray, ndarray, ndarray, str]:
+ # Access section xpath
+ sec_xpath = wing_sections_xpath + "/section[" + str(i_sec + 1) + "]"
+ sec_uid = tixi.getTextAttribute(sec_xpath, "uID")
+ sec_transf = Transformation()
+ sec_transf.get_cpacs_transf(tixi, sec_xpath)
+
+ # Get the number of elements
+ elem_cnt = elements_number(tixi, sec_xpath, "element", logg=False)
+ if elem_cnt > 1:
+ log.warning(
+ f"Sections {sec_uid} contains {elem_cnt} elements !"
+ )
+
+ # Access element xpath
+ elem_xpath = sec_xpath + "/elements/element[1]"
+ elem_transf = Transformation()
+ elem_transf.get_cpacs_transf(tixi, elem_xpath)
+
+ # Get wing profile (airfoil)
+ prof_uid, prof_vect_x, prof_vect_y, prof_vect_z = get_profile_coord(
+ tixi,
+ elem_xpath + "/airfoilUID"
+ )
+
+ # Apply scaling using numpy operations
+ x, y, z = prod_points(
+ elem_transf.scaling,
+ sec_transf.scaling,
+ wing_transf.scaling
+ )
+
+ prof_vect_x *= x
+ prof_vect_y *= y
+ prof_vect_z *= z
+
+ wg_sec_chord = corrects_airfoil_profile(prof_vect_x, prof_vect_y, prof_vect_z)
+
+ # Adding the two angles: May not work in every case !!!
+ x, y, z = sum_points(elem_transf.rotation, sec_transf.rotation, wg_sk_transf.rotation)
+ add_rotation = Point(x=x, y=y, z=z)
+
+ # Get section rotation
+ wg_sec_rot = euler2fix(add_rotation)
+
+ return wg_sec_rot, wg_sec_chord, prof_vect_x, prof_vect_y, prof_vect_z, prof_uid
+
+
+def get_leading_edge(
+ i_sec: int,
+ wg_sk_transf: Transformation,
+ wg_sec_rot: Point,
+ wg_sec_chord: float,
+ pos_x_list: List,
+ pos_y_list: List,
+ pos_z_list: List,
+) -> Tuple[float, float, float, float]:
+
+ wg_sec_dihed = math.radians(wg_sec_rot.x)
+ wg_sec_twist = math.radians(wg_sec_rot.y)
+ wg_sec_yaw = math.radians(wg_sec_rot.z)
+
+ # Apply 3d rotation of section rotation
+ x_le_rot, y_le_rot, z_le_rot = rotate_points(
+ pos_x_list[i_sec], pos_y_list[i_sec], pos_z_list[i_sec],
+ wg_sec_dihed, wg_sec_twist, wg_sec_yaw
+ )
+
+ x_le_abs, y_le_abs, z_le_abs = sum_points(
+ wg_sk_transf.translation,
+ Point(x=x_le_rot, y=y_le_rot, z=z_le_rot),
+ )
+
+ return x_le_abs, y_le_abs, z_le_abs, wg_sec_chord
+
+
+def access_leading_edges(
+ tixi: Tixi3,
+ list_cnt: List,
+ wing_sections_xpath: str,
+ wing_transf: Transformation,
+ wg_sk_transf: Transformation,
+ pos_x_list: List,
+ pos_y_list: List,
+ pos_z_list: List,
+) -> List[List]:
+ """
+ Returns list of leading edges and chord length of each wings' sections (wing_sections_xpath).
+
+ Args:
+ tixi (Tixi3): Tixi handle of CPACS file.
+ list_cnt (List): List of section numbers to access leading edge from.
+ wing_sections_xpath (str): xPath to the wing's sections element in the CPACS file.
+ wing_transf (Transformation): Wing's transformation.
+ wg_sk_transf (Transformation): Wing's skeleton transformation.
+ pos_x_list (List): List of x translations for each positioning.
+ pos_y_list (List): List of y translations for each positioning.
+ pos_z_list (List): List of z translations for each positioning.
+
+ Returns:
+ (List[List]):
+ List of list containing leading edges
+ and chord length of each sections of a wing.
+
+ """
+
+ le_list = []
+
+ for i_sec in list_cnt:
+ wg_sec_rot, wg_sec_chord, _, _, _, _ = get_section_rotation(
+ tixi,
+ i_sec,
+ wing_sections_xpath,
+ wing_transf, wg_sk_transf
+ )
+
+ x_le_abs, y_le_abs, z_le_abs, wg_sec_chord = get_leading_edge(
+ i_sec,
+ wg_sk_transf,
+ wg_sec_rot, wg_sec_chord,
+ pos_x_list, pos_y_list, pos_z_list,
+ )
+
+ le_list.append([x_le_abs, y_le_abs, z_le_abs, wg_sec_chord])
+
+ return le_list
+
+
+def wing_sections(
+ tixi: Tixi3,
+ wing_xpath: str,
+ pos_x_list: List,
+ pos_y_list: List,
+ pos_z_list: List,
+ list_type: str,
+) -> List[List]:
+ """
+ Retrieve and process the sections of a wing from the CPACS file.
+
+ Args:
+ tixi (Tixi3): TIXI Handle of the CPACS file.
+ wing_xpath (str): xPath to the wing element in the CPACS file.
+ pos_x_list (List): List of x translations for each positioning.
+ pos_y_list (List): List of y translations for each positioning.
+ pos_z_list (List): List of z translations for each positioning.
+ list_type (str):
+ Either 'first_n_last'
+ or 'first_n_second'
+ or 'secondlast_n_last'
+ or 'first' or 'all'.
+
+ Returns:
+ (List[List]): eading edge coordinates (x_le_abs, y_le_abs, z_le_abs)
+ and chord length of specified sections in list_type.
+
+ """
+
+ # Check if the wing has sections
+ wing_sections_xpath = wing_xpath + "/sections"
+
+ if tixi.checkElement(wing_sections_xpath):
+ sec_cnt = tixi.getNamedChildrenCount(wing_sections_xpath, "section")
+
+ wing_transf = Transformation()
+ wing_transf.get_cpacs_transf(tixi, wing_xpath)
+
+ # Create a class for the transformation of the WingSkeleton
+ wg_sk_transf = Transformation()
+ wg_sk_transf.rotation = euler2fix(wing_transf.rotation)
+ wg_sk_transf.translation = wing_transf.translation
+
+ if list_type == "first_n_last":
+ list_cnt = [0, sec_cnt - 1]
+ elif list_type == "first_n_second":
+ list_cnt = [0, 1]
+ elif list_type == "secondlast_n_last":
+ list_cnt = [sec_cnt - 2, sec_cnt - 1]
+ elif list_type == "first":
+ list_cnt = [0]
+ elif list_type == "all":
+ list_cnt = range(sec_cnt)
+ else:
+ log.warning(f"Value: {list_type} list_type is incorrect in wing_sections.")
+
+ return access_leading_edges(
+ tixi=tixi,
+ list_cnt=list_cnt,
+ wing_sections_xpath=wing_sections_xpath,
+ wing_transf=wing_transf, wg_sk_transf=wg_sk_transf,
+ pos_x_list=pos_x_list, pos_y_list=pos_y_list, pos_z_list=pos_z_list,
+ )
+
+
+def get_main_wing_le(tixi: Tixi3) -> Tuple[float, float, float, float]:
+ """
+ Get main wing's leading edge position of first section from tixi handle,
+ along with chord length.
+
+ Args:
+ tixi (Tixi3): Tixi handle of CPACS file.
+
+ Returns:
+ (Tuple[float, float, float, float]): Leading edge coordinate, chord length.
+
+ """
+ wing_xpath = WINGS_XPATH + "/wing[1]"
+
+ # Wing Positionings
+ _, pos_x_list, pos_y_list, pos_z_list = get_positionings(tixi, wing_xpath, "wing")
+
+ # Wing Sections
+ le_list = wing_sections(
+ tixi,
+ wing_xpath,
+ pos_x_list,
+ pos_y_list,
+ pos_z_list,
+ list_type="first",
+ )
+ first_list = le_list[0]
+
+ return first_list[0], first_list[1], first_list[2], first_list[3]
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute.")
diff --git a/ceasiompy/utils/mathfunctions.py b/ceasiompy/utils/mathfunctions.py
deleted file mode 100644
index 285b407e9..000000000
--- a/ceasiompy/utils/mathfunctions.py
+++ /dev/null
@@ -1,205 +0,0 @@
-"""
-CEASIOMpy: Conceptual Aircraft Design Software
-
-Developed for CFS ENGINEERING, 1015 Lausanne, Switzerland
-
-Math functions which are used in different modules of CEASIOMpy
-
-Python version: >=3.8
-
-| Author : Aidan Jungo
-| Creation: 2018-10-19
-
-TODO:
-
- * Angle naming could be improve to respect coding guidelines
- * Add Source and documentation
-
-"""
-
-# =================================================================================================
-# IMPORTS
-# =================================================================================================
-
-import copy
-import math
-import numpy as np
-
-from ceasiompy.utils.ceasiomlogger import get_logger
-
-log = get_logger()
-
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
-
-
-def get_rotation_matrix(RaX, RaY, RaZ):
-
- # Rotation matrices
- Rx = np.array(
- [
- [1.0, 0.0, 0.0],
- [0.0, math.cos(RaX), -math.sin(RaX)],
- [0.0, math.sin(RaX), math.cos(RaX)],
- ]
- )
- Ry = np.array(
- [
- [math.cos(RaY), 0.0, -math.sin(RaY)],
- [0.0, 1.0, 0.0],
- [math.sin(RaY), 0.0, math.cos(RaY)],
- ]
- )
- Rz = np.array(
- [
- [math.cos(RaZ), -math.sin(RaZ), 0.0],
- [math.sin(RaZ), math.cos(RaZ), 0.0],
- [0.0, 0.0, 1.0],
- ]
- )
-
- return Rx, Ry, Rz
-
-
-def euler2fix(rotation_euler):
- """Function to convert Euler angles into fix angles.
-
- Function to convert an Euler angle rotation into a fix angle rotation
- Euler angle (CPACS): First rotation around Ox, then rotation around
- already rotated Oy and finally rotation around already rotated Oz (x,y',z")
- Fix angle (SUMO): Rotation around the axis are done independently (x,y,x)
-
- Source :
- * TODO: add math documentation
-
- Args:
- rotation_euler (object): Object containing Euler rotation in x,y,z [deg]
-
- Returns:
- rotation_fix (object): Object containing Fixed angle rotation
- in x,y,z [degree]
-
- """
-
- # Angle of rotation (Euler angle)
- RaX = math.radians(rotation_euler.x)
- RaY = math.radians(rotation_euler.y)
- RaZ = math.radians(rotation_euler.z)
-
- # Rotation matrices
- Rx, Ry, Rz = get_rotation_matrix(RaX, RaY, RaZ)
-
- # Identity matrices
- I1 = np.eye(3)
- I2 = Rx @ I1
- I3 = Ry @ I2
-
- # Direction cosine matrix
- DirCos = Rz @ I3
-
- # Angle of rotation (fix frame angle)
- rax = math.atan2(-DirCos[1, 2], DirCos[2, 2])
- ray = math.atan2(DirCos[0, 2], DirCos[2, 2] * math.cos(rax) - DirCos[1, 2] * math.sin(rax))
- raz = math.atan2(
- DirCos[1, 0] * math.cos(rax) + DirCos[2, 0] * math.sin(rax),
- DirCos[1, 1] * math.cos(rax) + DirCos[2, 1] * math.sin(rax),
- )
-
- # Transform back to degree and round angles
- rax = round(math.degrees(rax), 2)
- ray = round(math.degrees(ray), 2)
- raz = round(math.degrees(raz), 2)
-
- # Get the result between -180 and 180 deg
- if rax >= 180:
- rax = rax - 360
- if rax <= -180:
- rax = rax + 360
- if ray >= 180:
- ray = ray - 360
- if ray <= -180:
- ray = ray + 360
- if raz >= 180:
- raz = raz - 360
- if raz <= -180:
- raz = raz + 360
-
- # But for a mysterious reason... (convention?)
- ray = -ray
-
- # Return the rotation as an object
- rotation_fix = copy.deepcopy(rotation_euler)
- rotation_fix.x = rax
- rotation_fix.y = ray
- rotation_fix.z = raz
-
- return rotation_fix
-
-
-def fix2euler(rotation_fix):
- """Function to convert fix angles into Euler angles.
-
- Function to convert a fix angle rotation into an Euler angle rotation
- Fix angle (for SUMO): Rotation around the tree axe are done independently
- (x,y,x)
- Euler angle (CPACS): First rotation around Ox, then rotation around
- already rotated Oy and finally rotation around already rotated Oz
- (x,y',z")
-
- Source :
- * TODO: add math documentation
-
- Args:
- rotation_fix (object): Object containing Fixed angle rotation
- in x,y,z [degree]
-
- Returns:
- rotation_euler (object): Object containing Euler rotation in x,y,z [deg]
-
-
- """
-
- # Angle of rotation (Fix frame angle)
- RaX = math.radians(rotation_fix.x)
- RaY = math.radians(rotation_fix.y)
- RaZ = math.radians(rotation_fix.z)
-
- # Rotation matrices
- Rx, Ry, Rz = get_rotation_matrix(RaX, RaY, RaZ)
-
- # Direction cosine matrix
- DirCos = Rx @ Ry @ Rz
-
- # Angle of rotation (euler angle)
- rax = math.atan2(DirCos[2, 1], DirCos[2, 2])
- ray = math.atan2(DirCos[2, 0], math.sqrt(DirCos[2, 1] ** 2 + DirCos[2, 2] ** 2))
- raz = math.atan2(DirCos[0, 0], DirCos[1, 0])
-
- # Transform back to degree and round angles
- rax = round(math.degrees(rax), 2)
- ray = round(math.degrees(ray), 2)
- raz = round(-math.degrees(raz) + 90.0, 2)
-
- # Return the rotation as an object
- rotation_euler = copy.deepcopy(rotation_fix)
- rotation_euler.x = rax
- rotation_euler.y = ray
- rotation_euler.z = raz
-
- return rotation_euler
-
-
-# =================================================================================================
-# MAIN
-# =================================================================================================
-
-if __name__ == "__main__":
-
- log.info("Nothing to execute!")
diff --git a/ceasiompy/utils/mathsfunctions.py b/ceasiompy/utils/mathsfunctions.py
new file mode 100644
index 000000000..02849c987
--- /dev/null
+++ b/ceasiompy/utils/mathsfunctions.py
@@ -0,0 +1,303 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed for CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Math functions which are used in different modules of CEASIOMpy.
+
+| Author : Leon Deligny
+| Creation: 2025-Mar-03
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
+import math
+
+import numpy as np
+
+from math import (
+ cos,
+ sin,
+)
+
+from typing import Tuple
+from numpy import ndarray
+from ceasiompy.utils.generalclasses import Point
+from scipy.spatial.transform import Rotation as R
+
+from ceasiompy import log
+
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
+
+
+def rot(angle: float) -> ndarray:
+ """
+ 2D Rotation matrix.
+
+ Args:
+ angle (float): Angle of rotation [rad].
+
+ Returns:
+ (ndarray): Rotation matrix of this angle.
+ """
+ c = cos(angle)
+ s = sin(angle)
+
+ return np.array([
+ [c, -s],
+ [s, c],
+ ])
+
+
+def rotate_2d_point(
+ x: Tuple[float, float],
+ center_point: Tuple[float, float],
+ angle: float,
+) -> Tuple[float, float]:
+ """
+ Rotate a point in a 2d space.
+
+ Args:
+ x (Tuple[float, float]): (x, z) point to rotate.
+ center_point (Tuple[float, float]): Center of rotation.
+ angle (float): Angle of ration [deg].
+
+ Returns:
+ (Tuple[float, float]): Rotated point.
+
+ """
+ angle_rad = math.radians(angle)
+ rotation_matrix = rot(angle_rad)
+
+ # Center the point
+ x_centered = np.array([x[0] - center_point[0], x[1] - center_point[1]])
+
+ # Apply the rotation matrix
+ rotated_point = np.dot(rotation_matrix, x_centered)
+
+ # Translate back to the original center
+ return rotated_point[0] + center_point[0], rotated_point[1] + center_point[1]
+
+
+def get_rotation_matrix(RaX: float, RaY: float, RaZ: float) -> Tuple[ndarray, ndarray, ndarray]:
+ """
+ Computes the rotation matrices for rotations around the X, Y, and Z axes.
+
+ Args:
+ RaX (float): Rotation angle around the X-axis [rad].
+ RaY (float): Rotation angle around the Y-axis [rad].
+ RaZ (float): Rotation angle around the Z-axis [rad].
+
+ Returns:
+ (Tuple[ndarray, ndarray, ndarray]): Rotation matrices for the X, Y, and Z axes resp.
+
+ """
+
+ cx = cos(RaX)
+ sx = sin(RaX)
+
+ Rx = np.array([
+ [1.0, 0.0, 0.0],
+ [0.0, cx, -sx],
+ [0.0, sx, cx],
+ ])
+
+ cy = cos(RaY)
+ sy = sin(RaY)
+
+ Ry = np.array([
+ [cy, 0.0, -sy],
+ [0.0, 1.0, 0.0],
+ [sy, 0.0, cy],
+ ])
+
+ cz = cos(RaZ)
+ sz = sin(RaZ)
+
+ Rz = np.array([
+ [cz, -sz, 0.0],
+ [sz, cz, 0.0],
+ [0.0, 0.0, 1.0],
+ ])
+
+ return Rx, Ry, Rz
+
+
+def euler2fix(rotation_euler):
+ """
+ Converts an Euler angle [deg] rotation into a fix angle [deg] rotation.
+
+ Euler angle (CPACS): First rotation around Ox, then rotation around
+ already rotated Oy and finally rotation around already rotated Oz (x,y',z").
+
+ Fix angle (SUMO): Rotation around the axis are done independently (x,y,x).
+
+ We are using XYZ Euler Angles, which are also known as Cardan angles.
+
+ Source :
+ https://ethz.ch/content/dam/ethz/special-interest/mavt/robotics-n-intelligent-systems/rsl-dam/documents/RobotDynamics2016/KinematicsSingleBody.pdf
+
+ """
+ object_ = False
+ if isinstance(rotation_euler, Point):
+ object_ = True
+ rotation_euler = np.array([rotation_euler.x, rotation_euler.y, rotation_euler.z])
+
+ rotation = R.from_euler('zyx', rotation_euler, degrees=True)
+
+ fix_angles = rotation.as_euler('xyz', degrees=True)
+
+ if object_:
+ return Point(
+ x=fix_angles[0],
+ y=fix_angles[1],
+ z=fix_angles[2],
+ )
+ else:
+ return fix_angles
+
+
+def fix2euler(rotation_fix):
+ """
+ Convert a fix angle [deg] rotation into an Euler angle [deg] rotation.
+
+ Fix angle (for SUMO): Rotation around the three axes are done independently.
+
+ Euler angle (CPACS): First rotation around Ox, then rotation around
+ already rotated Oy and finally rotation around already rotated Oz
+ (x,y',z").
+
+ We are using ZYX Euler angles, also known as Tait-Bryan angles.
+
+ Source:
+ https://ethz.ch/content/dam/ethz/special-interest/mavt/robotics-n-intelligent-systems/rsl-dam/documents/RobotDynamics2016/KinematicsSingleBody.pdf
+
+ """
+ object_ = False
+ if isinstance(rotation_fix, Point):
+ object_ = True
+ rotation_fix = np.array([rotation_fix.x, rotation_fix.y, rotation_fix.z])
+
+ rotation = R.from_euler('xyz', rotation_fix, degrees=True)
+
+ euler_angles = rotation.as_euler('zyx', degrees=True)
+
+ if object_:
+ return Point(
+ x=euler_angles[0],
+ y=euler_angles[1],
+ z=euler_angles[2],
+ )
+ else:
+ return euler_angles
+
+
+def rotate_points(
+ x: float,
+ y: float,
+ z: float,
+ RaX: float,
+ RaY: float,
+ RaZ: float
+) -> Tuple[float, float, float]:
+ """
+ Applies a 3D rotation to the coordinates of a point.
+
+ Args:
+ x (float): x coordinate of the initial point.
+ y (float): y coordinate of the initial point.
+ z (float): z coordinate of the initial point.
+ RaX (float): Rotation angle around x-axis [rad].
+ Ray (float): Rotation angle around y-axis [rad].
+ RaZ (float): Rotation angle around z-axis [rad].
+
+ Returns:
+ (Tuple[float, float, float]): x, y, z coordinates of the rotated point.
+
+ """
+ R_x, R_y, R_z = get_rotation_matrix(RaX, RaY, RaZ)
+
+ rotation_matrix = R_z @ R_y @ R_x
+
+ point = np.array([x, y, z])
+ rotated_point = rotation_matrix @ point
+
+ return rotated_point[0], rotated_point[1], rotated_point[2]
+
+
+def non_dimensionalize_rate(
+ p: float,
+ q: float,
+ r: float,
+ v: float,
+ b: float,
+ c: float,
+) -> Tuple[float, float, float]:
+ """
+ Non-dimensionalize pitch, roll and yaw rates.
+
+ Args:
+ p (float): Roll rate in [deg/s].
+ q (float): Pitch rate in [deg/s].
+ r (float): Yaw rate in [deg/s].
+ v (float): Velocity in [m/s].
+ b (float): Span of aircraft [m].
+ c (float): Chord of aircraft [m].
+
+ Returns:
+ pStar (float): Non-dimensionalized roll rate in [deg/s].
+ qStar (float): Non-dimensionalized pitch rate in [deg/s].
+ rStar (float): Non-dimensionalized yaw rate in [deg/s].
+
+ """
+ pStar = p * b / (2 * v)
+ qStar = q * c / (2 * v)
+ rStar = r * b / (2 * v)
+
+ return pStar, qStar, rStar
+
+
+def dimensionalize_rate(
+ pStar: float,
+ qStar: float,
+ rStar: float,
+ v: float,
+ b: float,
+ c: float,
+) -> Tuple[float, float, float]:
+ """
+ Dimensionalize pitch, roll and yaw rates.
+
+ Args:
+ pStar (float): Non-dimensionalized roll rate in [deg/s].
+ qStar (float): Non-dimensionalized pitch rate in [deg/s].
+ rStar (float): Non-dimensionalized yaw rate in [deg/s].
+ v (float): Velocity in [m/s].
+ b (float): Span of aircraft [m].
+ c (float): Chord of aircraft [m].
+
+ Returns:
+ p (float): Roll rate in [deg/s].
+ q (float): Pitch rate in [deg/s].
+ r (float): Yaw rate in [deg/s].
+
+ """
+ p = pStar * (2 * v) / b
+ q = qStar * (2 * v) / c
+ r = rStar * (2 * v) / b
+
+ return p, q, r
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+
+ log.info("Nothing to execute!")
diff --git a/ceasiompy/utils/moduleinterfaces.py b/ceasiompy/utils/moduleinterfaces.py
index 1b6d6d812..fd4fb7f8f 100644
--- a/ceasiompy/utils/moduleinterfaces.py
+++ b/ceasiompy/utils/moduleinterfaces.py
@@ -5,14 +5,10 @@
Module interfaces functions to deal with CPACS input and output
-Python version: >=3.8
| Author : Aaron Dettmann
| Creation: 2019-08-06
-TODO:
-
- *
"""
# =================================================================================================
@@ -25,16 +21,18 @@
import uuid
from pathlib import Path
-from ceasiompy.utils.ceasiomlogger import get_logger
+from typing import Union
+
+from ceasiompy import log
from ceasiompy.utils.commonpaths import MODULES_DIR_PATH
from cpacspy.cpacsfunctions import create_branch, open_tixi
-log = get_logger()
-MODULE_DIR = Path(__file__).parent
+from ceasiompy.utils import MODULE_DIR
MODNAME_TOP = "ceasiompy"
MODNAME_SPECS = "__specs__"
+MODNAME_INIT = "__init__"
# =================================================================================================
# CLASSES
@@ -85,7 +83,7 @@ def __init__(
self.var_name = var_name
self.var_type = var_type
self.default_value = default_value
- self.unit = unit
+ self.unit = self.filter_unit(unit)
self.descr = descr
self.xpath = xpath
@@ -94,6 +92,15 @@ def __init__(
self.gui_name = gui_name
self.gui_group = gui_group
+ def filter_unit(self, unit_entry: Union[None, str]) -> Union[None, str]:
+ if unit_entry is None:
+ return None
+ if not unit_entry.startswith('['):
+ unit_entry = '[' + unit_entry
+ if not unit_entry.endswith(']'):
+ unit_entry = unit_entry + ']'
+ return unit_entry
+
class CPACSInOut:
def __init__(self):
@@ -101,12 +108,14 @@ def __init__(self):
Class summarising the input and output data
Attributes:
- inputs (list): List of CPACS inputs
- outputs (list): List of CPACS output
+ inputs (list): List of CPACS inputs.
+ outputs (list): List of CPACS outputs.
+
"""
self.inputs = []
self.outputs = []
+ self.change_listeners = {}
def add_input(self, **kwargs):
"""Add a new entry to the inputs list"""
@@ -145,6 +154,29 @@ def get_gui_dict(self):
return gui_settings_dict
+ def set_gui_visibility(self, var_name, visible):
+ """Set the visibility of a GUI element"""
+ for entry in self.inputs:
+ if entry.var_name == var_name:
+ entry.gui = visible
+
+ def add_change_listener(self, var_name, listener):
+ """Add a change listener for a specific input"""
+ if var_name in self.change_listeners:
+ self.change_listeners[var_name].append(listener)
+
+ def notify_change_listeners(self, var_name):
+ """Notify all change listeners for a specific input"""
+ if var_name in self.change_listeners:
+ for listener in self.change_listeners[var_name]:
+ listener()
+
+ def get_defaultvalue_xpath(self, var_name):
+ """Get the value of a specific input"""
+ for entry in self.inputs:
+ if entry.var_name == var_name:
+ return (entry.default_value, entry.xpath)
+ raise ValueError(f"Input '{var_name}' not found")
# =================================================================================================
# FUNCTIONS
@@ -161,7 +193,7 @@ def get_module_path(module_name: str) -> Path:
def check_cpacs_input_requirements(
- cpacs_file, *, module_name=None, submodule_level=1, cpacs_inout=None
+ tixi, *, module_name=None, submodule_level=1, cpacs_inout=None
):
"""Check if the input CPACS file contains the required nodes
@@ -178,50 +210,50 @@ def check_cpacs_input_requirements(
cpacs_inout (obj): CPACSInOut() instance
Raises:
- CPACSRequirementError: If one or more paths are required by calling
- module but not available in CPACS file
+ CPACSRequirementError: If >=1 paths are required but not available in CPACS file.
"""
- if not isinstance(submodule_level, int) and submodule_level < 1:
- ValueError("'submodule_level' must be a positive integer")
+ if module_name != "utils":
+
+ if not isinstance(submodule_level, int) and submodule_level < 1:
+ ValueError("'submodule_level' must be a positive integer")
- # If 'cpacs_inout' not provided by caller, we try to determine it
- if cpacs_inout is None:
- if module_name is None:
- # Get the path of the caller submodule
- frm = inspect.stack()[1]
- mod = inspect.getmodule(frm[0])
- caller_module_path = Path(mod.__file__).parent
+ # If 'cpacs_inout' not provided by caller, we try to determine it
+ if cpacs_inout is None:
+ if module_name is None:
+ # Get the path of the caller submodule
+ frm = inspect.stack()[1]
+ mod = inspect.getmodule(frm[0])
+ caller_module_path = Path(mod.__file__).parent
- # Get the CEASIOM_XPATH submodule name
- module_name = caller_module_path.name
- for _ in range(1, submodule_level):
+ # Get the CEASIOM_XPATH submodule name
module_name = caller_module_path.name
+ for _ in range(1, submodule_level):
+ module_name = caller_module_path.name
- # Load the submodule specifications
- specs_module = get_specs_for_module(module_name, raise_error=True)
- cpacs_inout = specs_module.cpacs_inout
+ # Load the submodule specifications
+ specs_module = get_specs_for_module(module_name, raise_error=True)
+ cpacs_inout = specs_module.cpacs_inout
- tixi = open_tixi(cpacs_file)
- missing_nodes = []
- for entry in cpacs_inout.inputs:
+ missing_nodes = []
+ for entry in cpacs_inout.inputs:
- if entry.default_value is not None:
- continue
- if tixi.checkElement(entry.xpath) is False:
- missing_nodes.append(entry.xpath)
+ if entry.default_value is not None:
+ continue
+ if tixi.checkElement(entry.xpath) is False:
+ missing_nodes.append(entry.xpath)
- if missing_nodes:
- for missing in missing_nodes:
- log.error("The following xpath cannot be found: " + missing)
+ if missing_nodes:
+ for missing in missing_nodes:
+ log.error("The following xpath cannot be found: " + missing)
- raise CPACSRequirementError("CPACS xpath(s) required but does not exist!")
+ raise CPACSRequirementError("CPACS xpath(s) required but does not exist!")
def get_module_list(only_active=True):
"""Return a list of CEASIOMpy modules
- ['SkinFriction', 'PyTornado', ...]
+ ['SkinFriction', 'PyAVL', ...]
Returns:
A list of module names (as strings)
@@ -235,14 +267,14 @@ def get_module_list(only_active=True):
if module_name.startswith("__") or module_name.startswith("."):
continue
- specs = get_specs_for_module(module_name, raise_error=False)
+ init = get_init_for_module(module_name, raise_error=False)
try:
- module_status = specs.module_status
+ module_status = init.module_status
except AttributeError:
module_status = False
if module_name != "utils":
log.warning(
- f"module status of {module_name} is not define in its __specs__.py file"
+ f"Module status of {module_name} is not define in its __init__.py file."
)
if only_active:
@@ -282,7 +314,7 @@ def get_tooloutput_file_path(module_name):
return Path(MODULES_DIR_PATH, module_name, "ToolOutput", "ToolOutput.xml")
-def get_specs_for_module(module_name, raise_error=False):
+def get_specs_for_module(module_name: str, raise_error=False):
"""Return the __specs__ module for a CEASIOMpy module
Args:
@@ -297,12 +329,34 @@ def get_specs_for_module(module_name, raise_error=False):
try:
specs = importlib.import_module(".".join((module_name, MODNAME_SPECS)))
return specs
- except ImportError:
+ except ImportError as e:
+ log.error(f"Error loading __specs__ for module {module_name}: {e}")
if raise_error:
raise ImportError(f"{MODNAME_SPECS} module not found for {module_name}")
return None
+def get_init_for_module(module_name, raise_error=False):
+ """Return the __init__ module for a CEASIOMpy module
+
+ Args:
+ module_name (str): name of the module as a string
+ raise_error (bool): 'True' if error should be raised
+ if __specs__ does not exist
+ """
+
+ if not module_name.startswith("ceasiompy."):
+ module_name = ".".join((MODNAME_TOP, module_name))
+
+ try:
+ init = importlib.import_module(".".join((module_name, MODNAME_INIT)))
+ return init
+ except ImportError:
+ if raise_error:
+ raise ImportError(f"{MODNAME_INIT} module not found for {module_name}")
+ return None
+
+
def get_all_module_specs():
"""Return a dictionary with module names (keys) and specs files (values)
@@ -313,8 +367,8 @@ def get_all_module_specs():
The dictionary has the form:
{
- 'SkinFriction': pytornado_specs_module,
- 'PyTornado': pytornado_specs_module,
+ 'SkinFriction': skinfriction_specs_module,
+ 'PyAVL': pyavl_specs_module,
'SomeModuleWithoutSpecsFile': None,
...
}
diff --git a/ceasiompy/utils/tests/tests_ceasiompyutils/test_ceasiompyutils.py b/ceasiompy/utils/tests/tests_ceasiompyutils/test_ceasiompyutils.py
index 73b91fe34..74ab50af3 100644
--- a/ceasiompy/utils/tests/tests_ceasiompyutils/test_ceasiompyutils.py
+++ b/ceasiompy/utils/tests/tests_ceasiompyutils/test_ceasiompyutils.py
@@ -5,7 +5,6 @@
Test functions for 'utils/ceasiompyutils.py'
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2022-02-10
@@ -21,7 +20,6 @@
from pathlib import Path
import pytest
from ceasiompy.utils.ceasiompyutils import (
- SoftwareNotInstalled,
aircraft_name,
change_working_dir,
get_aeromap_list_from_xpath,
@@ -86,10 +84,10 @@ def test_get_results_directory():
with change_working_dir(TMP_DIR):
results_dir = get_results_directory("ExportCSV")
- assert results_dir == Path(Path.cwd(), "Results", "AeroCoefficients")
+ assert results_dir == Path(Path.cwd(), "Results", "ExportCSV")
results_dir = get_results_directory("CPACS2SUMO")
- assert results_dir == Path(Path.cwd(), "Results", "SUMO")
+ assert results_dir == Path(Path.cwd(), "Results", "CPACS2SUMO")
if results_dir.parent.exists():
shutil.rmtree(results_dir.parent)
@@ -112,8 +110,8 @@ def test_get_install_path():
assert get_install_path("NotExistingSoftware") is None
- with pytest.raises(SoftwareNotInstalled):
- get_install_path("NotExistingSoftware", raise_error=True)
+ # with pytest.raises(SoftwareNotInstalled):
+ # get_install_path("NotExistingSoftware", raise_error=True)
def test_run_software():
diff --git a/ceasiompy/utils/tests/tests_configfiles/test_configfiles.py b/ceasiompy/utils/tests/tests_configfiles/test_configfiles.py
index 9e5ae4cfc..3cec86440 100644
--- a/ceasiompy/utils/tests/tests_configfiles/test_configfiles.py
+++ b/ceasiompy/utils/tests/tests_configfiles/test_configfiles.py
@@ -5,7 +5,6 @@
Test functions for 'lib/SkinFriction/skinfriction.py'
-Python version: >=3.8
| Author : Aidan Jungo
diff --git a/ceasiompy/utils/tests/tests_gereralclasses/test_generalclasses.py b/ceasiompy/utils/tests/tests_gereralclasses/test_generalclasses.py
index f095d177f..4346f9918 100644
--- a/ceasiompy/utils/tests/tests_gereralclasses/test_generalclasses.py
+++ b/ceasiompy/utils/tests/tests_gereralclasses/test_generalclasses.py
@@ -5,7 +5,6 @@
Test functions for 'ceasiompy/utils/generalclasses.py'
-Python version: >=3.8
| Author : Aidan Jungo
diff --git a/ceasiompy/utils/tests/tests_logger/test_ceasiomlogger.py b/ceasiompy/utils/tests/tests_logger/test_ceasiomlogger.py
index 3bc66dd38..7e5c0cecf 100644
--- a/ceasiompy/utils/tests/tests_logger/test_ceasiomlogger.py
+++ b/ceasiompy/utils/tests/tests_logger/test_ceasiomlogger.py
@@ -5,7 +5,6 @@
Test for 'ceasiomlogger.py' function
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2018-09-27
@@ -16,15 +15,9 @@
# IMPORTS
# =================================================================================================
-from ceasiompy.utils.ceasiomlogger import get_logger
+from ceasiompy import log
from ceasiompy.utils.commonpaths import LOGFILE
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
-
-
# =================================================================================================
# FUNCTIONS
# =================================================================================================
@@ -36,7 +29,6 @@ def test_logger():
"""
# Set logger name and create a logger with this name
- log = get_logger()
# Use the 5 log level
log.debug("Test debug")
@@ -70,7 +62,6 @@ def test_logger():
# =================================================================================================
if __name__ == "__main__":
-
print("Running Test CEASIOMLogger")
print("To run test use the following command:")
print(">> pytest -v")
diff --git a/ceasiompy/utils/tests/tests_mathfunctions/test_mathfunctions.py b/ceasiompy/utils/tests/tests_mathfunctions/test_mathfunctions.py
index a055d8d58..e1dfd9e69 100644
--- a/ceasiompy/utils/tests/tests_mathfunctions/test_mathfunctions.py
+++ b/ceasiompy/utils/tests/tests_mathfunctions/test_mathfunctions.py
@@ -5,7 +5,6 @@
Test functions from 'lib/utils/mathfunctions.py'
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2018-10-19
@@ -16,17 +15,15 @@
# IMPORTS
# =================================================================================================
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.generalclasses import SimpleNamespace
-from ceasiompy.utils.mathfunctions import euler2fix, fix2euler
from pytest import approx
+from ceasiompy.utils.mathsfunctions import (
+ euler2fix,
+ fix2euler,
+)
-log = get_logger()
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
+from ceasiompy.utils.generalclasses import Point
+from ceasiompy import log
# =================================================================================================
# FUNCTIONS
@@ -36,61 +33,38 @@
def test_euler2fix():
"""Test convertion from Euler angles to fix angles"""
- euler_angle = SimpleNamespace()
+ euler_angle = Point()
euler_angle.x = 0
euler_angle.y = 0
euler_angle.z = 0
fix_angle = euler2fix(euler_angle)
- assert fix_angle.x == 0.0
- assert fix_angle.y == 0.0
- assert fix_angle.z == 0.0
- euler_angle.x = 50
- euler_angle.y = 32
- euler_angle.z = 65
- fix_angle = euler2fix(euler_angle)
- assert fix_angle.x == approx(49.24)
- assert fix_angle.y == approx(-33.39)
- assert fix_angle.z == approx(64.58)
+ assert fix_angle.x == approx(0.0)
+ assert fix_angle.y == approx(0.0)
+ assert fix_angle.z == approx(0.0)
- euler_angle.x = -12.5
- euler_angle.y = 27
- euler_angle.z = 93
+ euler_angle.x = 135
+ euler_angle.y = 99
+ euler_angle.z = -30
fix_angle = euler2fix(euler_angle)
- assert fix_angle.x == approx(27.56)
- assert fix_angle.y == approx(11.12)
- assert fix_angle.z == approx(92.72)
+ assert fix_angle.x == approx(98.045944)
+ assert fix_angle.y == approx(-14.5532525)
+ assert fix_angle.z == approx(83.4377462)
- euler_angle.x = 90
- euler_angle.y = 90
- euler_angle.z = 90
+ euler_angle.x = 50
+ euler_angle.y = 32
+ euler_angle.z = 65
fix_angle = euler2fix(euler_angle)
- assert fix_angle.x == 90.0
- assert fix_angle.y == -90.0
- assert fix_angle.z == 90.0
+ assert fix_angle.x == approx(64.580333)
+ assert fix_angle.y == approx(-33.388795)
+ assert fix_angle.z == approx(49.2418955)
def test_fix2euler():
"""Test convertion from fix angles to Euler angles"""
- fix_angle = SimpleNamespace()
-
- fix_angle.x = 0
- fix_angle.y = 0
- fix_angle.z = 0
- euler_angle = euler2fix(fix_angle)
- assert euler_angle.x == 0.0
- assert euler_angle.y == 0.0
- assert euler_angle.z == 0.0
-
- fix_angle.x = 90
- fix_angle.y = 90
- fix_angle.z = 90
- euler_angle = euler2fix(fix_angle)
- assert euler_angle.x == 90.0
- assert euler_angle.y == -90.0
- assert euler_angle.z == 90.0
+ fix_angle = Point()
# Test by doing both transformation
fix_angle.x = 30.23
@@ -98,7 +72,10 @@ def test_fix2euler():
fix_angle.z = -10.98
euler_angle = fix2euler(fix_angle)
fix_angle2 = euler2fix(euler_angle)
- assert fix_angle == fix_angle2
+
+ assert fix_angle.x == approx(fix_angle2.x)
+ assert fix_angle.y == approx(fix_angle2.y)
+ assert fix_angle.z == approx(fix_angle2.z)
# =================================================================================================
@@ -106,7 +83,6 @@ def test_fix2euler():
# =================================================================================================
if __name__ == "__main__":
-
log.info("Running Test Math Functions")
log.info("To run test use the following command:")
log.info(">> pytest -v")
diff --git a/ceasiompy/utils/tests/tests_moduleinterfaces/ToolInput/cpacs_test_file.xml b/ceasiompy/utils/tests/tests_moduleinterfaces/ToolInput/cpacs_test_file.xml
index 2cca037b1..6669a3c0a 100644
--- a/ceasiompy/utils/tests/tests_moduleinterfaces/ToolInput/cpacs_test_file.xml
+++ b/ceasiompy/utils/tests/tests_moduleinterfaces/ToolInput/cpacs_test_file.xml
@@ -4240,33 +4240,5 @@
-
- aeroMap_pyTornado
- 5
- 2
-
-
- False
- False
-
-
- False
- False
-
-
- False
- False
-
-
- False
- False
-
-
-
- False
- False
- False
-
-
diff --git a/ceasiompy/utils/tests/tests_moduleinterfaces/test_moduleinterfaces.py b/ceasiompy/utils/tests/tests_moduleinterfaces/test_moduleinterfaces.py
index 2f14ac612..8dfad9711 100644
--- a/ceasiompy/utils/tests/tests_moduleinterfaces/test_moduleinterfaces.py
+++ b/ceasiompy/utils/tests/tests_moduleinterfaces/test_moduleinterfaces.py
@@ -5,7 +5,6 @@
Test functions for 'lib/moduleinterfaces.py'
-Python version: >=3.8
| Author : Aaron Dettmann
| Creation: 2019-09-09
@@ -16,8 +15,9 @@
# IMPORTS
# =================================================================================================
-
+import streamlit as st
from pathlib import Path
+from cpacspy.cpacspy import CPACS
import pytest
from ceasiompy.utils.moduleinterfaces import (
@@ -31,8 +31,8 @@
get_toolinput_file_path,
get_tooloutput_file_path,
)
-from ceasiompy.utils.commonpaths import MODULES_DIR_PATH
-from ceasiompy.utils.commonxpath import RANGE_XPATH
+from ceasiompy.utils.commonpaths import MODULES_DIR_PATH, CPACS_FILES_PATH
+from ceasiompy.utils.commonxpath import RANGE_CRUISE_ALT_XPATH
MODULE_DIR = Path(__file__).parent
CPACS_TEST_FILE = Path(MODULE_DIR, "ToolInput", "cpacs_test_file.xml")
@@ -107,17 +107,17 @@ def test_check_cpacs_input_requirements():
"""
cpacs_inout = CPACSInOut()
- cpacs_file = Path(MODULE_DIR, "ToolInput", "D150_AGILE_Hangar_v3.xml")
+ cpacs_file = CPACS(Path(MODULE_DIR, "ToolInput", "D150_AGILE_Hangar_v3.xml"))
cpacs_inout.add_input(
var_name="cruise_alt",
default_value=12000,
unit="m",
descr="Aircraft cruise altitude",
- xpath=RANGE_XPATH + "/cruiseAltitude",
+ xpath=RANGE_CRUISE_ALT_XPATH,
)
- assert check_cpacs_input_requirements(cpacs_file, cpacs_inout=cpacs_inout) is None
+ assert check_cpacs_input_requirements(cpacs_file.tixi, cpacs_inout=cpacs_inout) is None
cpacs_inout.add_input(
var_name="something",
@@ -128,7 +128,7 @@ def test_check_cpacs_input_requirements():
)
with pytest.raises(CPACSRequirementError):
- check_cpacs_input_requirements(cpacs_file, cpacs_inout=cpacs_inout)
+ check_cpacs_input_requirements(cpacs_file.tixi, cpacs_inout=cpacs_inout)
def test_get_module_list():
@@ -198,7 +198,7 @@ def test_get_all_module_specs():
"""
Test that 'get_all_module_specs()' runs
"""
-
+ st.session_state.cpacs = CPACS(Path(CPACS_FILES_PATH, "D150_simple.xml"))
all_specs = get_all_module_specs()
assert isinstance(all_specs, dict)
@@ -217,7 +217,6 @@ def test_create_default_toolspecific():
# =================================================================================================
if __name__ == "__main__":
-
print("Test moduleinterfaces.py")
print("To run test use the following command:")
print(">> pytest -v")
diff --git a/ceasiompy/utils/tests/tests_workflowclasses/ceasiompy.cfg b/ceasiompy/utils/tests/tests_workflowclasses/ceasiompy.cfg
index 94600a43f..0a1b80c0a 100644
--- a/ceasiompy/utils/tests/tests_workflowclasses/ceasiompy.cfg
+++ b/ceasiompy/utils/tests/tests_workflowclasses/ceasiompy.cfg
@@ -1,5 +1,5 @@
% File written 2022-01-17 14:46:53.344314
-CPACS_TOOLINPUT = ../D150_simple.xml
-MODULE_TO_RUN = ( CPACS2SUMO, CLCalculator, PyTornado, SaveAeroCoefficients)
-OPTIM_METHOD = OPTIM
+CPACS_TOOLINPUT = ./D150_simple.xml
+MODULE_TO_RUN = ( CLCalculator, PyAVL, SaveAeroCoefficients)
+OPTIM_METHOD = Optimisation
MODULE_OPTIM = ( YES, YES, NO, NO)
diff --git a/ceasiompy/utils/tests/tests_workflowclasses/test_workflowclasses.py b/ceasiompy/utils/tests/tests_workflowclasses/test_workflowclasses.py
index 36bcb7f87..b1844be3f 100644
--- a/ceasiompy/utils/tests/tests_workflowclasses/test_workflowclasses.py
+++ b/ceasiompy/utils/tests/tests_workflowclasses/test_workflowclasses.py
@@ -5,7 +5,6 @@
Test functions for 'utils/workflowclasses.py'
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2022-01-21
@@ -23,7 +22,8 @@
from ceasiompy.utils.ceasiompyutils import run_module
MODULE_DIR = Path(__file__).parent
-CPACS_PATH = Path(MODULE_DIR.parents[3], "test_files", "CPACSfiles", "D150_simple.xml")
+CPACS_PATH = Path(
+ MODULE_DIR.parents[3], "test_files", "CPACSfiles", "D150_simple.xml")
CPACS_PATH_OUT = Path(MODULE_DIR, "D150_simple_out.xml")
# =================================================================================================
@@ -92,7 +92,8 @@ def test_run(self):
if CPACS_PATH_OUT.exists():
CPACS_PATH_OUT.unlink()
- module = ModuleToRun("ModuleTemplate", self.wkflow_test, CPACS_PATH, CPACS_PATH_OUT)
+ module = ModuleToRun(
+ "ModuleTemplate", self.wkflow_test, CPACS_PATH, CPACS_PATH_OUT)
# TODO: how to separate test from workflowclasses.py and ceasiompyutils.py
run_module(module)
@@ -112,9 +113,8 @@ class TestWorkflow:
workflow = Workflow()
MODULE_TO_RUN = [
- "CPACS2SUMO",
"CLCalculator",
- "PyTornado",
+ "PyAVL",
"SaveAeroCoefficients",
]
@@ -124,14 +124,16 @@ def test_from_config_file(self):
# Copy config file to WKFLOW_test dir
shutil.copy(
- Path(MODULE_DIR, "ceasiompy.cfg"), Path(MODULE_DIR, "WKFLOW_test", "ceasiompy.cfg")
+ Path(MODULE_DIR, "ceasiompy.cfg"), Path(
+ MODULE_DIR, "WKFLOW_test", "ceasiompy.cfg")
)
- self.workflow.from_config_file(Path(MODULE_DIR, "WKFLOW_test", "ceasiompy.cfg"))
+ self.workflow.from_config_file(
+ Path(MODULE_DIR, "WKFLOW_test", "ceasiompy.cfg"))
assert self.workflow.modules_list == self.MODULE_TO_RUN
assert self.workflow.module_optim == self.MODULE_OPTIM
- assert self.workflow.optim_method == "OPTIM"
+ assert self.workflow.optim_method == "Optimisation"
def test_write_config_file(self):
pass
@@ -147,46 +149,40 @@ def test_set_workflow(self):
with pytest.raises(ValueError):
self.workflow.set_workflow()
- self.workflow.optim_method = "NONE"
- self.workflow.module_optim = self.MODULE_OPTIM
- with pytest.raises(ValueError):
- self.workflow.set_workflow()
-
self.workflow.module_optim = self.MODULE_OPTIM
self.workflow.working_dir = ""
with pytest.raises(ValueError):
self.workflow.set_workflow()
- self.workflow.from_config_file(Path(MODULE_DIR, "WKFLOW_test", "ceasiompy.cfg"))
+ self.workflow.from_config_file(Path(MODULE_DIR, "ceasiompy.cfg"))
self.workflow.cpacs_in = Path(MODULE_DIR, "NotExistingCPACS.xml")
with pytest.raises(FileNotFoundError):
self.workflow.set_workflow()
# Test normal behavior
self.workflow = Workflow()
- self.workflow.from_config_file(Path(MODULE_DIR, "WKFLOW_test", "ceasiompy.cfg"))
- self.workflow.optim_method = "OPTIM"
+ self.workflow.from_config_file(Path(MODULE_DIR, "ceasiompy.cfg"))
+ self.workflow.optim_method = "Optimisation"
self.workflow.set_workflow()
assert self.workflow.current_wkflow_dir.exists()
assert self.workflow.cpacs_in.exists()
- assert len(list(self.workflow.current_wkflow_dir.iterdir())) == 5
+ assert len(list(self.workflow.current_wkflow_dir.iterdir())) == 4
- assert self.workflow.modules[0].name == "OPTIM"
- assert self.workflow.modules[1].name == "PyTornado"
- assert self.workflow.modules[2].name == "SaveAeroCoefficients"
+ assert self.workflow.modules[0].name == "Optimisation"
+ assert self.workflow.modules[1].name == "SaveAeroCoefficients"
assert self.workflow.modules[0].is_optim_module
- assert self.workflow.modules[0].optim_method == "OPTIM"
+ assert self.workflow.modules[0].optim_method == "Optimisation"
assert self.workflow.modules[0].module_wkflow_path == Path(
- self.workflow.current_wkflow_dir, "01_OPTIM"
+ self.workflow.current_wkflow_dir, "01_Optimisation"
)
assert not self.workflow.modules[1].is_optim_module
assert self.workflow.modules[1].optim_method is None
assert self.workflow.modules[1].module_wkflow_path == Path(
- self.workflow.current_wkflow_dir, "02_PyTornado"
+ self.workflow.current_wkflow_dir, "02_SaveAeroCoefficients"
)
@@ -195,7 +191,6 @@ def test_set_workflow(self):
# =================================================================================================
if __name__ == "__main__":
-
print("Test configfile.py")
print("To run test use the following command:")
print(">> pytest -v")
diff --git a/ceasiompy/utils/workflowclasses.py b/ceasiompy/utils/workflowclasses.py
index eb2bc2252..7c3e8be0a 100644
--- a/ceasiompy/utils/workflowclasses.py
+++ b/ceasiompy/utils/workflowclasses.py
@@ -5,7 +5,6 @@
Classes to run ceasiompy workflows
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2019-10-04
@@ -22,17 +21,21 @@
import shutil
from datetime import datetime
from pathlib import Path
+import importlib
from ceasiompy.Optimisation.optimisation import routine_launcher
-from ceasiompy.utils.ceasiomlogger import add_to_runworkflow_history, get_logger
-from ceasiompy.utils.ceasiompyutils import change_working_dir, run_module
+from ceasiompy.utils.ceasiompylogger import add_to_runworkflow_history
+from ceasiompy.utils.ceasiompyutils import change_working_dir, run_module, get_results_directory
from ceasiompy.utils.configfiles import ConfigFile
from ceasiompy.utils.moduleinterfaces import get_module_list
-from ceasiompy.utils.commonpaths import CPACS_FILES_PATH, LOGFILE, MODULES_DIR_PATH
-# log = get_logger()
+from ceasiompy import log
+from ceasiompy.utils.moduleinterfaces import MODNAME_INIT
+
+from ceasiompy.utils.commonpaths import CPACS_FILES_PATH, LOGFILE, MODULES_DIR_PATH
-OPTIM_METHOD = ["OPTIM", "DOE"]
+#
+OPTIM_METHOD = ["Optimisation", "DOE"]
# =================================================================================================
# CLASSES
@@ -41,7 +44,11 @@
class ModuleToRun:
def __init__(
- self, name: str, wkflow_dir: Path, cpacs_in: Path = None, cpacs_out: Path = None
+ self,
+ name: str,
+ wkflow_dir: Path,
+ cpacs_in: Path = None,
+ cpacs_out: Path = None,
) -> None:
# Check module name validity
@@ -57,6 +64,14 @@ def __init__(
self.name = name
self.wkflow_dir = wkflow_dir
self.cpacs_in = cpacs_in
+ init = importlib.import_module(f"ceasiompy.{name}.{MODNAME_INIT}")
+ add_res_dir: bool = init.RES_DIR
+
+ if add_res_dir:
+ self.results_dir = get_results_directory(
+ name, create=True, wkflow_dir=wkflow_dir)
+ else:
+ self.results_dir = None
self.cpacs_out = cpacs_out
# Set module path
@@ -71,11 +86,13 @@ def __init__(
def create_module_wkflow_dir(self, cnt: int) -> None:
if self.is_optim_module and self.optim_method:
- module_wkflow_name = str(cnt).rjust(2, "0") + "_" + self.optim_method
+ module_wkflow_name = str(cnt).rjust(
+ 2, "0") + "_" + self.optim_method
else:
module_wkflow_name = str(cnt).rjust(2, "0") + "_" + self.name
- self.module_wkflow_path = Path.joinpath(self.wkflow_dir, module_wkflow_name)
+ self.module_wkflow_path = Path.joinpath(
+ self.wkflow_dir, module_wkflow_name)
self.module_wkflow_path.mkdir()
@@ -95,14 +112,15 @@ def __init__(
if "Optimisation" not in self.modules_list:
self.modules_list.insert(0, "Optimisation")
- self.modules = [ModuleToRun(module, subworkflow_dir) for module in modules_list]
+ self.modules = [ModuleToRun(module, subworkflow_dir)
+ for module in modules_list]
self.iteration = 0
def set_subworkflow(self) -> None:
"""Set input and output for subworkflow."""
- for m, module in enumerate(self.modules):
+ for m, _ in enumerate(self.modules):
# Create the module directory in the subworkflow directory
with change_working_dir(self.subworkflow_dir):
@@ -133,7 +151,8 @@ def run_subworkflow(self) -> None:
run_module(module, self.subworkflow_dir)
# TODO: copy last tool output when optim done (last iteration)
- shutil.copy(self.modules[-1].cpacs_out, Path(self.subworkflow_dir, "ToolOutput.xml"))
+ shutil.copy(self.modules[-1].cpacs_out,
+ Path(self.subworkflow_dir, "ToolOutput.xml"))
# TODO: Probably not here
self.iteration += 1
@@ -141,7 +160,8 @@ def run_subworkflow(self) -> None:
# Other iterations
module_optim = [module for module in self.modules]
- routine_launcher(self.optim_method, module_optim, self.subworkflow_dir.parent)
+ routine_launcher(self.optim_method, module_optim,
+ self.subworkflow_dir.parent)
class Workflow:
@@ -209,8 +229,9 @@ def set_workflow(self) -> None:
"""Create the directory structure and set input/output of each modules"""
# Check optim method validity
- if str(self.optim_method) not in OPTIM_METHOD + ["None", "NONE"]:
- raise ValueError(f"Optimisation method {self.optim_method} not supported")
+ if str(self.optim_method) not in (OPTIM_METHOD + ["None", "NONE"]):
+ raise ValueError(
+ f"Optimisation method {self.optim_method} not supported")
# Check coherence of the optimisation modules from config file
if "YES" in self.module_optim:
@@ -220,10 +241,12 @@ def set_workflow(self) -> None:
"no optimisation method is defined"
)
- optim_idx = [i for i, val in enumerate(self.module_optim) if val == "YES"]
+ optim_idx = [i for i, val in enumerate(
+ self.module_optim) if val == "YES"]
for i, val in enumerate(optim_idx[:-1]):
if val + 1 != optim_idx[i + 1]:
- raise ValueError("All optimisation module must be contiguous!")
+ raise ValueError(
+ "All optimisation module must be contiguous!")
# Check working directory
if not self.working_dir:
@@ -233,20 +256,23 @@ def set_workflow(self) -> None:
os.chdir(wkdir)
# Check index of the last workflow directory to set the next one
- wkflow_list = [int(dir.stem.split("_")[-1]) for dir in wkdir.glob("Workflow_*")]
+ wkflow_list = [int(dir.stem.split("_")[-1])
+ for dir in wkdir.glob("Workflow_*")]
if wkflow_list:
wkflow_idx = str(max(wkflow_list) + 1).rjust(3, "0")
else:
wkflow_idx = "001"
- self.current_wkflow_dir = Path.joinpath(wkdir, "Workflow_" + wkflow_idx)
+ self.current_wkflow_dir = Path.joinpath(
+ wkdir, "Workflow_" + wkflow_idx)
self.current_wkflow_dir.mkdir()
# Copy CPACS to the workflow dir
if not self.cpacs_in.exists():
raise FileNotFoundError(f"{self.cpacs_in} has not been found!")
- wkflow_cpacs_in = Path.joinpath(self.current_wkflow_dir, "00_ToolInput.xml").absolute()
+ wkflow_cpacs_in = Path.joinpath(
+ self.current_wkflow_dir, "00_ToolInput.xml").absolute()
shutil.copy(self.cpacs_in, wkflow_cpacs_in)
@@ -264,26 +290,30 @@ def set_workflow(self) -> None:
# Create an object for the module if not and opitm module
if self.module_optim[m] == "NO":
- module = ModuleToRun(module_name, self.current_wkflow_dir, cpacs_in)
+ module = ModuleToRun(
+ module_name, self.current_wkflow_dir, cpacs_in)
skip_create_module = False
# Check if should be included in Optim/DoE
if self.optim_method and self.module_optim[m] == "YES":
if module_optim_idx is None:
- module = ModuleToRun(self.optim_method, self.current_wkflow_dir, cpacs_in)
+ module = ModuleToRun(
+ self.optim_method, self.current_wkflow_dir, cpacs_in)
module.optim_method = self.optim_method
module.is_optim_module = True
module.optim_related_modules.append(module_name)
module_optim_idx = m
else:
- self.modules[module_optim_idx].optim_related_modules.append(module_name)
+ self.modules[module_optim_idx].optim_related_modules.append(
+ module_name)
skip_create_module = True
if not skip_create_module:
module.create_module_wkflow_dir(cnt)
self.modules.append(module)
- module.cpacs_out = Path.joinpath(module.module_wkflow_path, "ToolOutput.xml")
+ module.cpacs_out = Path.joinpath(
+ module.module_wkflow_path, "ToolOutput.xml")
cnt += 1
# Create Optim/DoE subworkflow directory for the optim module if exists
@@ -297,50 +327,32 @@ def set_workflow(self) -> None:
)
self.subworkflow.set_subworkflow()
- # Create Results directory
- new_res_dir = Path.joinpath(self.current_wkflow_dir, "Results")
- new_res_dir.mkdir()
-
- def run_workflow(self) -> None:
+ def run_workflow(self, test=False) -> None:
"""Run the complete Worflow"""
add_to_runworkflow_history(self.current_wkflow_dir)
- # log.info("#" * 99)
- # log.info("### Starting the workflow")
- # log.info("#" * 99)
- # log.info(f"The workflow will be run in {self.current_wkflow_dir}")
- # log.info(f"Input CPACS file: {self.cpacs_in}")
- # log.info("The following modules with be run:")
-
- # for module in self.modules:
- # log.info(f" -> {module.name}")
-
for module in self.modules:
if module.is_optim_module:
self.subworkflow.run_subworkflow()
else:
- run_module(module, self.current_wkflow_dir)
-
- shutil.copy(module.cpacs_out, Path(self.current_wkflow_dir, "ToolOutput.xml"))
+ run_module(
+ module,
+ self.current_wkflow_dir,
+ self.modules_list.index(
+ module.name
+ ), test)
- # log.info("#" * 99)
- # log.info("### End of the workflow")
- # log.info("#" * 99)
+ shutil.copy(module.cpacs_out, Path(
+ self.current_wkflow_dir, "ToolOutput.xml"))
# Copy logfile in the Workflow directory
shutil.copy(LOGFILE, self.current_wkflow_dir)
-# =================================================================================================
-# FUNCTIONS
-# =================================================================================================
-
-
# =================================================================================================
# MAIN
# =================================================================================================
if __name__ == "__main__":
-
- print("Nothing to execute!")
+ log.info("Nothing to execute!")
diff --git a/environment.yml b/environment.yml
index b96227f15..30d91ba66 100644
--- a/environment.yml
+++ b/environment.yml
@@ -15,40 +15,44 @@ channels:
- defaults
dependencies:
- - python=3.8
- - cpacscreator=0.1
- - clang=13.0.0
+ - python=3.11.11
+ - cpacscreator=0.2
+ - clang=20.1.2
- codecov=2.1.11
- - coverage=6.2
- - flake8=4.0.1
- - ipython=8.4.0
- - ipywidgets=7.7.1
- - matplotlib=3.5.2
- - numpy=1.23.3
- - openmdao=3.18.0
- - pandas=1.4.2
- - plotly=5.10.0
- - pip=21.3.1
- - pytest-cov=3.0.0
+ - coverage=7.6.9
+ - flake8=7.1.1
+ - ipython=8.30.0
+ - ipywidgets=8.1.5
+ - numpy=2.2.4
+ - openmdao=3.38.0
+ - pandas=2.2.3
+ - plotly=5.24.1
+ - pip=25.0.1
+ - pytest-cov=6.0.0
- python-kaleido=0.2.1
- - pythreejs=2.3.0
- - pyvista=0.36.1
- - xmltodict=0.12.0
- - vtk=9.0.1
+ - pythreejs=2.4.2
+ - scipy=1.13.1
+ - xmltodict=0.14.2
- pip:
- - altair==4.0.0
- - ambiance==1.3.0
- - black==22.3
- - cpacspy==0.1.8
- - cython==0.29.30
- - framat
- - gmsh==4.10.3
+ - altair==5.5.0
+ - ambiance==1.3.1
+ - black==25.1.0
+ - cpacspy==0.2.0
+ - Cython==3.0.12
+ - framat==0.4.8
+ - gmsh==4.13.1
- markdownpy==0.1.4
- - om-pycycle
- - pytest==7.2.0
- - shapely
- - smt==1.2.0
- - streamlit==1.14.0
+ - matplotlib==3.10.1
+ - om-pycycle==4.3.0
+ - pycycle==0.0.8
+ - pydantic==2.11.3
+ - psutil==7.0.0
+ - pytest==8.3.4
+ - pyvista==0.44.2
+ - shapely==2.1.0
+ - smt==2.9.2
+ - stpyvista==0.1.4
+ - streamlit==1.44.1
- streamlit-autorefresh
-
+ - vtk==9.3.0
\ No newline at end of file
diff --git a/installation/CentOS/install_su2.sh b/installation/CentOS/install_su2.sh
index 8aa4fd5db..013b2cfdd 100644
--- a/installation/CentOS/install_su2.sh
+++ b/installation/CentOS/install_su2.sh
@@ -2,7 +2,8 @@
# Script to install SU2 on Centos 8
-su2_version="7.3.0"
+su2_version="8.1.0"
+mpi_version="4.1.1"
current_dir="$(pwd)"
@@ -33,6 +34,18 @@ echo export PYTHONPATH=\$PYTHONPATH:\$SU2_RUN >> ~/.bashrc
echo export PATH=\"\$PATH:\$SU2_RUN\" >> ~/.bashrc
echo "Installing MPICH..."
-sudo dnf install -y mpich
+sudo dnf install -y mpich$mpi_version
+
+echo "Adding MPICH path to the .bashrc"
+mpich_path="/usr/bin"
+echo export PATH=\"\$PATH:$mpich_path\" >> ~/.bashrc
+
+source ~/.bashrc
+
+echo "Checking SU2 version"
+"$SU2_RUN/SU2_CFD" --help
+
+echo "Checking MPICH version"
+mpirun --version
cd "$current_dir"
\ No newline at end of file
diff --git a/installation/INSTALLATION.md b/installation/INSTALLATION.md
index 2552e9084..98b8f7daa 100644
--- a/installation/INSTALLATION.md
+++ b/installation/INSTALLATION.md
@@ -19,7 +19,7 @@ Before you run this command, you should read the following remarks:
- If you already have a recent version of [Miniconda](https://docs.conda.io/en/latest/miniconda.html) or [Anaconda](https://anaconda.org/) installed on you computer, you can skip the line with `./install_conda.sh`
-- PyTornado, SUMO, SU2 and Paraview are not mandatory to run CEASIOMpy. You can skip the lines corresponding to those you don't want to install. However, if you want to be able to run all possible workflows, you should have all the modules.
+- Avl, SUMO, SU2 and Paraview are not mandatory to run CEASIOMpy. You can skip the lines corresponding to those you don't want to install. However, if you want to be able to run all possible workflows, you should have all the modules.
- Some softwares will need to be executed with `sudo` command. It will ask you for your password and you will have to type `yes` to confirm some of the installation steps.
@@ -39,7 +39,6 @@ cd CEASIOMpy/installation/Ubuntu
source ~/.bashrc
./install_ceasiompy.sh
./install_pyavl.sh
-./install_pytornado.sh
./install_sumo.sh
./install_su2.sh
./install_paraview.sh
@@ -56,7 +55,6 @@ cd CEASIOMpy/installation/CentOS
source ~/.bashrc
./install_ceasiompy.sh
./install_pyavl.sh
-./install_pytornado.sh
./install_sumo.sh
./install_su2.sh
./install_paraview.sh
@@ -70,7 +68,6 @@ conda activate ceasiompy
python -c "import ceasiompy"
cpacscreator # A "CPACSCreator" window will open, you can close it.
avl # The avl interface should appear in the terminal
-pytornado # You should see the help of PyTornado in the terminal.
sumo # A "SUMO" window will open, you can close it.
SU2_CFD # You should see something like "Error in "void CConfig::SetConfig_Parsing(char*)" in the terminal
paraview # A "Paraview" window will open, you can close it.
@@ -96,7 +93,6 @@ You can try to install it manually, the basic steps are the following:
- Create the conda environment `ceasiompy` with the command `conda env create -f environment.yml`
- Activate the conda environment with the command `conda activate ceasiompy`
- Run the command `pip install -e .`
-- Install [PyTornado](https://github.com/airinnova/pytornado)
- Install [SUMO](https://www.larosterna.com/products/open-source)
- Install [AVL](https://web.mit.edu/drela/Public/web/avl)
- Install [SU2](https://su2code.github.io/download.html)
@@ -113,7 +109,6 @@ You can install it manually, the basic steps are the following:
- Create the conda environment `ceasiompy` with the command `conda env create -f environment.yml`
- Activate the conda environment with the command `conda activate ceasiompy`
- Run the command `pip install -e .`
-- Install [PyTornado](https://github.com/airinnova/pytornado)
- Install [SUMO](https://www.larosterna.com/products/open-source)
- Install [AVL](https://web.mit.edu/drela/Public/web/avl)
- Install [SU2](https://su2code.github.io/download.html)
diff --git a/installation/Ubuntu/install_su2.sh b/installation/Ubuntu/install_su2.sh
index e08d3010b..0af542381 100755
--- a/installation/Ubuntu/install_su2.sh
+++ b/installation/Ubuntu/install_su2.sh
@@ -1,8 +1,9 @@
#!/bin/bash
-# Script to install SU2 on Ubuntu 20.04 and Mint 20.3
+# Script to install SU2
-su2_version="7.3.0"
+su2_version="8.1.0"
+mpi_version="4.1.1"
current_dir="$(pwd)"
@@ -33,6 +34,19 @@ echo export PYTHONPATH=\$PYTHONPATH:\$SU2_RUN >> ~/.bashrc
echo export PATH=\"\$PATH:\$SU2_RUN\" >> ~/.bashrc
echo "Installing MPICH..."
-sudo apt install -y mpich
+sudo apt install -y mpich=$mpi_version
+
+echo "Adding MPICH path to the .bashrc"
+
+mpich_path="/usr/bin"
+echo export PATH=\"\$PATH:$mpich_path\" >> ~/.bashrc
+
+source ~/.bashrc
+
+echo "Checking SU2 version"
+"$SU2_RUN/SU2_CFD" --help
+
+echo "Checking MPICH version"
+mpirun --version
cd "$current_dir"
\ No newline at end of file
diff --git a/src/bin/CEASIOMpy.command b/src/bin/CEASIOMpy.command
index 3753b168a..a63de2bec 100644
--- a/src/bin/CEASIOMpy.command
+++ b/src/bin/CEASIOMpy.command
@@ -7,4 +7,4 @@ source $CONDA_BASE/etc/profile.d/conda.sh
conda activate ceasiompy
# Run CEASIOMpy
-ceasiompy_run -g
+ceasiompy_run -g
diff --git a/src/bin/ceasiompy_exec.py b/src/bin/ceasiompy_exec.py
index 66b83ed58..682648a61 100755
--- a/src/bin/ceasiompy_exec.py
+++ b/src/bin/ceasiompy_exec.py
@@ -8,34 +8,30 @@
Main module of CEASIOMpy to launch workflow by different way.
-Python version: >=3.8
| Author: Aidan Jungo
| Creation: 2022-03-29
-Todo:
- *
-
"""
# =================================================================================================
# IMPORTS
# =================================================================================================
-import argparse
import os
-from pathlib import Path
+import argparse
-from ceasiompy.utils.ceasiomlogger import get_logger
-from ceasiompy.utils.commonpaths import CPACS_FILES_PATH, TEST_CASES_PATH, STREAMLIT_PATH
+from pathlib import Path
from ceasiompy.utils.workflowclasses import Workflow
-log = get_logger()
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
+from ceasiompy import log
+from unittest.mock import patch
+from ceasiompy.utils.commonpaths import (
+ STREAMLIT_PATH,
+ TEST_CASES_PATH,
+ CPACS_FILES_PATH,
+)
# =================================================================================================
# FUNCTIONS
@@ -102,7 +98,7 @@ def run_testcase(testcase_nb):
print(">> cd WKDIR")
print(
">> ceasiompy_run -m ../test_files/CPACSfiles/D150_simple.xml "
- "PyTornado SkinFriction SaveAeroCoefficients"
+ "PyAVL SkinFriction SaveAeroCoefficients"
)
elif testcase_nb == 5:
@@ -112,7 +108,7 @@ def run_testcase(testcase_nb):
print("\nTest case number must be 1,2,3,4 or 5.")
-def run_modules_list(args_list):
+def run_modules_list(args_list, test=False):
"""Run a workflow from a CPACS file and a list of modules."""
if len(args_list) < 2:
@@ -136,8 +132,18 @@ def run_modules_list(args_list):
modules_list = args_list[1:]
- log.info("CEASIOMpy as been started from a command line")
+ log.info("CEASIOMpy has been started from a command line.")
+ if test:
+ with patch("streamlit.runtime.scriptrunner_utils.script_run_context"):
+ with patch("streamlit.runtime.state.session_state_proxy"):
+ log.info(f"Integration test for {args_list}.")
+ run_ceasiompy_workflow(cpacs_path, modules_list, test)
+ else:
+ run_ceasiompy_workflow(cpacs_path, modules_list, test)
+
+
+def run_ceasiompy_workflow(cpacs_path, modules_list, test):
workflow = Workflow()
workflow.cpacs_in = cpacs_path
workflow.modules_list = modules_list
@@ -145,13 +151,13 @@ def run_modules_list(args_list):
workflow.write_config_file()
workflow.set_workflow()
- workflow.run_workflow()
+ workflow.run_workflow(test)
def run_config_file(config_file):
"""Run a workflow from a config file"""
- log.info("CEASIOMpy as been started from a config file")
+ log.info("CEASIOMpy has been started from a config file.")
config_file_path = Path(config_file)
@@ -169,18 +175,15 @@ def run_config_file(config_file):
def run_gui():
"""Create an run a workflow from a GUI."""
- log.info("CEASIOMpy as been started from the GUI")
-
+ log.info("CEASIOMpy has been started from the GUI.")
os.system(f"cd {STREAMLIT_PATH} && streamlit run CEASIOMpy.py")
-
# =================================================================================================
# MAIN
# =================================================================================================
def main():
-
parser = argparse.ArgumentParser(
description="CEASIOMpy: Conceptual Aircraft Design Environment",
usage=argparse.SUPPRESS,
@@ -235,7 +238,6 @@ def main():
return
if args.gui:
-
run_gui()
return
diff --git a/src/streamlit/CEASIOMpy.py b/src/streamlit/CEASIOMpy.py
index 63c55e16c..1782c47f8 100644
--- a/src/streamlit/CEASIOMpy.py
+++ b/src/streamlit/CEASIOMpy.py
@@ -3,121 +3,151 @@
Developed for CFS ENGINEERING, 1015 Lausanne, Switzerland
-Main Streamlit page for CEASIOMpy GUI
+Main Streamlit page for CEASIOMpy GUI.
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2022-09-09
-TODO:
-
"""
-import io
-from pathlib import Path
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
import pyvista as pv
-from ceasiompy.utils.commonnames import CEASIOMPY_BEIGE, CEASIOMPY_ORANGE
-from ceasiompy.utils.workflowclasses import Workflow
-from cpacspy.cpacspy import CPACS
-
import streamlit as st
-import streamlit.components.v1 as components
-from streamlitutils import create_sidebar, st_directory_picker
-how_to_text = (
- "### How to use CEASIOMpy?\n"
- "1. Chose your *Working directory*\n"
- "1. Chose a *CPACS file*\n"
- "1. Go to the *Workflow* page (with the menu above)\n"
+from stpyvista import stpyvista
+from src.streamlit.streamlitutils import (
+ create_sidebar,
)
-create_sidebar(how_to_text)
+from pathlib import Path
+from cpacspy.cpacspy import CPACS
+from ceasiompy.utils.workflowclasses import Workflow
+from ceasiompy.utils.commonpaths import WKDIR_PATH
+from ceasiompy.utils.commonnames import (
+ CEASIOMPY_BEIGE,
+ CEASIOMPY_ORANGE,
+)
-def section_select_working_dir():
+# =================================================================================================
+# CONSTANTS
+# =================================================================================================
- st.markdown("#### Working directory")
+HOW_TO_TEXT = (
+ "### How to use CEASIOMpy?\n"
+ "1. Choose a *Working directory*\n"
+ "1. Choose a *CPACS file*\n"
+ "1. Go to *Workflow* page (with menu above)\n"
+)
- if "workflow" not in st.session_state:
- st.session_state.workflow = Workflow()
+PAGE_NAME = "CEASIOMpy"
- st.session_state.workflow.working_dir = st_directory_picker(Path("../../WKDIR").absolute())
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
def section_select_cpacs():
+ if "workflow" not in st.session_state:
+ st.session_state.workflow = Workflow()
+ st.session_state.workflow.working_dir = WKDIR_PATH
st.markdown("#### CPACS file")
- st.session_state.cpacs_file = st.file_uploader("Select a CPACS file", type=["xml"])
+ # Check if the CPACS file path is already in session state
+ if "cpacs_file_path" in st.session_state:
+ cpacs_file_path = st.session_state.cpacs_file_path
+ if Path(cpacs_file_path).exists():
+ st.session_state.cpacs = CPACS(cpacs_file_path)
+ # st.info(f"**Aircraft name:** {st.session_state.cpacs.ac_name}")
+ else:
+ st.session_state.cpacs_file_path = None
- if st.session_state.cpacs_file:
+ # File uploader widget
+ uploaded_file = st.file_uploader(
+ "Select a CPACS file",
+ type=["xml"],
+ )
- cpacs_new_path = Path(
- st.session_state.workflow.working_dir, st.session_state.cpacs_file.name
- )
+ if uploaded_file:
+ cpacs_new_path = Path(st.session_state.workflow.working_dir, uploaded_file.name)
with open(cpacs_new_path, "wb") as f:
- f.write(st.session_state.cpacs_file.getbuffer())
+ f.write(uploaded_file.getbuffer())
st.session_state.workflow.cpacs_in = cpacs_new_path
st.session_state.cpacs = CPACS(cpacs_new_path)
+ st.session_state.cpacs_file_path = str(cpacs_new_path)
- cpacs_new_path = Path(
- st.session_state.workflow.working_dir, st.session_state.cpacs_file.name
- )
- st.info(f"**Aircraft name:** {st.session_state.cpacs.ac_name}")
+ # st.info(f"**Aircraft name:** {st.session_state.cpacs.ac_name}")
- if "cpacs" not in st.session_state:
- st.session_state.cpacs = CPACS(cpacs_new_path)
+ # Display the file uploader widget with the previously uploaded file
+ if "cpacs_file_path" in st.session_state and st.session_state.cpacs_file_path:
+ st.success(f"Uploaded file: {st.session_state.cpacs_file_path}")
def section_3D_view():
- """Show a 3D view of the aircraft by exporting a STL file. The pyvista viewer is based on:
- https://github.com/edsaac/streamlit-PyVista-viewer
+ """
+ Shows a 3D view of the aircraft by exporting a STL file.
+ The pyvista viewer is based on:
+ https://github.com/edsaac/streamlit-PyVista-viewer
"""
- st.markdown("## 3D view")
+ st.markdown("##### 3D view")
if "cpacs" not in st.session_state:
st.warning("No CPACS file has been selected!")
return
+ else:
+ stl_file = Path(st.session_state.workflow.working_dir, "aircraft.stl")
- if not st.button("Show 3D view"):
- return
+ st.session_state.cpacs.aircraft.tigl.exportMeshedGeometrySTL(str(stl_file), 0.01)
- stl_file = Path(st.session_state.workflow.working_dir, "aircraft.stl")
+ # Using pythreejs as pyvista backend
+ pv.set_jupyter_backend("static")
- st.session_state.cpacs.aircraft.tigl.exportMeshedGeometrySTL(str(stl_file), 0.01)
+ # Initialize pyvista reader and plotter
+ plotter = pv.Plotter(border=False, window_size=[572, 600])
+ plotter.background_color = CEASIOMPY_BEIGE
+ reader = pv.STLReader(str(stl_file))
- # Using pythreejs as pyvista backend
- pv.set_jupyter_backend("pythreejs")
+ # Read data and send to plotter
+ mesh = reader.read()
+ plotter.add_mesh(mesh, color=CEASIOMPY_ORANGE)
- # Initialize pyvista reader and plotter
- plotter = pv.Plotter(border=False, window_size=[572, 600])
- plotter.background_color = CEASIOMPY_BEIGE
- reader = pv.STLReader(str(stl_file))
+ # Camera
+ plotter.camera.azimuth = 110.0
+ plotter.camera.elevation = -20.0
+ plotter.camera.zoom(1.4)
- # Read data and send to plotter
- mesh = reader.read()
- plotter.add_mesh(mesh, color=CEASIOMPY_ORANGE)
+ stpyvista(plotter)
- # Camera
- plotter.camera.azimuth = 110.0
- plotter.camera.elevation = -20.0
- plotter.camera.zoom(1.4)
+# =================================================================================================
+# MAIN
+# =================================================================================================
- # Export to a pythreejs HTML
- model_html = io.StringIO()
- plotter.export_html(model_html, backend="pythreejs")
- # Show in webpage
- components.html(model_html.getvalue(), height=600, width=572, scrolling=False)
+if __name__ == "__main__":
+ create_sidebar(HOW_TO_TEXT)
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
-st.title("CEASIOMpy")
+ st.title(PAGE_NAME)
-section_select_working_dir()
-section_select_cpacs()
-section_3D_view()
+ section_select_cpacs()
+ section_3D_view()
diff --git a/src/streamlit/guiobjects.py b/src/streamlit/guiobjects.py
new file mode 100644
index 000000000..0d9cd0dc1
--- /dev/null
+++ b/src/streamlit/guiobjects.py
@@ -0,0 +1,200 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed for CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+GUI objects in CEASIOMpy.
+
+| Author : Leon Deligny
+| Creation: 21 March 2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+import pandas as pd
+import streamlit as st
+
+from src.streamlit.streamlitutils import save_cpacs_file
+from cpacspy.cpacsfunctions import (
+ get_string_vector,
+ get_value_or_default,
+)
+
+from ceasiompy import log
+
+# ==============================================================================
+# FUNCTIONS
+# ==============================================================================
+
+
+def aeromap_selection(cpacs, xpath, key, description):
+ aeromap_uid_list = cpacs.get_aeromap_uid_list()
+
+ if not len(aeromap_uid_list):
+ st.error("You must create an aeromap in order to use this module!")
+ else:
+ value = get_value_or_default(
+ cpacs.tixi, xpath, aeromap_uid_list[0]
+ )
+ if value in aeromap_uid_list:
+ idx = aeromap_uid_list.index(value)
+ else:
+ idx = 0
+ st.radio(
+ "Select an aeromap",
+ key=key,
+ options=aeromap_uid_list,
+ index=idx,
+ help=description,
+ on_change=save_cpacs_file,
+ )
+
+
+def aeromap_checkbox(cpacs, xpath, key, description) -> None:
+ aeromap_uid_list = cpacs.get_aeromap_uid_list()
+
+ if not len(aeromap_uid_list):
+ st.error("You must create an aeromap in order to use this module!")
+
+ else:
+ with st.columns([1, 2])[0]:
+ try:
+ default_otp = get_string_vector(cpacs.tixi, xpath)
+ except ValueError:
+ default_otp = None
+
+ st.multiselect(
+ "Select one or several aeromaps",
+ key=key,
+ options=aeromap_uid_list,
+ default=default_otp,
+ help=description,
+ on_change=save_cpacs_file,
+ )
+
+
+def path_vartype(key) -> None:
+ uploaded_file = st.file_uploader(
+ "Select a SU2 file",
+ type=["su2"],
+ )
+ if uploaded_file:
+ su2_file_path = (
+ st.session_state.workflow.working_dir
+ / uploaded_file.name
+ )
+
+ # Save the uploaded file to the specified path
+ with open(su2_file_path, "wb") as f:
+ f.write(uploaded_file.getbuffer())
+ st.session_state[key] = str(su2_file_path)
+ save_cpacs_file()
+
+ if key in st.session_state:
+ st.success(f"Uploaded file: {st.session_state[key]}")
+
+
+def multiselect_vartype(default_value, name, key) -> None:
+ # Initialize the list in session state if it doesn't exist
+ if key not in st.session_state:
+ st.session_state[key] = []
+ st.session_state[key].extend(default_value)
+
+ # Display the current list of floats in a table
+ if st.session_state[key]:
+ st.table(pd.DataFrame(st.session_state[key], columns=[name]))
+
+ # Input for new float value
+ new_value = st.number_input(name, value=0.0, step=0.1, key=f"new_{key}")
+
+ # Add button to append the new value to the list
+ if st.button("➕ Add", key=f"add_{key}"):
+ st.session_state[key].append(new_value)
+ save_cpacs_file()
+ st.rerun()
+
+ # Remove button to remove the last value from the list
+ if st.button("❌ Remove", key=f"remove_last_{key}"):
+ if st.session_state[key]:
+ st.session_state[key].pop()
+ save_cpacs_file()
+ st.rerun()
+
+
+def int_vartype(tixi, xpath, default_value, name, key, description) -> None:
+ with st.columns([1, 2])[0]:
+ value = int(get_value_or_default(tixi, xpath, default_value))
+ st.number_input(
+ name,
+ value=value,
+ key=key,
+ help=description,
+ on_change=save_cpacs_file
+ )
+
+
+def float_vartype(tixi, xpath, default_value, name, key, description) -> None:
+ value = get_value_or_default(
+ tixi, xpath, default_value
+ )
+ with st.columns([1, 2])[0]:
+ st.number_input(
+ name,
+ value=value,
+ format="%0.3f",
+ key=key,
+ help=description,
+ on_change=save_cpacs_file
+ )
+
+
+def list_vartype(tixi, xpath, default_value, name, key, description) -> None:
+ if default_value is None:
+ log.warning(f"Could not create GUI for {xpath} in list_vartype.")
+ else:
+ value = get_value_or_default(
+ tixi, xpath, default_value[0]
+ )
+ idx = default_value.index(value)
+ st.radio(
+ name,
+ options=default_value,
+ index=idx,
+ key=key,
+ help=description,
+ on_change=save_cpacs_file,
+ )
+
+
+def bool_vartype(tixi, xpath, default_value, name, key, description) -> None:
+ st.checkbox(
+ name,
+ value=get_value_or_default(tixi, xpath, default_value),
+ key=key,
+ help=description,
+ on_change=save_cpacs_file,
+ )
+
+
+def else_vartype(tixi, xpath, default_value, name, key, description) -> None:
+ if name != "Choose mesh":
+ value = str(get_value_or_default(tixi, xpath, default_value))
+ with st.columns([1, 2])[0]:
+ st.text_input(
+ name,
+ value=value,
+ key=key,
+ help=description,
+ on_change=save_cpacs_file,
+ )
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/src/streamlit/moduletab.py b/src/streamlit/moduletab.py
new file mode 100644
index 000000000..61e805851
--- /dev/null
+++ b/src/streamlit/moduletab.py
@@ -0,0 +1,256 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed for CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Streamlit Tabs per module function.
+
+| Author : Leon Deligny
+| Creation: 21 March 2025
+
+"""
+
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+import streamlit as st
+
+from cpacspy.cpacsfunctions import get_value_or_default
+from ceasiompy.utils.geometryfunctions import get_aircrafts_list
+from src.streamlit.streamlitutils import section_edit_aeromap
+from ceasiompy.utils.moduleinterfaces import get_specs_for_module
+from src.streamlit.guiobjects import (
+ int_vartype,
+ list_vartype,
+ bool_vartype,
+ else_vartype,
+ path_vartype,
+ float_vartype,
+ aeromap_checkbox,
+ aeromap_selection,
+ multiselect_vartype,
+)
+
+from typing import List
+from collections import OrderedDict
+
+from ceasiompy import log
+
+# ==============================================================================
+# FUNCTIONS
+# ==============================================================================
+
+
+def if_choice_vartype(
+ vartype_map,
+ session_state,
+ xpath,
+ default_value,
+ name,
+ key,
+ description,
+) -> None:
+
+ default_index = int(default_value.index(get_value_or_default(
+ session_state.cpacs.tixi,
+ xpath + "type",
+ "Other"
+ )))
+
+ selected_type = st.radio(
+ "Select variable type:",
+ options=default_value,
+ index=default_index,
+ key=f"{key}_type",
+ help="Choose the input type",
+ )
+ session_state[f"{key}_types"] = selected_type
+ selected_func = vartype_map[selected_type]
+
+ if selected_type == "Path":
+ selected_func(
+ key=key,
+ )
+ session_state.xpath_to_update[xpath] = key
+ elif selected_type == "Other":
+ selected_func(
+ tixi=session_state.cpacs.tixi,
+ xpath=xpath,
+ default_value="",
+ name=name,
+ key=key,
+ description=description,
+ )
+ session_state.xpath_to_update[xpath] = key
+ elif selected_type == "db":
+ selected_func(
+ tixi=session_state.cpacs.tixi,
+ xpath=xpath + "list",
+ default_value=get_aircrafts_list(),
+ name=name,
+ key=key + "list",
+ description=description,
+ )
+ session_state.xpath_to_update[xpath + "list"] = key + "list"
+ session_state.xpath_to_update[xpath + "type"] = f"{key}_types"
+
+
+def order_by_gps(inputs: List) -> OrderedDict:
+ groups = list(OrderedDict.fromkeys([v[6] for _, v in inputs.items()]))
+
+ groups_container = OrderedDict()
+ for group in groups:
+ groups_container[group] = st.expander(f"**{group}**", expanded=True)
+
+ return groups_container
+
+
+def checks(session_state, tabs) -> None:
+ if "tabs" not in session_state:
+ session_state["tabs"] = []
+
+ if "workflow_modules" in session_state and session_state.workflow_modules:
+ session_state.tabs = tabs(session_state.workflow_modules)
+
+ if "xpath_to_update" not in session_state:
+ session_state.xpath_to_update = {}
+
+
+def add_gui_object(
+ session_state,
+ name,
+ group,
+ groups_container,
+ key,
+ aeromap_map,
+ xpath,
+ description,
+ var_type,
+ vartype_map,
+ default_value,
+) -> None:
+
+ # Iterate per group
+ with groups_container[group]:
+
+ # Check if the name or var_type is in the dictionary and call the corresponding function
+ if name in aeromap_map:
+ aeromap_map[name](session_state.cpacs, xpath, key, description)
+ elif var_type == "path_type":
+ path_vartype(key)
+ elif var_type in vartype_map:
+ vartype_map[var_type](
+ session_state.cpacs.tixi,
+ xpath,
+ default_value,
+ name,
+ key,
+ description
+ )
+ elif var_type == "multiselect":
+ multiselect_vartype(default_value, name, key)
+ else:
+ else_vartype(
+ tixi=session_state.cpacs.tixi,
+ xpath=xpath,
+ default_value=default_value,
+ name=name,
+ key=key,
+ description=description,
+ )
+
+ session_state.xpath_to_update[xpath] = key
+
+
+def add_module_tab() -> None:
+ if "cpacs" not in st.session_state:
+ st.warning("No CPACS file has been selected!")
+ return
+
+ with st.expander("**Edit Aeromaps**", expanded=False):
+ section_edit_aeromap()
+
+ checks(st.session_state, st.tabs)
+
+ aeromap_map = {
+ "__AEROMAP_SELECTION": aeromap_selection,
+ "__AEROMAP_CHECKBOX": aeromap_checkbox,
+ }
+
+ vartype_map = {
+ int: int_vartype,
+ float: float_vartype,
+ list: list_vartype,
+ bool: bool_vartype,
+ }
+
+ dynamic_vartype_map = {
+ "Path": path_vartype,
+ "db": list_vartype,
+ "Other": else_vartype,
+ }
+
+ # Load each module iteratively
+ for m, (tab, module) in enumerate(
+ zip(st.session_state.tabs, st.session_state.workflow_modules)
+ ):
+
+ with tab:
+ st.text("")
+ specs = get_specs_for_module(module)
+ # Check if specs.cpacs_inout is None
+ if specs.cpacs_inout is None:
+ log.error("specs.cpacs_inout is None. Ensure it is initialized before use.")
+ else:
+ inputs = specs.cpacs_inout.get_gui_dict()
+ if not inputs:
+ st.warning("No settings to modify this module.")
+ continue
+
+ groups_container = order_by_gps(inputs)
+
+ for name, default_value, var_type, unit, xpath, description, group in inputs.values():
+ key = f"{m}_{module}_{name.replace(' ', '')}_{group.replace(' ', '')}"
+ process_unit(name, unit)
+
+ if var_type == "DynamicChoice":
+ with groups_container[group]:
+ if_choice_vartype(
+ dynamic_vartype_map,
+ session_state=st.session_state,
+ xpath=xpath,
+ default_value=default_value,
+ name=name,
+ key=key,
+ description=description,
+ )
+
+ else:
+ add_gui_object(
+ st.session_state,
+ name,
+ group,
+ groups_container,
+ key,
+ aeromap_map,
+ xpath,
+ description,
+ var_type,
+ vartype_map,
+ default_value,
+ )
+
+
+def process_unit(name: str, unit: str) -> None:
+ # TODO: Add constants in __init__ ?
+ if unit not in ["[]", "[1]", None]:
+ name = f"{name} {unit}"
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git "a/src/streamlit/pages/01_ \342\236\241_Workflow.py" "b/src/streamlit/pages/01_ \342\236\241_Workflow.py"
index 1a3ad7510..aa43c456b 100644
--- "a/src/streamlit/pages/01_ \342\236\241_Workflow.py"
+++ "b/src/streamlit/pages/01_ \342\236\241_Workflow.py"
@@ -5,20 +5,30 @@
Streamlit page to create a CEASIOMpy workflow
-Python version: >=3.8
-| Author : Aidan Jungo
+| Author: Aidan Jungo
| Creation: 2022-09-16
-
-TODO:
+| Modified: Leon Deligny
+| Date: 07-Mar-2025
"""
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
import streamlit as st
+
from ceasiompy.utils.moduleinterfaces import get_module_list
from streamlitutils import create_sidebar
-how_to_text = (
+# ==============================================================================
+# CONSTANTS
+# ==============================================================================
+
+PAGE_NAME = "Workflow"
+
+HOW_TO_TEXT = (
"### How to use Create a workflow?\n"
"You can either:\n"
"- Select a predefined workflow and modify it\n"
@@ -26,48 +36,25 @@
"When it is done, go to the *Settings* page\n"
)
-create_sidebar(how_to_text)
-
-# Custom CSS
-st.markdown(
- """
-
- """,
- unsafe_allow_html=True,
-)
+# ==============================================================================
+# FUNCTIONS
+# ==============================================================================
def section_predefined_workflow():
+ """
+ Where to define the Pre-defined workflows.
+ """
st.markdown("#### Predefined Workflows")
predefine_workflows = [
- ["PyAVL", "SaveAeroCoefficients"],
+ ["PyAVL", "DynamicStability", "Database"],
+ ["CPACSUpdater", "CPACSCreator", "CPACS2GMSH", "Database"],
+ ["SU2Run", "Database"],
["CPACS2GMSH", "SU2Run", "SkinFriction"],
+ # ["CPACS2SUMO", "SUMOAutoMesh", "SU2Run", "ExportCSV"],
["CPACS2GMSH", "ThermoData", "SU2Run"],
- ["CPACS2SUMO", "SUMOAutoMesh", "SU2Run", "ExportCSV"],
]
for workflow in predefine_workflows:
@@ -76,6 +63,9 @@ def section_predefined_workflow():
def section_add_module():
+ """
+ Where to select the workflow.
+ """
st.markdown("#### Your workflow")
@@ -88,19 +78,21 @@ def section_add_module():
col1, col2, col3, _ = st.columns([6, 1, 1, 5])
with col1:
- st.markdown(f"**{module}**")
+ st.button(
+ module,
+ key=f"module_number_{i}"
+ )
with col2:
if st.button("⬆️", key=f"move{i}", help="Move up") and i > 0:
st.session_state.workflow_modules.pop(i)
st.session_state.workflow_modules.insert(i - 1, module)
- st.experimental_rerun()
+ st.rerun()
with col3:
if st.button("❌", key=f"del{i}", help=f"Remove {module} from the workflow"):
st.session_state.workflow_modules.pop(i)
- st.experimental_rerun()
-
+ st.rerun()
else:
st.warning("No module has been added to the workflow.")
@@ -111,23 +103,63 @@ def section_add_module():
col1, col2 = st.columns(2)
with col1:
- st.markdown(" ")
module = st.selectbox("Module to add to the workflow:", available_module_list)
with col2:
- st.markdown(" ")
- st.markdown("#")
+ # Add vertical spacing to match the label height of selectbox
+ st.markdown("
", unsafe_allow_html=True)
if st.button("✔", help="Add this module to the workflow"):
st.session_state.workflow_modules.append(module)
- st.experimental_rerun()
-
-
-st.title("Workflow")
-
-section_predefined_workflow()
-
-st.markdown("---")
-
-section_add_module()
-
-st.markdown("---")
+ st.rerun()
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ # Define interface
+ create_sidebar(HOW_TO_TEXT)
+
+ # Custom CSS
+ st.markdown(
+ """
+
+ """,
+ unsafe_allow_html=True,
+ )
+
+ st.title(PAGE_NAME)
+
+ section_predefined_workflow()
+
+ st.markdown("---")
+
+ section_add_module()
+
+ st.markdown("---")
+
+ # Add last_page
+ st.session_state.last_page = PAGE_NAME
diff --git "a/src/streamlit/pages/02_\342\232\231\357\270\217_Settings.py" "b/src/streamlit/pages/02_\342\232\231\357\270\217_Settings.py"
index 56243454c..37427628e 100644
--- "a/src/streamlit/pages/02_\342\232\231\357\270\217_Settings.py"
+++ "b/src/streamlit/pages/02_\342\232\231\357\270\217_Settings.py"
@@ -5,442 +5,43 @@
Streamlit page to change settings of a CEASIOMpy workflow
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2022-09-16
-
-TODO:
+| Modified : Leon Deligny
+| Date: 10-Mar-2025
"""
-from collections import OrderedDict
-from pathlib import Path
-from cpacspy.cpacsfunctions import create_branch
-
-from tixi3 import tixi3wrapper
-from cpacspy.cpacsfunctions import open_tixi
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
-import pandas as pd
import streamlit as st
-from ceasiompy.utils.moduleinterfaces import get_specs_for_module, get_toolinput_file_path
-from cpacspy.cpacsfunctions import (
- add_string_vector,
- add_value,
- get_string_vector,
- get_value_or_default,
-)
-from cpacspy.cpacspy import CPACS
-from streamlitutils import create_sidebar
-from ceasiompy.utils.commonxpath import SU2MESH_XPATH
-import os
+from src.streamlit.moduletab import add_module_tab
-how_to_text = (
- "### How to use Settings?\n"
- "1. With *Edit aeromap* you can create or modify aeromap\n"
- "1. Then, you can go though each tab and modify the settings of each module\n"
- "1. Click the *Save* button\n"
- "1. Go to the *Run Workflow* page\n"
+from streamlitutils import (
+ create_sidebar,
+ save_cpacs_file,
)
-create_sidebar(how_to_text)
+# ==============================================================================
+# CONSTANTS
+# ==============================================================================
-# Custom CSS
-st.markdown(
- """
-
- """,
- unsafe_allow_html=True,
+HOW_TO_TEXT = (
+ "### How to use Settings?\n"
+ "1. With *Edit aeromap* you can create or modify an aeromap\n"
+ "1. Through each tab you can modify the settings of each module\n"
+ "1. Once finished, go to the *Run Workflow* page\n"
)
+PAGE_NAME = "Settings"
-def update_value(xpath, key):
- if key in st.session_state:
- value = st.session_state[key]
-
- if isinstance(value, list):
- if value:
- add_string_vector(st.session_state.cpacs.tixi, xpath, value)
- return
- else:
- value = ""
-
- add_value(st.session_state.cpacs.tixi, xpath, value)
-
-
-def update_all_modified_value():
- for xpath, key in st.session_state.xpath_to_update.items():
- update_value(xpath, key)
-
-
-def save_cpacs_file():
- update_all_modified_value()
- saved_cpacs_file = Path(st.session_state.workflow.working_dir, "CPACS_selected_from_GUI.xml")
- st.session_state.cpacs.save_cpacs(saved_cpacs_file, overwrite=True)
- st.session_state.workflow.cpacs_in = saved_cpacs_file
- st.session_state.cpacs = CPACS(saved_cpacs_file)
-
-
-# def upload_and_save_file(file_uploader, save_dir):
-
-# file = file_uploader("Select a file", type=["cgns", "su2"], key="file_uploader_key")
-
-# if file is not None:
-# file_name = file.name
-
-# # Crea la cartella di salvataggio se non esiste
-# os.makedirs(save_dir, exist_ok=True)
-
-# # Salva il file nella directory specificata
-# file_path = os.path.join(save_dir, file_name)
-# with open(file_path, "wb") as f:
-# f.write(file.getbuffer())
-
-# # Visualizza il percorso completo del file locale
-# st.success(f"File saved to: {file_path}")
-
-# return file_path
-
-# return None
-
-
-def section_edit_aeromap():
- st.markdown("#### Available aeromaps")
-
- aeromap_uid_list = st.session_state.cpacs.get_aeromap_uid_list()
-
- for i, aeromap in enumerate(aeromap_uid_list):
- col1, col2, col3, _ = st.columns([6, 1, 1, 5])
-
- with col1:
- st.markdown(f"**{aeromap}**")
-
- with col2:
- if st.button("⬆️", key=f"export{i}", help="Export aeromap") and i != 0:
- csv_file_name = aeromap.replace(" ", "_") + ".csv"
- st.session_state.cpacs.get_aeromap_by_uid(aeromap).export_csv(csv_file_name)
- with open(csv_file_name) as f:
- st.download_button("Download", f, file_name=csv_file_name)
-
- with col3:
- if st.button("❌", key=f"del{i}", help="Delete this aeromap"):
- st.session_state.cpacs.delete_aeromap(aeromap)
- st.experimental_rerun()
-
- st.markdown("#### Add a point")
-
- selected_aeromap = st.selectbox(
- "in", aeromap_uid_list, help="Choose in which aeromap you want to add the point"
- )
-
- col1, col2, col3, col4, col5 = st.columns([2, 2, 2, 2, 1])
-
- with col1:
- alt = st.number_input("Alt", value=1000, min_value=0, step=100)
- with col2:
- mach = st.number_input("Mach", value=0.3, min_value=0.0, step=0.1)
- with col3:
- aos = st.number_input("AoS", value=0.0, min_value=-90.0, max_value=90.0, step=0.1)
- with col4:
- aoa = st.number_input("AoA", value=0.0, min_value=-90.0, max_value=90.0, step=0.1)
- with col5:
- st.session_state.point_exist = False
- st.markdown("")
- st.markdown("")
- if st.button("➕"):
- aeromap = st.session_state.cpacs.get_aeromap_by_uid(selected_aeromap)
-
- try:
- aeromap.add_row(mach=mach, alt=alt, aos=aos, aoa=aoa)
- except ValueError:
- st.session_state.point_exist = True
-
- if not st.session_state.point_exist:
- aeromap.save()
- save_cpacs_file()
-
- if st.session_state.point_exist:
- st.error("This point already exist in this aeromap")
-
- if selected_aeromap:
- st.dataframe(st.session_state.cpacs.get_aeromap_by_uid(selected_aeromap).df)
-
- st.markdown("#### Create new aeromap")
-
- form = st.form("create_new_aeromap_form")
-
- help_aeromap_uid = "The aeromap will contain 1 point corresponding to the value above, then \
- you can add more point to it."
- new_aeromap_uid = form.text_input(
- "Aeromap uid",
- help=help_aeromap_uid,
- )
- default_description = "Created with CEASIOMpy Graphical user interface"
- new_aeromap_description = form.text_input(
- "Aeromap description", value=default_description, help="optional"
- )
-
- if form.form_submit_button("Create new"):
- if new_aeromap_uid not in aeromap_uid_list:
- new_aeromap = st.session_state.cpacs.create_aeromap(new_aeromap_uid)
- new_aeromap.description = new_aeromap_description
- new_aeromap.add_row(mach=mach, alt=alt, aos=aos, aoa=aoa)
- new_aeromap.save()
- st.experimental_rerun()
- else:
- st.error("There is already an aeromap with this name!")
-
- st.markdown("#### Import aeromap from CSV")
-
- uploaded_csv = st.file_uploader("Choose a CSV file")
- if uploaded_csv:
- uploaded_aeromap_uid = uploaded_csv.name.split(".csv")[0]
-
- if st.button("Add this aeromap"):
- if uploaded_aeromap_uid in aeromap_uid_list:
- st.error("There is already an aeromap with this name!")
- return
-
- new_aeromap = st.session_state.cpacs.create_aeromap(uploaded_aeromap_uid)
- new_aeromap.df = pd.read_csv(uploaded_csv, keep_default_na=False)
- new_aeromap.save()
- st.experimental_rerun()
-
-
-def mesh_file_upload():
- st.markdown("#### Upload mesh file")
-
- # Verifica se è stato caricato un file mesh
- uploaded_mesh = st.file_uploader(
- "Select a mesh file",
- key="00_mesh_upload",
- type=["su2", "cgns"],
- )
-
- if uploaded_mesh:
- # Crea la cartella "mesh" se non esiste
- mesh_dir = os.path.join(st.session_state.workflow.working_dir, "mesh")
- os.makedirs(mesh_dir, exist_ok=True)
-
- # Crea un nuovo percorso per il file .su2 nella cartella "mesh"
- mesh_new_path = os.path.join(mesh_dir, uploaded_mesh.name)
- print(f"mesh path: {mesh_new_path}")
-
- try:
- # Scrivi il file nel percorso specificato
- with open(mesh_new_path, "wb") as f:
- f.write(uploaded_mesh.getbuffer())
-
- # Apri il file CPACS
- cpacs_path = st.session_state.workflow.cpacs_in
- print(f"cpacs_path: {cpacs_path}")
- tixi = open_tixi(cpacs_path)
-
- # Definisci la variabile mesh_xpath in base alla tua struttura CPACS
- mesh_xpath = "/cpacs/toolspecific/CEASIOMpy/filesPath/su2Mesh"
-
- # Verifica se il percorso esiste
- if not tixi.checkElement(mesh_xpath):
- # Se il percorso non esiste, crealo
- create_branch(tixi, mesh_xpath)
-
- if st.button("Add this mesh"):
- # Aggiorna il percorso del file SU2 mesh nel documento CPACS
- tixi.updateTextElement(mesh_xpath, str(mesh_new_path))
-
- # Salva il file CPACS
- save_cpacs_file()
-
- return mesh_new_path
-
- except Exception as e:
- st.error(f"An error occurred: {e}")
- return None
-
- return None
-
-
-def add_module_tab():
- if "cpacs" not in st.session_state:
- st.warning("No CPACS file has been selected!")
- return
-
- with st.expander("Edit Aeromaps"):
- section_edit_aeromap()
-
- # with st.expander("Add mesh"):
- # mesh_file_upload()
-
- if "tabs" not in st.session_state:
- st.session_state["tabs"] = []
-
- if st.session_state.workflow_modules:
- st.session_state.tabs = st.tabs(st.session_state.workflow_modules)
-
- st.session_state.xpath_to_update = {}
-
- for m, (tab, module) in enumerate(
- zip(st.session_state.tabs, st.session_state.workflow_modules)
- ):
- with tab:
- st.text("")
- specs = get_specs_for_module(module)
- inputs = specs.cpacs_inout.get_gui_dict()
-
- if not inputs:
- st.warning("No settings to modify this module")
- continue
-
- groups = list(OrderedDict.fromkeys([v[6] for _, v in inputs.items()]))
-
- groups_container = OrderedDict()
- for group in groups:
- groups_container[group] = st.container()
-
- with groups_container[group]:
- st.markdown(f"**{group}**")
-
- for name, default_value, var_type, unit, xpath, description, group in inputs.values():
- with groups_container[group]:
- if not group:
- group = "none"
-
- key = f"{m}_{module}_{name.replace(' ', '')}_{group.replace(' ', '')}"
-
- if unit not in ["", "1", None]:
- name = f"{name} {unit}"
-
- if name == "__AEROMAP_SELECTION":
- aeromap_uid_list = st.session_state.cpacs.get_aeromap_uid_list()
-
- if not len(aeromap_uid_list):
- st.error("You must create an aeromap in order to use this module!")
- continue
-
- value = get_value_or_default(
- st.session_state.cpacs.tixi, xpath, aeromap_uid_list[0]
- )
- if value in aeromap_uid_list:
- idx = aeromap_uid_list.index(value)
- else:
- idx = 0
- st.radio(
- "Select an aeromap",
- key=key,
- options=aeromap_uid_list,
- index=idx,
- help=description,
- )
-
- elif name == "__AEROMAP_CHECKBOX":
- aeromap_uid_list = st.session_state.cpacs.get_aeromap_uid_list()
-
- if not len(aeromap_uid_list):
- st.error("You must create an aeromap in order to use this module!")
- continue
-
- with st.columns([1, 2])[0]:
- try:
- default_otp = get_string_vector(st.session_state.cpacs.tixi, xpath)
- except ValueError:
- default_otp = None
-
- st.multiselect(
- "Select one or several aeromaps",
- key=key,
- options=aeromap_uid_list,
- default=default_otp,
- help=description,
- )
-
- elif name == "pathtype":
- # Chiama la funzione mesh_file_upload() e ottieni il percorso del file mesh
- mesh_path = mesh_file_upload()
-
- with st.columns([1, 2])[0]:
- st.text_input(
- "Mesh Path",
- value=mesh_path,
- key=key,
- help="Path to the mesh file",
- )
-
- elif var_type == int:
- with st.columns([1, 2])[0]:
- st.number_input(
- name,
- value=int(
- get_value_or_default(
- st.session_state.cpacs.tixi, xpath, default_value
- )
- ),
- key=key,
- help=description,
- )
-
- elif var_type == float:
- with st.columns([1, 2])[0]:
- st.number_input(
- name,
- value=get_value_or_default(
- st.session_state.cpacs.tixi, xpath, default_value
- ),
- format="%g",
- key=key,
- help=description,
- )
-
- elif var_type == list:
- value = get_value_or_default(
- st.session_state.cpacs.tixi, xpath, default_value[0]
- )
- idx = default_value.index(value)
- st.radio(
- name,
- options=default_value,
- index=idx,
- key=key,
- help=description,
- )
-
- elif var_type == bool:
- st.checkbox(
- name,
- value=get_value_or_default(
- st.session_state.cpacs.tixi, xpath, default_value
- ),
- key=key,
- help=description,
- )
-
- # elif var_type == "pathtype":
-
- # save_dir = "tempDir"
- # mesh_path = upload_and_save_file(st.file_uploader, save_dir)
- # print(f"mesh_path={mesh_path}")
- # st.text_input(get_value_or_default(
- # st.session_state.cpacs.tixi, xpath, mesh_path))
- # update_value(xpath, key)
-
- else:
- with st.columns([1, 2])[0]:
- st.text_input(
- name,
- value=get_value_or_default(
- st.session_state.cpacs.tixi, xpath, default_value
- ),
- key=key,
- help=description,
- )
-
- st.session_state.xpath_to_update[xpath] = key
+# ==============================================================================
+# FUNCTIONS
+# ==============================================================================
def section_settings():
@@ -448,17 +49,45 @@ def section_settings():
st.warning("No module selected!")
return
- add_module_tab()
-
if not len(st.session_state.workflow_modules):
st.warning("You must first build a workflow in the corresponding tab.")
- if "cpacs" in st.session_state:
- with st.columns([3, 2, 3])[1]:
- if st.button("Save 💾", key="save_button", help="Save CPACS"):
- save_cpacs_file()
+ add_module_tab()
+
+ # Make sure to run at least once to pre-load the default values
+ # of __specs__.py files. Then save each modifications independently.
+ # Important: Needs to be called after add_module_tab.
+ if "save_cpacs_file_run" not in st.session_state:
+ save_cpacs_file()
+ st.session_state.save_cpacs_file_run = True
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+
+ # Define interface
+ create_sidebar(HOW_TO_TEXT)
+
+ # Custom CSS
+ st.markdown(
+ """
+
+ """,
+ unsafe_allow_html=True,
+ )
+ st.title(PAGE_NAME)
-st.title("Settings")
+ section_settings()
-section_settings()
+ # Update last_page
+ st.session_state.last_page = PAGE_NAME
diff --git "a/src/streamlit/pages/03_\342\226\266\357\270\217_Run_Workflow.py" "b/src/streamlit/pages/03_\342\226\266\357\270\217_Run_Workflow.py"
index a02005ead..fc81fca0b 100644
--- "a/src/streamlit/pages/03_\342\226\266\357\270\217_Run_Workflow.py"
+++ "b/src/streamlit/pages/03_\342\226\266\357\270\217_Run_Workflow.py"
@@ -5,69 +5,98 @@
Streamlit page to run a CEASIOMpy workflow
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2022-09-16
-
-TODO:
+| Modified : Leon Deligny
+| Date: 10-Mar-2025
"""
-import os
-from pathlib import Path
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
-import ceasiompy.__init__
+import os
+import psutil
+import signal
import streamlit as st
-from streamlitutils import create_sidebar
+
from streamlit_autorefresh import st_autorefresh
+from streamlitutils import (
+ create_sidebar,
+ save_cpacs_file,
+)
-CEASIOMPY_PATH = Path(ceasiompy.__init__.__file__).parents[1]
-LOGFILE = Path(CEASIOMPY_PATH, "ceasiompy.log")
+from pathlib import Path
-how_to_text = (
+from ceasiompy.utils.commonpaths import LOGFILE
+
+# ==============================================================================
+# CONSTANTS
+# ==============================================================================
+
+# Set the current page in session state
+PAGE_NAME = "Run Workflow"
+
+HOW_TO_TEXT = (
"### How to Run your workflow?\n"
- "1. Just click on the *Run* button\n"
- "Depending your workflow it could take time to get result, you can see the logs of what's "
- "happening.\n\n"
+ "1. Click on the *Run* button\n"
+ "Some workflows takes time, you can always check the LogFile \n\n"
"2. When it is done, go to the *Results* page\n"
)
-create_sidebar(how_to_text)
+# ==============================================================================
+# FUNCTIONS
+# ==============================================================================
-# Custom CSS
-st.markdown(
+
+def terminate_previous_workflows() -> None:
"""
-
- """,
- unsafe_allow_html=True,
-)
+ Terminate any previously running workflow processes.
+ """
+ for proc in psutil.process_iter(attrs=["pid", "name", "cmdline"]):
+ try:
+ # Check if the process is running the workflow script
+ if "python" in proc.info["name"] and "runworkflow.py" in proc.info["cmdline"]:
+ # Terminate the process
+ os.kill(proc.info["pid"], signal.SIGTERM)
+ except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
+ pass
-def run_workflow_button():
- if st.button("Run ▶️", help="Run the workflow "):
+def workflow_buttons() -> None:
+ """
+ Run workflow button.
+ """
+
+ # Create two buttons side by side
+ col1, col2 = st.columns([1, 1])
- # save_cpacs_file()
+ with col1:
+ if st.button("Run ▶️", help="Run the workflow"):
+ terminate_previous_workflows()
- st.session_state.workflow.modules_list = st.session_state.workflow_modules
- st.session_state.workflow.optim_method = "None"
- st.session_state.workflow.module_optim = ["NO"] * len(
- st.session_state.workflow.modules_list
- )
- st.session_state.workflow.write_config_file()
+ st.session_state.workflow.modules_list = st.session_state.workflow_modules
+ st.session_state.workflow.optim_method = "None"
+ st.session_state.workflow.module_optim = ["NO"] * len(
+ st.session_state.workflow.modules_list
+ )
+ st.session_state.workflow.write_config_file()
- # Run workflow from an external script
- config_path = Path(st.session_state.workflow.working_dir, "ceasiompy.cfg")
- os.system(f"python runworkflow.py {config_path} &")
+ # Run workflow from an external script
+ config_path = Path(st.session_state.workflow.working_dir, "ceasiompy.cfg")
+ os.system(f"python runworkflow.py {config_path} &")
+ with col2:
+ if st.button("Terminate ✖️", help="Terminate the workflow"):
+ terminate_previous_workflows()
-def show_logs():
+
+def show_logs() -> None:
+ """
+ Log interface.
+ """
st.markdown("")
st.markdown("##### Logfile")
@@ -77,12 +106,42 @@ def show_logs():
lines_str = "\n".join(reversed(lines))
- st.text_area("(more recent on top)", lines_str, height=300, disabled=True)
+ st.text_area("(more recent on top)", lines_str, height=400, disabled=True)
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+
+ # Define Interface
+ create_sidebar(HOW_TO_TEXT)
+
+ # Custom CSS
+ st.markdown(
+ """
+
+ """,
+ unsafe_allow_html=True,
+ )
+
+ st.title(PAGE_NAME)
+ if "last_page" in st.session_state and st.session_state.last_page != PAGE_NAME:
+ save_cpacs_file()
-st.title("Run workflow")
+ workflow_buttons()
+ show_logs()
-run_workflow_button()
-show_logs()
+ # AutoRefresh for logs
+ st_autorefresh(interval=1000, limit=10000, key="auto_refresh")
-st_autorefresh(interval=1000, limit=10000, key="auto_refresh")
+ # Update last_page
+ st.session_state.last_page = PAGE_NAME
diff --git "a/src/streamlit/pages/04_\360\237\223\210_Results.py" "b/src/streamlit/pages/04_\360\237\223\210_Results.py"
index bd7b37f7e..0a2403b13 100644
--- "a/src/streamlit/pages/04_\360\237\223\210_Results.py"
+++ "b/src/streamlit/pages/04_\360\237\223\210_Results.py"
@@ -5,55 +5,50 @@
Streamlit page to show results of CEASIOMpy
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2022-09-16
-
-TODO:
+| Modified : Leon Deligny
+| Date: 10-Mar-2025
"""
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
import os
-from pathlib import Path
import pandas as pd
-import plotly.graph_objects as go
import streamlit as st
-from ceasiompy.utils.commonpaths import DEFAULT_PARAVIEW_STATE
-from cpacspy.cpacspy import CPACS
-from cpacspy.utils import PARAMS_COEFS
-from streamlit_autorefresh import st_autorefresh
-from streamlitutils import create_sidebar
-
-how_to_text = (
- "### How to check your results\n"
- "1. Select the aeromap(s) you want to show\n"
- "1. Chose the parameter you want to plot\n"
- "1. Save some figure if you want\n"
- "1. Check results from each module\n"
-)
-
-create_sidebar(how_to_text)
+import plotly.graph_objects as go
+from streamlit_autorefresh import st_autorefresh
+from streamlitutils import create_sidebar, get_last_workflow
-def get_last_workflow():
- """Get the last workflow of the working directory"""
+from pathlib import Path
+from cpacspy.cpacspy import CPACS
- if "workflow" not in st.session_state:
- st.warning("No workflow to show the result yet.")
- return
+from cpacspy.utils import PARAMS_COEFS
+from ceasiompy.utils.commonpaths import DEFAULT_PARAVIEW_STATE
- last_workflow_nb = 0
+# =================================================================================================
+# CONSTANTS
+# =================================================================================================
- for dir in Path(st.session_state.workflow.working_dir).iterdir():
- if "Workflow_" in str(dir):
- last_workflow_nb = max(last_workflow_nb, int(str(dir).split("_")[-1]))
+HOW_TO_TEXT = (
+ "### How to check your results\n"
+ "1. Select an aeromap(s) \n"
+ "1. Choose the parameters to plot\n"
+ "1. Save the figure\n"
+ "1. Check the results for each module\n"
+)
- if last_workflow_nb == 0:
- return None
+PAGE_NAME = "Results"
- return Path(st.session_state.workflow.working_dir, f"Workflow_{last_workflow_nb:03}")
+# =================================================================================================
+# FUNCTIONS
+# =================================================================================================
def clear_containers(container_list):
@@ -64,70 +59,83 @@ def clear_containers(container_list):
del st.session_state[container]
+def display_results_else(path):
+ if path.is_dir():
+ for child in path.iterdir():
+ display_results(child)
+ else:
+ st.text_area(path.stem, path.read_text(), height=200, key=str(path))
+
+
def display_results(results_dir):
- """Display results depending which type of file they are."""
+ try:
+ """Display results depending which type of file they are."""
- container_list = ["logs_container", "figures_container", "paraview_container"]
- clear_containers(container_list)
+ container_list = ["logs_container", "figures_container", "paraview_container"]
+ clear_containers(container_list)
- for child in sorted(Path(results_dir).iterdir()):
+ for child in sorted(Path(results_dir).iterdir()):
- if child.suffix == ".smx":
- if st.button(f"Open {child.name} with SUMO", key=f"{child}_sumo_geom"):
- os.system(f"dwfsumo {str(child)}")
+ if child.suffix == ".smx":
+ if st.button(f"Open {child.name} with SUMO", key=f"{child}_sumo_geom"):
+ os.system(f"dwfsumo {str(child)}")
- elif child.suffix == ".su2":
- if st.button(f"Open {child.name} with Scope", key=f"{child}_su2_mesh"):
- os.system(f"dwfscope {str(child)}")
+ elif child.suffix == ".su2":
+ if st.button(f"Open {child.name} with Scope", key=f"{child}_su2_mesh"):
+ os.system(f"dwfscope {str(child)}")
- elif child.suffix == ".vtu":
+ elif child.suffix == ".vtu":
- if "paraview_container" not in st.session_state:
- st.session_state["paraview_container"] = st.container()
- st.session_state.paraview_container.markdown("**Paraview**")
+ if "paraview_container" not in st.session_state:
+ st.session_state["paraview_container"] = st.container()
+ st.session_state.paraview_container.markdown("**Paraview**")
- if st.session_state.paraview_container.button(
- f"Open {child.name} with Paraview", key=f"{child}_vtu"
- ):
- open_paraview(child)
+ if st.session_state.paraview_container.button(
+ f"Open {child.name} with Paraview", key=f"{child}_vtu"
+ ):
+ open_paraview(child)
- elif child.suffix == ".png":
+ elif child.suffix == ".png":
- if "figures_container" not in st.session_state:
- st.session_state["figures_container"] = st.container()
- st.session_state.figures_container.markdown("**Figures**")
+ if "figures_container" not in st.session_state:
+ st.session_state["figures_container"] = st.container()
+ st.session_state.figures_container.markdown("**Figures**")
- st.session_state.figures_container.markdown(f"{child.stem.replace('_',' ')}")
- st.session_state.figures_container.image(str(child))
+ st.session_state.figures_container.markdown(f"{child.stem.replace('_', ' ')}")
+ st.session_state.figures_container.image(str(child))
- elif child.suffix == ".md":
- st.markdown(child.read_text())
+ elif child.suffix == ".md":
+ st.markdown(child.read_text())
- elif child.suffix == ".json":
- st.text_area(child.stem, child.read_text(), height=200)
+ elif child.suffix == ".json":
+ st.text_area(child.stem, child.read_text(), height=200)
- elif child.suffix == ".log" or child.suffix == ".txt":
- if "logs_container" not in st.session_state:
- st.session_state["logs_container"] = st.container()
- st.session_state.logs_container.markdown("**Logs**")
- st.session_state.logs_container.text_area(child.stem, child.read_text(), height=200)
+ elif child.suffix == ".log" or child.suffix == ".txt":
+ if "logs_container" not in st.session_state:
+ st.session_state["logs_container"] = st.container()
+ st.session_state.logs_container.markdown("**Logs**")
+ st.session_state.logs_container.text_area(
+ child.stem, child.read_text(), height=200)
- elif child.name == "history.csv":
- st.markdown("**Convergence**")
+ elif child.name == "history.csv":
+ st.markdown("**Convergence**")
- df = pd.read_csv(child)
- df.rename(columns=lambda x: x.strip().strip('"'), inplace=True)
+ df = pd.read_csv(child)
+ df.rename(columns=lambda x: x.strip().strip('"'), inplace=True)
- st.line_chart(df[["CD", "CL", "CMy"]])
- st.line_chart(df[["rms[Rho]", "rms[RhoU]", "rms[RhoV]", "rms[RhoW]", "rms[RhoE]"]])
+ st.line_chart(df[["CD", "CL", "CMy"]])
+ st.line_chart(df[["rms[Rho]", "rms[RhoU]", "rms[RhoV]", "rms[RhoW]", "rms[RhoE]"]])
- elif child.suffix == ".csv":
- st.markdown(f"**{child.name}**")
- st.dataframe(pd.read_csv(child))
+ elif child.suffix == ".csv":
+ st.markdown(f"**{child.name}**")
+ st.dataframe(pd.read_csv(child))
- elif "Case" in child.name and child.is_dir():
- with st.expander(child.stem, expanded=False):
- display_results(child)
+ elif "Case" in child.name and child.is_dir():
+ with st.expander(child.stem, expanded=False):
+ display_results(child)
+
+ except BaseException:
+ display_results_else(results_dir)
def open_paraview(file):
@@ -248,7 +256,7 @@ def show_aeromap():
if st.button("Save this figure 📷"):
fig_name = f"{cpacs.ac_name}_{y_axis}_vs_{x_axis}{img_format}"
current_workflow = get_last_workflow()
- aerocoef_dir = Path(current_workflow, "Results", "AeroCoefficients")
+ aerocoef_dir = Path(current_workflow, "Results", "ExportCSV")
if not aerocoef_dir.exists():
aerocoef_dir.mkdir(parents=True)
fig.write_image(Path(aerocoef_dir, fig_name))
@@ -278,13 +286,25 @@ def show_results():
display_results(Path(results_dir, tab_name))
-st.title("Results")
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+
+ # Define interface
+ create_sidebar(HOW_TO_TEXT)
+
+ st.title("Results")
+
+ show_aeromap()
+ show_results()
-show_aeromap()
-show_results()
+ if st.button("🔄 Refresh"):
+ st.rerun()
-if st.button("🔄 Refresh"):
- st.experimental_rerun()
+ if st.checkbox("Auto refresh"):
+ st_autorefresh(interval=2000, limit=10000, key="auto_refresh")
-if st.checkbox("Auto refresh"):
- st_autorefresh(interval=2000, limit=10000, key="auto_refresh")
+ # Update last_page
+ st.session_state.last_page = PAGE_NAME
diff --git a/src/streamlit/runworkflow.py b/src/streamlit/runworkflow.py
index a9c3068a7..034d0373c 100644
--- a/src/streamlit/runworkflow.py
+++ b/src/streamlit/runworkflow.py
@@ -1,10 +1,32 @@
+"""
+CEASIOMpy: Conceptual Aircraft Design Software
+
+Developed for CFS ENGINEERING, 1015 Lausanne, Switzerland
+
+Initialize CEASIOMpy's workflow.
+
+| Author : Leon Deligny
+| Creation: 2025-Mar-04
+
+"""
+
+# =================================================================================================
+# IMPORTS
+# =================================================================================================
+
import sys
+
from pathlib import Path
from ceasiompy.utils.workflowclasses import Workflow
-config = sys.argv[1]
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+if __name__ == "__main__":
+ config = sys.argv[1]
-workflow = Workflow()
-workflow.from_config_file(Path(config))
-workflow.set_workflow()
-workflow.run_workflow()
+ workflow = Workflow()
+ workflow.from_config_file(Path(config))
+ workflow.set_workflow()
+ workflow.run_workflow()
diff --git a/src/streamlit/streamlitutils.py b/src/streamlit/streamlitutils.py
index 39ddd6376..2c826cab1 100644
--- a/src/streamlit/streamlitutils.py
+++ b/src/streamlit/streamlitutils.py
@@ -5,21 +5,93 @@
Streamlit utils functions for CEASIOMpy
-Python version: >=3.8
| Author : Aidan Jungo
| Creation: 2022-12-01
-TODO:
-
"""
+# ==============================================================================
+# IMPORTS
+# ==============================================================================
+
+import pandas as pd
+import streamlit as st
+
+from cpacspy.cpacsfunctions import (
+ add_string_vector,
+ add_value,
+)
+
+from PIL import Image
from pathlib import Path
+from cpacspy.cpacspy import CPACS
+from ceasiompy import log
from ceasiompy.utils.commonpaths import CEASIOMPY_LOGO_PATH
-from PIL import Image
-import streamlit as st
+# ==============================================================================
+# FUNCTIONS
+# ==============================================================================
+
+
+def update_value(xpath, key):
+ try:
+ if key in st.session_state:
+ value = st.session_state.get(key)
+
+ # Lists are different
+ if isinstance(value, list):
+ # Check if list is empty
+ if value:
+ add_string_vector(st.session_state.cpacs.tixi, xpath, value)
+ return
+ else:
+ add_string_vector(st.session_state.cpacs.tixi, xpath, [""])
+ return
+ else:
+ # Otherwise just add value
+ add_value(st.session_state.cpacs.tixi, xpath, value)
+ except Exception:
+ donothing = "DoNothing"
+ donothing += ""
+
+
+def update_all_modified_value():
+ if "xpath_to_update" not in st.session_state:
+ print("No xpath_to_update in st.session_state. Initializing it to an empty dictionary.")
+ elif st.session_state.xpath_to_update == {}:
+ print("\n Empty st.session_state.xpath_to_update \n")
+ else:
+ for xpath, key in st.session_state.xpath_to_update.items():
+ update_value(xpath, key)
+
+
+def get_last_workflow():
+ """Get the last workflow of the working directory"""
+
+ if "workflow" not in st.session_state:
+ st.warning("No workflow to show the result yet.")
+ return
+
+ last_workflow_nb = 0
+
+ for dir_ in Path(st.session_state.workflow.working_dir).iterdir():
+ if "Workflow_" in str(dir_):
+ last_workflow_nb = max(last_workflow_nb, int(str(dir_).split("_")[-1]))
+
+ if last_workflow_nb == 0:
+ return None
+
+ return Path(st.session_state.workflow.working_dir, f"Workflow_{last_workflow_nb:03}")
+
+
+def save_cpacs_file():
+ update_all_modified_value()
+ saved_cpacs_file = Path(st.session_state.workflow.working_dir, "CPACS_selected_from_GUI.xml")
+ st.session_state.cpacs.save_cpacs(saved_cpacs_file, overwrite=True)
+ st.session_state.workflow.cpacs_in = saved_cpacs_file
+ st.session_state.cpacs = CPACS(saved_cpacs_file)
def create_sidebar(how_to_text):
@@ -43,15 +115,16 @@ def st_directory_picker(initial_path=Path()):
manual_input = Path(manual_input)
if manual_input != st.session_state.path:
st.session_state.path = manual_input
- st.experimental_rerun()
+ st.rerun()
_, col1, col2, col3, _ = st.columns([3, 1, 3, 1, 3])
with col1:
- st.markdown("#")
+ st.markdown("", unsafe_allow_html=True)
if st.button("⬅️") and "path" in st.session_state:
st.session_state.path = st.session_state.path.parent
- st.experimental_rerun()
+ st.rerun()
+ st.markdown("
", unsafe_allow_html=True)
with col2:
subdirectroies = [
@@ -62,14 +135,119 @@ def st_directory_picker(initial_path=Path()):
if subdirectroies:
st.session_state.new_dir = st.selectbox("Subdirectories", sorted(subdirectroies))
else:
- st.markdown("#")
+ st.markdown("", unsafe_allow_html=True)
st.markdown("No subdir ", unsafe_allow_html=True)
+ st.markdown("
", unsafe_allow_html=True)
with col3:
if subdirectroies:
- st.markdown("#")
+ st.markdown("", unsafe_allow_html=True)
if st.button("➡️") and "path" in st.session_state:
st.session_state.path = Path(st.session_state.path, st.session_state.new_dir)
- st.experimental_rerun()
+ st.rerun()
+ st.markdown("
", unsafe_allow_html=True)
return st.session_state.path
+
+
+def section_edit_aeromap():
+ st.markdown("#### Available aeromaps")
+
+ for i, aeromap in enumerate(st.session_state.cpacs.get_aeromap_uid_list()):
+ col1, col2, col3, _ = st.columns([6, 1, 1, 5])
+
+ with col1:
+ st.markdown(f"**{aeromap}**")
+
+ with col2:
+ if st.button("⬆️", key=f"export{i}", help="Export aeromap") and i != 0:
+ csv_file_name = aeromap.replace(" ", "_") + ".csv"
+ st.session_state.cpacs.get_aeromap_by_uid(aeromap).export_csv(csv_file_name)
+ with open(csv_file_name) as f:
+ st.download_button("Download", f, file_name=csv_file_name)
+
+ with col3:
+ if st.button("❌", key=f"del{i}", help="Delete this aeromap"):
+ st.session_state.cpacs.delete_aeromap(aeromap)
+ st.rerun()
+
+ st.markdown("#### Add a point")
+
+ selected_aeromap = st.selectbox(
+ "in",
+ st.session_state.cpacs.get_aeromap_uid_list(),
+ help="Choose in which aeromap you want to add the point"
+ )
+
+ col1, col2, col3, col4, col5 = st.columns([2, 2, 2, 2, 1])
+
+ with col1:
+ alt = st.number_input("Alt", value=1000, min_value=0, step=100)
+ with col2:
+ mach = st.number_input("Mach", value=0.3, min_value=0.0, step=0.1)
+ with col3:
+ aoa = st.number_input("AoA", value=0, min_value=-90, max_value=90, step=1)
+ with col4:
+ aos = st.number_input("AoS", value=0, min_value=-90, max_value=90, step=1)
+ with col5:
+ st.markdown("")
+ st.markdown("")
+ if st.button("➕"):
+ st.session_state.cpacs.get_aeromap_by_uid(
+ selected_aeromap).add_row(mach=mach, alt=alt, aos=aos, aoa=aoa)
+ st.session_state.cpacs.get_aeromap_by_uid(selected_aeromap).save()
+ save_cpacs_file()
+
+ if selected_aeromap:
+ st.dataframe(st.session_state.cpacs.get_aeromap_by_uid(selected_aeromap).df)
+
+ st.markdown("#### Create new aeromap")
+
+ form = st.form("create_new_aeromap_form")
+
+ help_aeromap_uid = "The aeromap will contain 1 point corresponding to the value above, then \
+ you can add more point to it."
+ new_aeromap_uid = form.text_input(
+ "Aeromap uid",
+ help=help_aeromap_uid,
+ )
+ default_description = "Created with CEASIOMpy Graphical user interface"
+ new_aeromap_description = form.text_input(
+ "Aeromap description", value=default_description, help="optional"
+ )
+
+ if form.form_submit_button("Create new"):
+ if new_aeromap_uid not in st.session_state.cpacs.get_aeromap_uid_list():
+ new_aeromap = st.session_state.cpacs.create_aeromap(new_aeromap_uid)
+ new_aeromap.description = new_aeromap_description
+ new_aeromap.add_row(mach=mach, alt=alt, aos=aos, aoa=aoa)
+ new_aeromap.save()
+ log.info(f"Creating new aeromap {new_aeromap_uid}.")
+ st.rerun()
+ else:
+ st.error("There is already an aeromap with this name!")
+
+ st.markdown("#### Import aeromap from CSV")
+
+ uploaded_csv = st.file_uploader("Choose a CSV file")
+ if uploaded_csv:
+ uploaded_aeromap_uid = uploaded_csv.name.split(".csv")[0]
+
+ if st.button("Add this aeromap"):
+ if uploaded_aeromap_uid in st.session_state.cpacs.get_aeromap_uid_list():
+ st.error("There is already an aeromap with this name!")
+ return
+
+ new_aeromap = st.session_state.cpacs.create_aeromap(uploaded_aeromap_uid)
+ new_aeromap.df = pd.read_csv(uploaded_csv, keep_default_na=False)
+ new_aeromap.save()
+ st.rerun()
+
+
+# =================================================================================================
+# MAIN
+# =================================================================================================
+
+
+if __name__ == "__main__":
+ log.info("Nothing to execute!")
diff --git a/test_cases/test_case_1/README.md b/test_cases/test_case_1/README.md
index f23e83d34..cb336bbc7 100644
--- a/test_cases/test_case_1/README.md
+++ b/test_cases/test_case_1/README.md
@@ -23,12 +23,12 @@ conda activate ceasiompy
ceasiompy_run --testcase 1
```
-This workflow will take as input the CPACS file of the aircraft (D150_simple.xml), it will run the PyTornado module, and it will export the aerodynamic coefficients in a CSV file.
+This workflow will take as input the CPACS file of the aircraft (D150_simple.xml), it will run the PyAVL module, and it will export the aerodynamic coefficients in a CSV file.
```mermaid
graph LR;
- D150([D150 CPACS file])-->PyTornado;
- PyTornado-->ExportCSV;
+ D150([D150 CPACS file])-->PyAVL;
+ PyAVL-->ExportCSV;
```
You can find the exported CSV file in the results dir: `/Workflow_001/Results/Aeromaps/test_apm.csv` and it should look like this:
@@ -41,7 +41,7 @@ altitude,machNumber,angleOfSideslip,angleOfAttack,cd,cl,cs,cmd,cml,cms
0,0.3,10,10,0.0545719,1.08554,-0.100854,0.249796,-0.84655,-4.65115
```
-You can see than PyTornado ran four calculations with different angles of attack and sideslip, but at the same altitude and Mach number. On the next test case, you will learn how to define your own flight parameters.
+You can see than PyAVL ran four calculations with different angles of attack and sideslip, but at the same altitude and Mach number. On the next test case, you will learn how to define your own flight parameters.
These results have also been written in the last output CPACS file that you can find at : `/Workflow_001/02_ExportCSV/ToolOutput.xml`. It is a big file, because it contains all the geometric description of the aircraft. However, if you open it in a text editor and look for `test_apm`, you should find the following:
@@ -66,6 +66,6 @@ These results have also been written in the last output CPACS file that you can
```
-You can see that parameters `altitude`, `machNumber`, `angleOfSideslip`, `angleOfAttack` and coefficients `cl`, `cd`, `cs`, `cml`, `cms` and `cmd` are defined as lists of four values, representing the four calculations made by PyTornado.
+You can see that parameters `altitude`, `machNumber`, `angleOfSideslip`, `angleOfAttack` and coefficients `cl`, `cd`, `cs`, `cml`, `cms` and `cmd` are defined as lists of four values, representing the four calculations made by PyAVL.
| [Home](../../README.md#test-cases) | Next test case [**>>**](../test_case_2/README.md)
diff --git a/test_cases/test_case_1/testcase1.cfg b/test_cases/test_case_1/testcase1.cfg
index f68359c94..b33f2c17a 100644
--- a/test_cases/test_case_1/testcase1.cfg
+++ b/test_cases/test_case_1/testcase1.cfg
@@ -3,4 +3,4 @@
% Test Case 1
%--------------------------------------------------------------------------
CPACS_TOOLINPUT = ../../test_files/CPACSfiles/D150_simple.xml
-MODULE_TO_RUN = ( PyTornado, ExportCSV )
+MODULE_TO_RUN = ( PyAVL, ExportCSV )
diff --git a/test_cases/test_case_4/README.md b/test_cases/test_case_4/README.md
index d1e53221b..f53be6c79 100644
--- a/test_cases/test_case_4/README.md
+++ b/test_cases/test_case_4/README.md
@@ -16,15 +16,15 @@ conda activate ceasiompy
cd WKDIR
# To run the test case 4
-ceasiompy_run -m ../test_files/CPACSfiles/D150_simple.xml PyTornado SkinFriction SaveAeroCoefficients
+ceasiompy_run -m ../test_files/CPACSfiles/D150_simple.xml PyAVL SkinFriction SaveAeroCoefficients
```
The following workflow will be executed:
```mermaid
graph LR;
- D150([D150 CPACS file])-->PyTornado;
- PyTornado-->SkinFriction;
+ D150([D150 CPACS file])-->PyAVL;
+ PyAVL-->SkinFriction;
SkinFriction-->SaveAeroCoefficients:
```
diff --git a/test_cases/test_case_5/README.md b/test_cases/test_case_5/README.md
index 1624de813..cb52a7bf1 100644
--- a/test_cases/test_case_5/README.md
+++ b/test_cases/test_case_5/README.md
@@ -11,7 +11,7 @@ For the test case you must first create and save a file named `my_ceasiompy_conf
```text
% This is an example of config file for ceasiompy
CPACS_TOOLINPUT = ../test_files/CPACSfiles/D150_simple.xml
-MODULE_TO_RUN = ( PyTornado, SkinFriction, SaveAeroCoefficients )
+MODULE_TO_RUN = ( PyAVL, SkinFriction, SaveAeroCoefficients )
```
You can now run it with the following command:
diff --git a/test_files/CEASIOMpy_config_template/CEASIOMpy_config_template.cfg b/test_files/CEASIOMpy_config_template/CEASIOMpy_config_template.cfg
index 7f0b4d733..07f5f7d47 100644
--- a/test_files/CEASIOMpy_config_template/CEASIOMpy_config_template.cfg
+++ b/test_files/CEASIOMpy_config_template/CEASIOMpy_config_template.cfg
@@ -42,7 +42,7 @@ CPACS_TOOLOUTPUT = ./ToolOutput.xml
%
% Aerodynamics
% CLCalculator
-% PyTornado
+% PyAVL
% SU2MeshDef
% SU2Run
% SkinFriction
@@ -56,8 +56,8 @@ CPACS_TOOLOUTPUT = ./ToolOutput.xml
% SMTrain
% SMUse
%
-MODULE_PRE = (PyTornado)
-MODULE_OPTIM = (PyTornado)
+MODULE_PRE = (PyAVL)
+MODULE_OPTIM = (PyAVL)
MODULE_POST = (SaveAeroCoefficients)
%
% ------------------------ OPTIMISATION OPTIONS -------------------------------%
diff --git a/test_files/CEASIOMpy_config_template/CEASIOMpy_optim_gui_pyTornado.cfg b/test_files/CEASIOMpy_config_template/CEASIOMpy_optim_gui_pyAVL.cfg
similarity index 97%
rename from test_files/CEASIOMpy_config_template/CEASIOMpy_optim_gui_pyTornado.cfg
rename to test_files/CEASIOMpy_config_template/CEASIOMpy_optim_gui_pyAVL.cfg
index fe4d5a0a5..61341193e 100644
--- a/test_files/CEASIOMpy_config_template/CEASIOMpy_optim_gui_pyTornado.cfg
+++ b/test_files/CEASIOMpy_config_template/CEASIOMpy_optim_gui_pyAVL.cfg
@@ -36,7 +36,7 @@ CPACS_TOOLINPUT = ./ToolInput/ToolInput.xml
%
% Aerodynamics
% CLCalculator
-% PyTornado
+% PyAVL
% SU2MeshDef
% SU2Run
% SkinFriction
@@ -51,7 +51,7 @@ CPACS_TOOLINPUT = ./ToolInput/ToolInput.xml
% SMUse
%
MODULE_PRE = NONE
-MODULE_OPTIM = (PyTornado)
+MODULE_OPTIM = (PyAVL)
MODULE_POST = NONE
%
% ------------------------ OPTIMISATION OPTIONS -------------------------------%
diff --git a/test_files/CEASIOMpy_config_template/CEASIOMpy_simple_gui_pyTornado.cfg b/test_files/CEASIOMpy_config_template/CEASIOMpy_simple_gui_pyAVL.cfg
similarity index 97%
rename from test_files/CEASIOMpy_config_template/CEASIOMpy_simple_gui_pyTornado.cfg
rename to test_files/CEASIOMpy_config_template/CEASIOMpy_simple_gui_pyAVL.cfg
index c853277c4..39ac7e563 100644
--- a/test_files/CEASIOMpy_config_template/CEASIOMpy_simple_gui_pyTornado.cfg
+++ b/test_files/CEASIOMpy_config_template/CEASIOMpy_simple_gui_pyAVL.cfg
@@ -36,7 +36,7 @@ CPACS_TOOLINPUT = ./ToolInput/ToolInput.xml
%
% Aerodynamics
% CLCalculator
-% PyTornado
+% PyAVL
% SU2MeshDef
% SU2Run
% SkinFriction
@@ -50,7 +50,7 @@ CPACS_TOOLINPUT = ./ToolInput/ToolInput.xml
% SMTrain
% SMUse
%
-MODULE_PRE = ( PyTornado )
+MODULE_PRE = ( PyAVL )
MODULE_OPTIM = None
MODULE_POST = None
%
diff --git a/test_files/CEASIOMpy_config_template/CEASIOMpy_simple_gui_su2.cfg b/test_files/CEASIOMpy_config_template/CEASIOMpy_simple_gui_su2.cfg
index bae911697..0ee5b4b70 100644
--- a/test_files/CEASIOMpy_config_template/CEASIOMpy_simple_gui_su2.cfg
+++ b/test_files/CEASIOMpy_config_template/CEASIOMpy_simple_gui_su2.cfg
@@ -36,7 +36,7 @@ CPACS_TOOLINPUT = ./ToolInput/ToolInput.xml
%
% Aerodynamics
% CLCalculator
-% PyTornado
+% PyAVL
% SU2MeshDef
% SU2Run
% SkinFriction
diff --git a/test_files/CEASIOMpy_config_template/CEASIOMpy_workflow_default.cfg b/test_files/CEASIOMpy_config_template/CEASIOMpy_workflow_default.cfg
index 0fe9977b0..5e88c66f3 100644
--- a/test_files/CEASIOMpy_config_template/CEASIOMpy_workflow_default.cfg
+++ b/test_files/CEASIOMpy_config_template/CEASIOMpy_workflow_default.cfg
@@ -36,7 +36,7 @@ CPACS_TOOLINPUT = ./ToolInput/ToolInput.xml
%
% Aerodynamics
% CLCalculator
-% PyTornado
+% PyAVL
% SU2MeshDef
% SU2Run
% SkinFriction
@@ -50,7 +50,7 @@ CPACS_TOOLINPUT = ./ToolInput/ToolInput.xml
% SMTrain
% SMUse
%
-MODULE_PRE = (PyTornado)
+MODULE_PRE = (PyAVL)
MODULE_OPTIM = NONE
MODULE_POST = NONE
%
diff --git a/test_files/CEASIOMpy_config_template/CEASIOMpy_workflow_test.cfg b/test_files/CEASIOMpy_config_template/CEASIOMpy_workflow_test.cfg
index 0fe9977b0..5e88c66f3 100644
--- a/test_files/CEASIOMpy_config_template/CEASIOMpy_workflow_test.cfg
+++ b/test_files/CEASIOMpy_config_template/CEASIOMpy_workflow_test.cfg
@@ -36,7 +36,7 @@ CPACS_TOOLINPUT = ./ToolInput/ToolInput.xml
%
% Aerodynamics
% CLCalculator
-% PyTornado
+% PyAVL
% SU2MeshDef
% SU2Run
% SkinFriction
@@ -50,7 +50,7 @@ CPACS_TOOLINPUT = ./ToolInput/ToolInput.xml
% SMTrain
% SMUse
%
-MODULE_PRE = (PyTornado)
+MODULE_PRE = (PyAVL)
MODULE_OPTIM = NONE
MODULE_POST = NONE
%
diff --git a/test_files/CPACSfiles/D150_simple.xml b/test_files/CPACSfiles/D150_simple.xml
index d30ba3ef8..4010f8cc4 100644
--- a/test_files/CPACSfiles/D150_simple.xml
+++ b/test_files/CPACSfiles/D150_simple.xml
@@ -3,48 +3,59 @@
xsi_noNamespaceSchemaLocation="CPACS_21_Schema.xsd">
D150
- D150 geometry
- Pier Davide Ciampa
- 2017-09-04T12:00:00
0.1
- 3.0
-
-
- Converted to cpacs 3.0 using cpacs2to3
+ 3.0
+
+
+ Converted to cpacs 3.0 using cpacs2to3
cpacs2to3
2019-01-31T16:03:44
1.1
3.0
-
-
- Fixed order of wing profiles.
+
+
+ Fixed order of wing profiles.
fix_errors.py
2019-04-29T20:48:14
1.2.0
3.0
-
-
- Changed uID and names to more simplified ones
+
+
+ Changed uID and names to more simplified ones
Vivien Riolo
2020-02-13T20:48:14
3.1
3.0
-
-
- Added missing names
+
+
+ Added missing names
Vivien Riolo
2020-05-14T10:29:00
3.1
3.0
-
-
-
- Simplified version for CEASIOMpy
- Aidan Jungo
- 2020-06-15T10:29:00
- 3.1
- 3.0
-
+
+
+ Simplified version for CEASIOMpy
+ Aidan Jungo
+ 2020-06-15T10:29:00
+ 3.1
+ 3.0
+
+
+ D150 geometry
+ Pier Davide Ciampa
+ 2017-09-04T12:00:00
+ 3.2
+ 3.0
+
+
+ D150 geometry
+ Leon Deligny
+ 2025-09-04T12:00:00
+ 3.3
+ 3.0
+
+
@@ -3091,8 +3102,6 @@
0
0
Fuselage1_Sec1
-
-
pos2
@@ -4153,8 +4162,6 @@
0
0
Wing1_Sec1
-
-
p2
@@ -4254,6 +4261,7 @@
+ 0
0
0
@@ -4267,6 +4275,7 @@
0
+ 0
1
0
@@ -4327,6 +4336,7 @@
+ 0
0
0
@@ -4340,6 +4350,7 @@
0
+ 0
1
0
@@ -4400,6 +4411,7 @@
+ 0
-1
0
@@ -4413,6 +4425,7 @@
-25
+ 0
0
0
@@ -4426,6 +4439,7 @@
0
+ 0
1
0
@@ -4495,6 +4509,7 @@
+ 0
0
0.0
@@ -4508,6 +4523,7 @@
0
+ 0
1
0.0
@@ -4575,6 +4591,7 @@
+ 0
0
0.0
@@ -4588,6 +4605,7 @@
0
+ 0
1
0.0
@@ -4655,6 +4673,7 @@
+ 0
0
0.0
@@ -4668,6 +4687,7 @@
0
+ 0
1
0.0
@@ -4735,6 +4755,7 @@
+ 0
0
0.0
@@ -4748,6 +4769,7 @@
0
+ 0
1
0.0
@@ -4815,6 +4837,7 @@
+ 0
0
0.0
@@ -4828,6 +4851,7 @@
0
+ 0
1
0.0
@@ -4994,8 +5018,6 @@
0
0
Wing2H_Sec1
-
-
w2p2
@@ -5067,6 +5089,7 @@
+ 0
-1
0
@@ -5080,6 +5103,7 @@
-15
+ 0
0
0
@@ -5093,6 +5117,7 @@
0
+ 0
1
0
@@ -5237,16 +5262,14 @@
-
+ Wing3V_Pos1
0
0
0
Wing3V_Sec1
-
-
-
+ Wing3V_Pos2
7.70151
40.3982
0
@@ -5315,6 +5338,7 @@
+ 0
-1
0
@@ -5328,6 +5352,7 @@
-30
+ 0
0
0
@@ -5341,6 +5366,7 @@
0
+ 0
1
0
@@ -5645,34 +5671,5 @@
test_apm
-
- 20
- 5
-
-
- False
- False
-
-
- False
- False
-
-
- False
- False
-
-
- False
- False
-
-
-
- False
- False
- False
- False
-
- test_apm
-
-
+
\ No newline at end of file
diff --git a/tests/Test_input.xml b/test_files/CPACSfiles/D150_simple_test_static.xml
similarity index 98%
rename from tests/Test_input.xml
rename to test_files/CPACSfiles/D150_simple_test_static.xml
index c4d779208..4c3e08e1d 100644
--- a/tests/Test_input.xml
+++ b/test_files/CPACSfiles/D150_simple_test_static.xml
@@ -3091,8 +3091,6 @@
0
0
Fuselage1_Sec1
-
-
pos2
@@ -4153,8 +4151,6 @@
0
0
Wing1_Sec1
-
-
p2
@@ -4994,8 +4990,6 @@
0
0
Wing2H_Sec1
-
-
w2p2
@@ -5132,7 +5126,7 @@
uID="Wing3V_transformation1_translation1">
29.838
-0.00200292
- 1.89101
+ 1.8
@@ -5237,16 +5231,14 @@
-
+ Wing3V_Pos1
0
0
0
Wing3V_Sec1
-
-
-
+ Wing3V_Pos2
7.70151
40.3982
0
@@ -5394,14 +5386,11 @@
ISA
- 0
+ 0.0
0.3
- 0
- 0
+ 0.0
+ 0.0
-
- ISA
-
test_apm
@@ -5414,6 +5403,12 @@
0.3;0.3;0.3;0.3
0;10;0;10
0;0;10;10
+ 0.002;0.002;0.004;0.002
+ 0.002;0.002;0.004;0.004
+ 0.004;0.002;0.004;0.002
+ nan;nan;nan;nan
+ nan;nan;nan;nan
+ nan;nan;nan;nan
@@ -5599,102 +5594,51 @@
- 702.04
+ 701.813
-
-
- test_apm
-
-
-
- 2
- 50
- 1
- 0
-
-
- Fuselage1;Wing3V;Wing2H;Wing1
-
-
- NO
- 1
- aeromap_empty
-
- False
- False
- -1
-
-
- False
-
-
-
-
- test_apm
-
- True
- True
- False
-
-
-
- test_apm
-
-
- False
- False
-
False
- 4
- True
+ False
+ 6.0
- 13
+ 25.0
- 1.0
+ 2.0
+ 0.349
- 1.0
+ 1.5
+ 0.161
+ 0.23
+ 0.23
- 1.6
+ 2.0
+ False
False
+ 20.0
+ 20.0
-
- 0.6
-
+
+
+ test_apm
+
+
+ False
+ aeromap_empty
+
+ None
+ None
+ None
+
+
+
+
+ test_apm
+
-
- 20
- 5
-
-
- False
- False
-
-
- False
- False
-
-
- False
- False
-
-
- False
- False
-
-
-
- False
- False
- False
- False
-
- test_apm
-
\ No newline at end of file
diff --git a/test_files/CPACSfiles/labARscaled.xml b/test_files/CPACSfiles/labARscaled.xml
index 847c8e6d4..107796e63 100644
--- a/test_files/CPACSfiles/labARscaled.xml
+++ b/test_files/CPACSfiles/labARscaled.xml
@@ -1,6 +1,5 @@
-
+
labARscaled
Simple Wing for unit testing
diff --git a/test_files/CPACSfiles/simple_propeller.xml b/test_files/CPACSfiles/simple_propeller.xml
index b0ac3b3c0..d3a1e7639 100644
--- a/test_files/CPACSfiles/simple_propeller.xml
+++ b/test_files/CPACSfiles/simple_propeller.xml
@@ -1,5 +1,5 @@
-
+
Cpacs2Test
Simple Wing for unit testing
@@ -333,7 +333,8 @@
0
0
-
+
0
0
0
@@ -354,7 +355,8 @@
0
0
-
+
0
0
0
@@ -376,7 +378,8 @@
0
0
-
+
0.5
0
0
@@ -397,7 +400,8 @@
0
0
-
+
0
0
0
@@ -419,7 +423,8 @@
0
0
-
+
0
0
0
@@ -440,7 +445,8 @@
0
0
-
+
0
0
0
@@ -518,7 +524,8 @@
0
0
-
+
0
0
0
@@ -540,7 +547,8 @@
0
0
-
+
0
0
0
@@ -563,7 +571,8 @@
0
0
-
+
0
0
0
@@ -585,7 +594,8 @@
0
0
-
+
0
0
0
@@ -608,7 +618,8 @@
0
0
-
+
0
0
0
@@ -630,7 +641,8 @@
0
0
-
+
0.5
0
0
@@ -668,7 +680,8 @@
Cpacs2Test_Wing_Sec3
- Autogenerated positioning to set the position of section: Cpacs2Test_Wing_Sec3After
+ Autogenerated positioning to set the position of section:
+ Cpacs2Test_Wing_Sec3After
3.12517
16.2707
0
@@ -677,14 +690,18 @@
- Fuselage Segment from Cpacs2Test - Wing Section 1 Main Element to Cpacs2Test - Wing Section 2 Main Element
- Fuselage Segment from Cpacs2Test - Wing Section 1 Main Element to Cpacs2Test - Wing Section 2 Main Element
+ Fuselage Segment from Cpacs2Test - Wing Section 1 Main Element to Cpacs2Test -
+ Wing Section 2 Main Element
+ Fuselage Segment from Cpacs2Test - Wing Section 1 Main Element to
+ Cpacs2Test - Wing Section 2 Main Element
Cpacs2Test_Wing_Sec1_El1
Cpacs2Test_Wing_Sec2_El1
- Fuselage Segment from Cpacs2Test - Wing Section 2 Main Element to Cpacs2Test - Wing Section 3 Main Element
- Fuselage Segment from Cpacs2Test - Wing Section 2 Main Element to Cpacs2Test - Wing Section 3 Main Element
+ Fuselage Segment from Cpacs2Test - Wing Section 2 Main Element to Cpacs2Test -
+ Wing Section 3 Main Element
+ Fuselage Segment from Cpacs2Test - Wing Section 2 Main Element to
+ Cpacs2Test - Wing Section 3 Main Element
Cpacs2Test_Wing_Sec2_El1
Cpacs2Test_Wing_Sec3_El1
@@ -756,13 +773,13 @@
Wing
-
-
+
+
90
0
-90
-
+
-0.1
2
0
@@ -771,8 +788,8 @@
PylonR section 1
-
-
+
+
0
0
0
@@ -782,7 +799,7 @@
PylonR section 1 element 1
PylonRProfile
-
+
@@ -799,8 +816,8 @@
PylonR section 2 element 1
PylonRProfile
-
-
+
+
1
1
1
@@ -811,8 +828,8 @@
PylonR Section 3
-
-
+
+
0.02
0.02
0.02
@@ -822,8 +839,8 @@
PylonR section 3 element 1
PylonRProfile
-
-
+
+
1
1
1
@@ -880,25 +897,33 @@
NACA0.00.00.12
NACA 4 Series Profile
- 1;0.9875;0.975;0.9625;0.95;0.9375;0.925;0.9125;0.9;0.8875;0.875;0.8625;0.85;0.8375;0.825;0.8125;0.8;0.7875;0.775;0.7625;0.75;0.7375;0.725;0.7125;0.7;0.6875;0.675;0.6625;0.65;0.6375;0.625;0.6125;0.6;0.5875;0.575;0.5625;0.55;0.5375;0.525;0.5125;0.5;0.4875;0.475;0.4625;0.45;0.4375;0.425;0.4125;0.4;0.3875;0.375;0.3625;0.35;0.3375;0.325;0.3125;0.3;0.2875;0.275;0.2625;0.25;0.2375;0.225;0.2125;0.2;0.1875;0.175;0.1625;0.15;0.1375;0.125;0.1125;0.1;0.0875;0.075;0.0625;0.05;0.0375;0.025;0.0125;0;0.0125;0.025;0.0375;0.05;0.0625;0.075;0.0875;0.1;0.1125;0.125;0.1375;0.15;0.1625;0.175;0.1875;0.2;0.2125;0.225;0.2375;0.25;0.2625;0.275;0.2875;0.3;0.3125;0.325;0.3375;0.35;0.3625;0.375;0.3875;0.4;0.4125;0.425;0.4375;0.45;0.4625;0.475;0.4875;0.5;0.5125;0.525;0.5375;0.55;0.5625;0.575;0.5875;0.6;0.6125;0.625;0.6375;0.65;0.6625;0.675;0.6875;0.7;0.7125;0.725;0.7375;0.75;0.7625;0.775;0.7875;0.8;0.8125;0.825;0.8375;0.85;0.8625;0.875;0.8875;0.9;0.9125;0.925;0.9375;0.95;0.9625;0.975;0.9875;1
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- -0.00126;-0.00300042;-0.00471439;-0.00640257;-0.00806559;-0.00970404;-0.0113185;-0.0129093;-0.0144772;-0.0160224;-0.0175453;-0.0190463;-0.0205256;-0.0219836;-0.0234204;-0.0248362;-0.0262312;-0.0276053;-0.0289587;-0.0302913;-0.0316031;-0.0328939;-0.0341636;-0.0354121;-0.0366391;-0.0378442;-0.0390272;-0.0401876;-0.041325;-0.0424389;-0.0435287;-0.0445939;-0.0456337;-0.0466475;-0.0476344;-0.0485936;-0.0495243;-0.0504253;-0.0512958;-0.0521345;-0.0529403;-0.0537118;-0.0544478;-0.0551469;-0.0558074;-0.0564277;-0.0570062;-0.057541;-0.0580301;-0.0584715;-0.0588629;-0.059202;-0.0594862;-0.0597128;-0.059879;-0.0599816;-0.0600173;-0.0599823;-0.0598728;-0.0596844;-0.0594124;-0.0590516;-0.0585963;-0.05804;-0.0573754;-0.0565945;-0.0556877;-0.0546444;-0.0534516;-0.0520943;-0.050554;-0.0488081;-0.0468277;-0.0445751;-0.041999;-0.0390267;-0.0355469;-0.0313739;-0.0261472;-0.018939;0;0.018939;0.0261472;0.0313739;0.0355469;0.0390267;0.041999;0.0445751;0.0468277;0.0488081;0.050554;0.0520943;0.0534516;0.0546444;0.0556877;0.0565945;0.0573754;0.05804;0.0585963;0.0590516;0.0594124;0.0596844;0.0598728;0.0599823;0.0600173;0.0599816;0.059879;0.0597128;0.0594862;0.059202;0.0588629;0.0584715;0.0580301;0.057541;0.0570062;0.0564277;0.0558074;0.0551469;0.0544478;0.0537118;0.0529403;0.0521345;0.0512958;0.0504253;0.0495243;0.0485936;0.0476344;0.0466475;0.0456337;0.0445939;0.0435287;0.0424389;0.041325;0.0401876;0.0390272;0.0378442;0.0366391;0.0354121;0.0341636;0.0328939;0.0316031;0.0302913;0.0289587;0.0276053;0.0262312;0.0248362;0.0234204;0.0219836;0.0205256;0.0190463;0.0175453;0.0160224;0.0144772;0.0129093;0.0113185;0.00970404;0.00806559;0.00640257;0.00471439;0.00300042;0.00126
+
+ 1;0.9875;0.975;0.9625;0.95;0.9375;0.925;0.9125;0.9;0.8875;0.875;0.8625;0.85;0.8375;0.825;0.8125;0.8;0.7875;0.775;0.7625;0.75;0.7375;0.725;0.7125;0.7;0.6875;0.675;0.6625;0.65;0.6375;0.625;0.6125;0.6;0.5875;0.575;0.5625;0.55;0.5375;0.525;0.5125;0.5;0.4875;0.475;0.4625;0.45;0.4375;0.425;0.4125;0.4;0.3875;0.375;0.3625;0.35;0.3375;0.325;0.3125;0.3;0.2875;0.275;0.2625;0.25;0.2375;0.225;0.2125;0.2;0.1875;0.175;0.1625;0.15;0.1375;0.125;0.1125;0.1;0.0875;0.075;0.0625;0.05;0.0375;0.025;0.0125;0;0.0125;0.025;0.0375;0.05;0.0625;0.075;0.0875;0.1;0.1125;0.125;0.1375;0.15;0.1625;0.175;0.1875;0.2;0.2125;0.225;0.2375;0.25;0.2625;0.275;0.2875;0.3;0.3125;0.325;0.3375;0.35;0.3625;0.375;0.3875;0.4;0.4125;0.425;0.4375;0.45;0.4625;0.475;0.4875;0.5;0.5125;0.525;0.5375;0.55;0.5625;0.575;0.5875;0.6;0.6125;0.625;0.6375;0.65;0.6625;0.675;0.6875;0.7;0.7125;0.725;0.7375;0.75;0.7625;0.775;0.7875;0.8;0.8125;0.825;0.8375;0.85;0.8625;0.875;0.8875;0.9;0.9125;0.925;0.9375;0.95;0.9625;0.975;0.9875;1
+
+ 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
+
+ -0.00126;-0.00300042;-0.00471439;-0.00640257;-0.00806559;-0.00970404;-0.0113185;-0.0129093;-0.0144772;-0.0160224;-0.0175453;-0.0190463;-0.0205256;-0.0219836;-0.0234204;-0.0248362;-0.0262312;-0.0276053;-0.0289587;-0.0302913;-0.0316031;-0.0328939;-0.0341636;-0.0354121;-0.0366391;-0.0378442;-0.0390272;-0.0401876;-0.041325;-0.0424389;-0.0435287;-0.0445939;-0.0456337;-0.0466475;-0.0476344;-0.0485936;-0.0495243;-0.0504253;-0.0512958;-0.0521345;-0.0529403;-0.0537118;-0.0544478;-0.0551469;-0.0558074;-0.0564277;-0.0570062;-0.057541;-0.0580301;-0.0584715;-0.0588629;-0.059202;-0.0594862;-0.0597128;-0.059879;-0.0599816;-0.0600173;-0.0599823;-0.0598728;-0.0596844;-0.0594124;-0.0590516;-0.0585963;-0.05804;-0.0573754;-0.0565945;-0.0556877;-0.0546444;-0.0534516;-0.0520943;-0.050554;-0.0488081;-0.0468277;-0.0445751;-0.041999;-0.0390267;-0.0355469;-0.0313739;-0.0261472;-0.018939;0;0.018939;0.0261472;0.0313739;0.0355469;0.0390267;0.041999;0.0445751;0.0468277;0.0488081;0.050554;0.0520943;0.0534516;0.0546444;0.0556877;0.0565945;0.0573754;0.05804;0.0585963;0.0590516;0.0594124;0.0596844;0.0598728;0.0599823;0.0600173;0.0599816;0.059879;0.0597128;0.0594862;0.059202;0.0588629;0.0584715;0.0580301;0.057541;0.0570062;0.0564277;0.0558074;0.0551469;0.0544478;0.0537118;0.0529403;0.0521345;0.0512958;0.0504253;0.0495243;0.0485936;0.0476344;0.0466475;0.0456337;0.0445939;0.0435287;0.0424389;0.041325;0.0401876;0.0390272;0.0378442;0.0366391;0.0354121;0.0341636;0.0328939;0.0316031;0.0302913;0.0289587;0.0276053;0.0262312;0.0248362;0.0234204;0.0219836;0.0205256;0.0190463;0.0175453;0.0160224;0.0144772;0.0129093;0.0113185;0.00970404;0.00806559;0.00640257;0.00471439;0.00300042;0.00126
NACA0006
- 1;0.998459;0.993844;0.986185;0.975528;0.96194;0.945503;0.92632;0.904508;0.880203;0.853553;0.824724;0.793893;0.761249;0.726995;0.691342;0.654508;0.616723;0.578217;0.53923;0.5;0.46077;0.421783;0.383277;0.345492;0.308658;0.273005;0.238751;0.206107;0.175276;0.146447;0.119797;0.095492;0.07368;0.054497;0.03806;0.024472;0.013815;0.006156;0.001541;0;0.001541;0.006156;0.013815;0.024472;0.03806;0.054497;0.07368;0.095492;0.119797;0.146447;0.175276;0.206107;0.238751;0.273005;0.308658;0.345492;0.383277;0.421783;0.46077;0.5;0.53923;0.578217;0.616723;0.654508;0.691342;0.726995;0.761249;0.793893;0.824724;0.853553;0.880203;0.904508;0.92632;0.945503;0.96194;0.975528;0.986185;0.993844;0.998459;1
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;-0.000112;-0.000446;-0.000995;-0.001751;-0.002699;-0.003825;-0.005111;-0.006535;-0.008079;-0.009719;-0.011434;-0.013202;-0.015;-0.016805;-0.018594;-0.020343;-0.022027;-0.023621;-0.025098;-0.026431;-0.027592;-0.028554;-0.029291;-0.029778;-0.029994;-0.029921;-0.029544;-0.028856;-0.027854;-0.026541;-0.024927;-0.023024;-0.020853;-0.018433;-0.01579;-0.012947;-0.009927;-0.006752;-0.003438;0;0.003438;0.006752;0.009927;0.012947;0.01579;0.018433;0.020853;0.023024;0.024927;0.026541;0.027854;0.028856;0.029544;0.029921;0.029994;0.029778;0.029291;0.028554;0.027592;0.026431;0.025098;0.023621;0.022027;0.020343;0.018594;0.016805;0.015;0.013202;0.011434;0.009719;0.008079;0.006535;0.005111;0.003825;0.002699;0.001751;0.000995;0.000446;0.000112;0
+
+ 1;0.998459;0.993844;0.986185;0.975528;0.96194;0.945503;0.92632;0.904508;0.880203;0.853553;0.824724;0.793893;0.761249;0.726995;0.691342;0.654508;0.616723;0.578217;0.53923;0.5;0.46077;0.421783;0.383277;0.345492;0.308658;0.273005;0.238751;0.206107;0.175276;0.146447;0.119797;0.095492;0.07368;0.054497;0.03806;0.024472;0.013815;0.006156;0.001541;0;0.001541;0.006156;0.013815;0.024472;0.03806;0.054497;0.07368;0.095492;0.119797;0.146447;0.175276;0.206107;0.238751;0.273005;0.308658;0.345492;0.383277;0.421783;0.46077;0.5;0.53923;0.578217;0.616723;0.654508;0.691342;0.726995;0.761249;0.793893;0.824724;0.853553;0.880203;0.904508;0.92632;0.945503;0.96194;0.975528;0.986185;0.993844;0.998459;1
+
+ 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
+
+ 0;-0.000112;-0.000446;-0.000995;-0.001751;-0.002699;-0.003825;-0.005111;-0.006535;-0.008079;-0.009719;-0.011434;-0.013202;-0.015;-0.016805;-0.018594;-0.020343;-0.022027;-0.023621;-0.025098;-0.026431;-0.027592;-0.028554;-0.029291;-0.029778;-0.029994;-0.029921;-0.029544;-0.028856;-0.027854;-0.026541;-0.024927;-0.023024;-0.020853;-0.018433;-0.01579;-0.012947;-0.009927;-0.006752;-0.003438;0;0.003438;0.006752;0.009927;0.012947;0.01579;0.018433;0.020853;0.023024;0.024927;0.026541;0.027854;0.028856;0.029544;0.029921;0.029994;0.029778;0.029291;0.028554;0.027592;0.026431;0.025098;0.023621;0.022027;0.020343;0.018594;0.016805;0.015;0.013202;0.011434;0.009719;0.008079;0.006535;0.005111;0.003825;0.002699;0.001751;0.000995;0.000446;0.000112;0
Engine pylonR circle profile
- 0.5;0.4;0.3;0.2;0.1;0;-0.1;-0.2;-0.3;-0.4;-0.5;-0.4;-0.3;-0.2;-0.1;0;0.1;0.2;0.3;0.4;0.5
+
+ 0.5;0.4;0.3;0.2;0.1;0;-0.1;-0.2;-0.3;-0.4;-0.5;-0.4;-0.3;-0.2;-0.1;0;0.1;0.2;0.3;0.4;0.5
0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- 0;-0.3;-0.4;-0.458258;-0.489898;-0.5;-0.489898;-0.458258;-0.4;-0.3;0;0.3;0.4;0.458258;0.489898;0.5;0.489898;0.458258;0.4;0.3;0
+
+ 0;-0.3;-0.4;-0.458258;-0.489898;-0.5;-0.489898;-0.458258;-0.4;-0.3;0;0.3;0.4;0.458258;0.489898;0.5;0.489898;0.458258;0.4;0.3;0
@@ -918,9 +943,12 @@
NACA0.00.00.10
NACA 4 Series Profile
- 1;0.9875;0.975;0.9625;0.95;0.9375;0.925;0.9125;0.9;0.8875;0.875;0.8625;0.85;0.8375;0.825;0.8125;0.8;0.7875;0.775;0.7625;0.75;0.7375;0.725;0.7125;0.7;0.6875;0.675;0.6625;0.65;0.6375;0.625;0.6125;0.6;0.5875;0.575;0.5625;0.55;0.5375;0.525;0.5125;0.5;0.4875;0.475;0.4625;0.45;0.4375;0.425;0.4125;0.4;0.3875;0.375;0.3625;0.35;0.3375;0.325;0.3125;0.3;0.2875;0.275;0.2625;0.25;0.2375;0.225;0.2125;0.2;0.1875;0.175;0.1625;0.15;0.1375;0.125;0.1125;0.1;0.0875;0.075;0.0625;0.05;0.0375;0.025;0.0125;0;0.0125;0.025;0.0375;0.05;0.0625;0.075;0.0875;0.1;0.1125;0.125;0.1375;0.15;0.1625;0.175;0.1875;0.2;0.2125;0.225;0.2375;0.25;0.2625;0.275;0.2875;0.3;0.3125;0.325;0.3375;0.35;0.3625;0.375;0.3875;0.4;0.4125;0.425;0.4375;0.45;0.4625;0.475;0.4875;0.5;0.5125;0.525;0.5375;0.55;0.5625;0.575;0.5875;0.6;0.6125;0.625;0.6375;0.65;0.6625;0.675;0.6875;0.7;0.7125;0.725;0.7375;0.75;0.7625;0.775;0.7875;0.8;0.8125;0.825;0.8375;0.85;0.8625;0.875;0.8875;0.9;0.9125;0.925;0.9375;0.95;0.9625;0.975;0.9875;1
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
- -0.00105;-0.00250035;-0.00392865;-0.00533547;-0.00672133;-0.0080867;-0.00943205;-0.0107578;-0.0120643;-0.013352;-0.0146211;-0.0158719;-0.0171047;-0.0183197;-0.019517;-0.0206969;-0.0218593;-0.0230044;-0.0241322;-0.0252427;-0.0263359;-0.0274116;-0.0284697;-0.0295101;-0.0305326;-0.0315368;-0.0325226;-0.0334896;-0.0344375;-0.0353657;-0.0362739;-0.0371615;-0.0380281;-0.0388729;-0.0396953;-0.0404947;-0.0412702;-0.0420211;-0.0427465;-0.0434454;-0.0441169;-0.0447598;-0.0453732;-0.0459557;-0.0465061;-0.0470231;-0.0475052;-0.0479508;-0.0483584;-0.0487262;-0.0490524;-0.049335;-0.0495718;-0.0497607;-0.0498992;-0.0499847;-0.0500144;-0.0499853;-0.049894;-0.049737;-0.0495104;-0.0492097;-0.0488303;-0.0483666;-0.0478129;-0.0471621;-0.0464065;-0.045537;-0.044543;-0.0434119;-0.0421284;-0.0406734;-0.0390231;-0.0371459;-0.0349992;-0.0325222;-0.0296224;-0.0261449;-0.0217893;-0.0157825;0;0.0157825;0.0217893;0.0261449;0.0296224;0.0325222;0.0349992;0.0371459;0.0390231;0.0406734;0.0421284;0.0434119;0.044543;0.045537;0.0464065;0.0471621;0.0478129;0.0483666;0.0488303;0.0492097;0.0495104;0.049737;0.049894;0.0499853;0.0500144;0.0499847;0.0498992;0.0497607;0.0495718;0.049335;0.0490524;0.0487262;0.0483584;0.0479508;0.0475052;0.0470231;0.0465061;0.0459557;0.0453732;0.0447598;0.0441169;0.0434454;0.0427465;0.0420211;0.0412702;0.0404947;0.0396953;0.0388729;0.0380281;0.0371615;0.0362739;0.0353657;0.0344375;0.0334896;0.0325226;0.0315368;0.0305326;0.0295101;0.0284697;0.0274116;0.0263359;0.0252427;0.0241322;0.0230044;0.0218593;0.0206969;0.019517;0.0183197;0.0171047;0.0158719;0.0146211;0.013352;0.0120643;0.0107578;0.00943205;0.0080867;0.00672133;0.00533547;0.00392865;0.00250035;0.00105
+
+ 1;0.9875;0.975;0.9625;0.95;0.9375;0.925;0.9125;0.9;0.8875;0.875;0.8625;0.85;0.8375;0.825;0.8125;0.8;0.7875;0.775;0.7625;0.75;0.7375;0.725;0.7125;0.7;0.6875;0.675;0.6625;0.65;0.6375;0.625;0.6125;0.6;0.5875;0.575;0.5625;0.55;0.5375;0.525;0.5125;0.5;0.4875;0.475;0.4625;0.45;0.4375;0.425;0.4125;0.4;0.3875;0.375;0.3625;0.35;0.3375;0.325;0.3125;0.3;0.2875;0.275;0.2625;0.25;0.2375;0.225;0.2125;0.2;0.1875;0.175;0.1625;0.15;0.1375;0.125;0.1125;0.1;0.0875;0.075;0.0625;0.05;0.0375;0.025;0.0125;0;0.0125;0.025;0.0375;0.05;0.0625;0.075;0.0875;0.1;0.1125;0.125;0.1375;0.15;0.1625;0.175;0.1875;0.2;0.2125;0.225;0.2375;0.25;0.2625;0.275;0.2875;0.3;0.3125;0.325;0.3375;0.35;0.3625;0.375;0.3875;0.4;0.4125;0.425;0.4375;0.45;0.4625;0.475;0.4875;0.5;0.5125;0.525;0.5375;0.55;0.5625;0.575;0.5875;0.6;0.6125;0.625;0.6375;0.65;0.6625;0.675;0.6875;0.7;0.7125;0.725;0.7375;0.75;0.7625;0.775;0.7875;0.8;0.8125;0.825;0.8375;0.85;0.8625;0.875;0.8875;0.9;0.9125;0.925;0.9375;0.95;0.9625;0.975;0.9875;1
+
+ 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
+
+ -0.00105;-0.00250035;-0.00392865;-0.00533547;-0.00672133;-0.0080867;-0.00943205;-0.0107578;-0.0120643;-0.013352;-0.0146211;-0.0158719;-0.0171047;-0.0183197;-0.019517;-0.0206969;-0.0218593;-0.0230044;-0.0241322;-0.0252427;-0.0263359;-0.0274116;-0.0284697;-0.0295101;-0.0305326;-0.0315368;-0.0325226;-0.0334896;-0.0344375;-0.0353657;-0.0362739;-0.0371615;-0.0380281;-0.0388729;-0.0396953;-0.0404947;-0.0412702;-0.0420211;-0.0427465;-0.0434454;-0.0441169;-0.0447598;-0.0453732;-0.0459557;-0.0465061;-0.0470231;-0.0475052;-0.0479508;-0.0483584;-0.0487262;-0.0490524;-0.049335;-0.0495718;-0.0497607;-0.0498992;-0.0499847;-0.0500144;-0.0499853;-0.049894;-0.049737;-0.0495104;-0.0492097;-0.0488303;-0.0483666;-0.0478129;-0.0471621;-0.0464065;-0.045537;-0.044543;-0.0434119;-0.0421284;-0.0406734;-0.0390231;-0.0371459;-0.0349992;-0.0325222;-0.0296224;-0.0261449;-0.0217893;-0.0157825;0;0.0157825;0.0217893;0.0261449;0.0296224;0.0325222;0.0349992;0.0371459;0.0390231;0.0406734;0.0421284;0.0434119;0.044543;0.045537;0.0464065;0.0471621;0.0478129;0.0483666;0.0488303;0.0492097;0.0495104;0.049737;0.049894;0.0499853;0.0500144;0.0499847;0.0498992;0.0497607;0.0495718;0.049335;0.0490524;0.0487262;0.0483584;0.0479508;0.0475052;0.0470231;0.0465061;0.0459557;0.0453732;0.0447598;0.0441169;0.0434454;0.0427465;0.0420211;0.0412702;0.0404947;0.0396953;0.0388729;0.0380281;0.0371615;0.0362739;0.0353657;0.0344375;0.0334896;0.0325226;0.0315368;0.0305326;0.0295101;0.0284697;0.0274116;0.0263359;0.0252427;0.0241322;0.0230044;0.0218593;0.0206969;0.019517;0.0183197;0.0171047;0.0158719;0.0146211;0.013352;0.0120643;0.0107578;0.00943205;0.0080867;0.00672133;0.00533547;0.00392865;0.00250035;0.00105
@@ -942,4 +970,4 @@
-
+
\ No newline at end of file
diff --git a/WKDIR/.keep b/test_thermodata_out/.openmdao_out
similarity index 100%
rename from WKDIR/.keep
rename to test_thermodata_out/.openmdao_out
diff --git a/test_thermodata_out/reports/inputs.html b/test_thermodata_out/reports/inputs.html
new file mode 100644
index 000000000..6960ae097
--- /dev/null
+++ b/test_thermodata_out/reports/inputs.html
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test_thermodata_out/reports/n2.html b/test_thermodata_out/reports/n2.html
new file mode 100644
index 000000000..54b5e9ed0
--- /dev/null
+++ b/test_thermodata_out/reports/n2.html
@@ -0,0 +1,14805 @@
+
+
+
+OpenMDAO Model Hierarchy and N2 diagram
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Collapsed Variables
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Show N2 Error
+
+
+
+
+
+
+
+
+
+ Search
+ ✕ ➤
+
+
+ Select All
+ Select None
+ Apply
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/Test_input2.xml b/tests/Test_input2.xml
deleted file mode 100644
index 155150841..000000000
--- a/tests/Test_input2.xml
+++ /dev/null
@@ -1,1396 +0,0 @@
-
-
-
- Cpacs2Test
- Simple Wing for unit testing
- Martin Siggel
- 2012-10-09T15:12:47
- 0.5.0
- 3.2
-
-
- Converted to cpacs 3.0 using cpacs2to3 - does not include structure
- update
- cpacs2to3
- 2018-01-15T09:22:57
- 0.2
- 3.0
-
-
- Added missing UIDs.
- fix_errors.py
- 2019-04-29T20:48:26
- 0.3.0
- 3.0
-
-
- Converted to CPACS 3.1 using cpacs2to3
- cpacs2to3
- 2020-04-26T02:01:36
- 0.4.0
- 3.1
-
-
- Converted to CPACS 3.2 using cpacs2to3
- cpacs2to3
- 2021-04-23T18:02:03
- 0.5.0
- 3.2
-
-
-
-
-
-
- Rotor models
-
- 1
- 1
-
- 0.0
- 0.0
- 0.0
-
-
-
-
- Propeller
- propeller
-
-
- 0.8
- 0.8
- 0.8
-
-
- 0
- -90
- 0
-
-
- 0.2
- 3
- 0
-
-
-
- PropellerHub
- rigid
-
-
- 5
-
-
- Propeller_pitchHinge
-
-
- 0.10
- 0
- 0
-
-
- pitch
- 10
-
-
- Propeller_blade
-
-
-
- 3300
-
-
-
-
- Propeller_blade
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
- Propeller_blade_section1
-
-
- 0.07
- 0.07
- 0.07
-
-
- 14.56
- 0
- 10
-
-
- 0
- 0
- 0
-
-
-
-
- Propeller_blade_section1_element_1
- NACA0010
-
-
- 1
- 1
- 1.75
-
-
- 0
- 0
- -90
-
-
- 0
- 0.5
- 0
-
-
-
-
-
-
- Propeller_blade_section5
-
-
- 0.1
- 0.1
- 0.1
-
-
- 5.97
- 0
- -11.42
-
-
-
-
- Propeller_blade_section5_element1
- NACA0010
-
-
- 1
- 1
- 0.39
-
-
- 0
- 0
- -90
-
-
- 0
- 0.5
- 0
-
-
-
-
-
-
- Propeller_blade_section10
-
-
- 0.03
- 0.03
- 0.03
-
-
- -9.18
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
- Propeller_blade_section10_element1
- NACA0010
-
-
- 1
- 1
- 0.18
-
-
- 0
- 0
- -90
-
-
- 0
- 0.5
- 0
-
-
-
-
-
-
-
-
- Propeller_blade_positioning1
- 0.05
- 90
- 0
- Propeller_blade_section1
-
-
- Propeller_blade_positioning2
- 0.2
- 90
- 0
- Propeller_blade_section1
- Propeller_blade_section5
-
-
- Propeller_blade_positioning3
- 0.3
- 90
- 0
- Propeller_blade_section5
- Propeller_blade_section10
-
-
-
-
- Propeller_blade_segment1
- Propeller_blade_section1_element1
- Propeller_blade_section5_element1
-
-
- Propeller_blade_segment2
- Propeller_blade_section5_element1
- Propeller_blade_section10_element1
-
-
-
-
-
-
-
-
- SimpleEngine
-
-
- centerCowlRotationCurve
- 0
-
-
-
-
-
-
- First Section containing the profile curve in flow
- direction of the nacelle
- upperSection
- fanCowlUpperSectionProfile
-
-
- 2.0
- 2.0
- 1.0
-
-
- 0.0
- 0.0
- 0.0
-
-
- 0.0
- 0.0
- 0.5
-
-
-
-
-
- fanCowl_section_1
- fanCowlRotationCurve
- -0.6
- -0.3
- -0.7
- -0.2
-
-
-
-
-
-
-
- 2
- 1.0
- 1.2697887141731155
-
-
- 0.0
- 0.0
- 0.0
-
-
- 0
- 0.7059494885767693
- 0.2808900068815356
-
-
- coreCowlSectionProfile
-
-
-
- engine_nacelle_coreCowl_Section1
- -0.28
- -0.25
- -0.3
- -0.23
- coreCowlRotationCurve
-
-
-
-
-
-
-
- SimpleTest
-
- 1
- 1
-
- 0
- 0
- 0
-
-
-
-
- name
- description
-
-
- 1
- 0.5
- 0.5
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
- D150_Fuselage_1Section1
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
- D150_Fuselage_1Section1
- fuselageCircleProfileuID
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- D150_Fuselage_1Section2
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0.5
- 0
- 0
-
-
-
-
- D150_Fuselage_1Section2
- fuselageCircleProfileuID
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- D150_Fuselage_1Section3
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
- D150_Fuselage_1Section3
- fuselageCircleProfileuID
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
-
-
-
-
- D150_Fuselage_1Positioning1
- -0.5
- 90
- 0
- D150_Fuselage_1Section1ID
-
-
- D150_Fuselage_1Positioning3
- 2
- 90
- 0
- D150_Fuselage_1Section1ID
- D150_Fuselage_1Section3ID
-
-
-
-
- D150_Fuselage_1Segment2
- D150_Fuselage_1Section1IDElement1
- D150_Fuselage_1Section2IDElement1
-
-
- D150_Fuselage_1Segment3
- D150_Fuselage_1Section2IDElement1
- D150_Fuselage_1Section3IDElement1
-
-
-
-
-
-
- Wing
- SimpleFuselage
- This wing has been generated to test CATIA2CPACS.
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
- Cpacs2Test - Wing Section 1
- Cpacs2Test - Wing Section 1
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
- Cpacs2Test - Wing Section 1 Main Element
- Cpacs2Test - Wing Section 1 Main Element
- NACA0006
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Cpacs2Test - Wing Section 2
- Cpacs2Test - Wing Section 2
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
- Cpacs2Test - Wing Section 2 Main Element
- Cpacs2Test - Wing Section 2 Main Element
- NACA0006
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
-
-
- Cpacs2Test - Wing Section 3
- Cpacs2Test - Wing Section 3
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
- Cpacs2Test - Wing Section 3 Main Element
- Cpacs2Test - Wing Section 3 Main Element
- NACA0006
-
-
- 0.5
- 0.5
- 0.5
-
-
- 0
- 0
- 0
-
-
- 0.5
- 0
- 0
-
-
-
-
-
-
- Cpacs2Test_Wing_Sec3After
-
-
- 0.25
- 0.25
- 0.25
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
- Cpacs2Test_Wing_Sec3AfterElem1
- NACA0006
-
-
- 1
- 1
- 1
-
-
- 0
- 0
- 0
-
-
- 0
- 0
- 0
-
-
-
-
-
-
-
-
- Cpacs2Test - Wing Section 1 Positioning
- Cpacs2Test - Wing Section 1 Positioning
- 0
- 0
- 0
- Cpacs2Test_Wing_Sec1
-
-
- Cpacs2Test - Wing Section 2 Positioning
- Cpacs2Test - Wing Section 2 Positioning
- 1
- 0
- 0
- Cpacs2Test_Wing_Sec1
- Cpacs2Test_Wing_Sec2
-
-
- Cpacs2Test - Wing Section 3 Positioning
- Cpacs2Test - Wing Section 3 Positioning
- 1
- 0
- 0
- Cpacs2Test_Wing_Sec2
- Cpacs2Test_Wing_Sec3
-
-
- Autogenerated positioning to set the position of section:
- Cpacs2Test_Wing_Sec3After
- 3.12517
- 16.2707
- 0
- Cpacs2Test_Wing_Sec3After
-
-
-
-
- Fuselage Segment from Cpacs2Test - Wing Section 1 Main Element
- to Cpacs2Test - Wing Section 2 Main Element
- Fuselage Segment from Cpacs2Test - Wing Section 1 Main
- Element to Cpacs2Test - Wing Section 2 Main Element
- Cpacs2Test_Wing_Sec1_El1
- Cpacs2Test_Wing_Sec2_El1
-
-
- Fuselage Segment from Cpacs2Test - Wing Section 2 Main Element
- to Cpacs2Test - Wing Section 3 Main Element
- Fuselage Segment from Cpacs2Test - Wing Section 2 Main
- Element to Cpacs2Test - Wing Section 3 Main Element
- Cpacs2Test_Wing_Sec2_El1
- Cpacs2Test_Wing_Sec3_El1
-
-
- SegGenerated
- Cpacs2Test_Wing_Sec3_El1
- Cpacs2Test_Wing_Sec3AfterElem1
-
-
-
-
- Wing_CS1
- Cpacs2Test_Wing_Sec1_El1
- Cpacs2Test_Wing_Sec3_El1
-
-
-
-
- MySkinMat
- 0
-
-
-
-
-
-
- MyCellMat
- 0
-
-
-
- 0.8
- 0.8
-
-
- 1
- 1
-
-
-
- 0
- WING_CS1
-
-
- 0
- WING_CS1
-
-
-
-
- 0.5
- WING_CS1
-
-
- 0.5
- WING_CS1
-
-
- |
-
-
-
-
-
- MySkinMat
-
-
-
-
-
-
-
-
-
-
- Wing
-
-
- 90
- 0
- -90
-
-
- -0.45
- 1
- -0.12
-
-
- 0.5
- 1
- 1
-
-
-
-
- Pylon Section 1
-
-
- 0.25
- 0.25
- 0.2
-
-
-
-
- Cpacs2Test - Wing Section 1 Main Element
- PylonProfile
-
-
-
-
-
- Pylon Section 2
-
-
- 0.45
- 0.35
- 0.25
-
-
-
-
- Cpacs2Test - Wing Section 1 Main Element
- PylonProfile
-
-
-
-
-
- Pylon Section 3
-
-
- 0.1
- 0.1
- 0.05
-
-
-
-
- Cpacs2Test - Wing Section 1 Main Element
- PylonProfile
-
-
-
-
-
-
-
- Pylon Position 1
- 0
- 0
- 0
- Pylon_Sec1
-
-
- Pylon Position 2
- 0.5
- -20
- 0
- Pylon_Sec1
- Pylon_Sec2
-
-
- Pylon Position 3
- 0.9
- 0
- 0
- Pylon_Sec2
- Pylon_Sec3
-
-
-
-
- Pylon Segment 1
- Pylon_Sec1_El1
- Pylon_Sec2_El1
-
-
- Pylon Segment 2
- Pylon_Sec2_El1
- Pylon_Sec3_El1
-
-
-
-
- Wing
-
-
- 90
- 0
- -90
-
-
- 0.1
- 3
- 0
-
-
-
-
- PylonR section 1
-
-
- 0
- 0
- 0
-
-
-
-
- PylonR section 1 element 1
- PylonRProfile
-
-
-
-
-
- PylonR Section 2
-
-
- 0.15
- 0.15
- 0.15
-
-
-
-
- PylonR section 2 element 1
- PylonRProfile
-
-
- 1
- 1
- 1
-
-
-
-
-
-
- PylonR Section 3
-
-
- 0.02
- 0.02
- 0.02
-
-
-
-
- PylonR section 3 element 1
- PylonRProfile
-
-
- 1
- 1
- 1
-
-
-
-
-
-
-
-
- PylonR Position 1
- 0
- 0
- 0
- PylonR_Sec1
-
-
- PylonR Position 2
- 0.2
- 0
- 0
- PylonR_Sec1
- PylonR_Sec2
-
-
- PylonR Position 3
- 1
- 0
- 0
- PylonR_Sec2
- PylonR_Sec3
-
-
-
-
- PylonR Segment 1
- PylonR_Sec1_El1
- PylonR_Sec2_El1
-
-
- PylonR Segment 2
- PylonR_Sec2_El1
- PylonR_Sec3_El1
-
-
-
-
-
-
- SimpleEngine positioned relative to aircraft pylon
- BuzzLightyear
- SimpleEngine
- Pylon
-
-
- 0.66
- 0.66
- 0.66
-
-
- 0
- 0
- 0
-
-
- -0.1
- 0
- -0.47
-
-
-
-
-
-
-
- aeromap_empty
- Common default aeroMap
-
- ISA
-
-
- 0
- 0.3
- 0
- 0
-
-
- ISA
-
-
-
- test_apm
- Aeromap for tests
-
- ISA
-
-
- 0;0;0;0
- 0.3;0.3;0.3;0.3
- 0;10;0;10
- 0;0;10;10
-
-
-
-
-
-
-
-
-
- Peter
- rotational symmetric part of fan cowl
-
- 0;1
- -0.06;-0.06
-
-
-
- NACA0.00.00.12
- rotation Curve for center cowl of nacelle is currently lower half
- of NACA 12
-
-
- 2;1.975;1.95;1.925;1.9;1.875;1.85;1.825;1.8;1.775;1.75;1.725;1.7;1.675;1.65;1.625;1.6;1.575;1.55;1.525;1.5;1.475;1.45;1.425;1.4;1.375;1.35;1.325;1.3;1.275;1.25;1.225;1.2;1.175;1.15;1.125;1.1;1.075;1.05;1.025;1;0.975;0.95;0.925;0.9;0.875;0.85;0.825;0.8;0.775;0.75;0.725;0.7;0.675;0.65;0.625;0.6;0.575;0.55;0.525;0.5;0.475;0.45;0.425;0.4;0.375;0.35;0.325;0.3;0.275;0.25;0.225;0.2;0.175;0.15;0.125;0.1;0.075;0.05;0.025;0
-
- -0.00189;-0.00450063;-0.00707159;-0.00960385;-0.0120984;-0.0145561;-0.0169777;-0.019364;-0.0217158;-0.0240336;-0.0263179;-0.0285694;-0.0307884;-0.0329754;-0.0351306;-0.0372543;-0.0393468;-0.0414079;-0.043438;-0.045437;-0.0474047;-0.0493408;-0.0512454;-0.0531182;-0.0549586;-0.0567663;-0.0585408;-0.0602814;-0.0619875;-0.0636584;-0.065293;-0.0668909;-0.0684505;-0.0699712;-0.0714516;-0.0728904;-0.0742865;-0.0756379;-0.0769437;-0.0782018;-0.0794105;-0.0805677;-0.0816717;-0.0827203;-0.0837111;-0.0846415;-0.0855093;-0.0863115;-0.0870452;-0.0877073;-0.0882944;-0.088803;-0.0892293;-0.0895692;-0.0898185;-0.0899724;-0.090026;-0.0899735;-0.0898092;-0.0895266;-0.0891186;-0.0885774;-0.0878944;-0.08706;-0.0860631;-0.0848918;-0.0835315;-0.0819666;-0.0801774;-0.0781415;-0.075831;-0.0732122;-0.0702415;-0.0668626;-0.0629985;-0.05854;-0.0533204;-0.0470609;-0.0392208;-0.0284085;0
-
-
-
- rotational symmetric part of core cowl
-
- 0.271084;0.272088
- -0.0252;-0.0252
-
-
-
-
-
-
- NACA0.00.00.12
- upper section of the outer nacelle geometry
-
-
- 1;0.9875;0.975;0.9625;0.95;0.9375;0.925;0.9125;0.9;0.8875;0.875;0.8625;0.85;0.8375;0.825;0.8125;0.8;0.7875;0.775;0.7625;0.75;0.7375;0.725;0.7125;0.7;0.6875;0.675;0.6625;0.65;0.6375;0.625;0.6125;0.6;0.5875;0.575;0.5625;0.55;0.5375;0.525;0.5125;0.5;0.4875;0.475;0.4625;0.45;0.4375;0.425;0.4125;0.4;0.3875;0.375;0.3625;0.35;0.3375;0.325;0.3125;0.3;0.2875;0.275;0.2625;0.25;0.2375;0.225;0.2125;0.2;0.1875;0.175;0.1625;0.15;0.1375;0.125;0.1125;0.1;0.0875;0.075;0.0625;0.05;0.0375;0.025;0.0125;0;0.0125;0.025;0.0375;0.05;0.0625;0.075;0.0875;0.1;0.1125;0.125;0.1375;0.15;0.1625;0.175;0.1875;0.2;0.2125;0.225;0.2375;0.25;0.2625;0.275;0.2875;0.3;0.3125;0.325;0.3375;0.35;0.3625;0.375;0.3875;0.4;0.4125;0.425;0.4375;0.45;0.4625;0.475;0.4875;0.5;0.5125;0.525;0.5375;0.55;0.5625;0.575;0.5875;0.6;0.6125;0.625;0.6375;0.65;0.6625;0.675;0.6875;0.7;0.7125;0.725;0.7375;0.75;0.7625;0.775;0.7875;0.8;0.8125;0.825;0.8375;0.85;0.8625;0.875;0.8875;0.9;0.9125;0.925;0.9375;0.95;0.9625;0.975;0.9875;1
-
- -0.00126;-0.00300042;-0.00471439;-0.00640257;-0.00806559;-0.00970404;-0.0113185;-0.0129093;-0.0144772;-0.0160224;-0.0175453;-0.0190463;-0.0205256;-0.0219836;-0.0234204;-0.0248362;-0.0262312;-0.0276053;-0.0289587;-0.0302913;-0.0316031;-0.0328939;-0.0341636;-0.0354121;-0.0366391;-0.0378442;-0.0390272;-0.0401876;-0.041325;-0.0424389;-0.0435287;-0.0445939;-0.0456337;-0.0466475;-0.0476344;-0.0485936;-0.0495243;-0.0504253;-0.0512958;-0.0521345;-0.0529403;-0.0537118;-0.0544478;-0.0551469;-0.0558074;-0.0564277;-0.0570062;-0.057541;-0.0580301;-0.0584715;-0.0588629;-0.059202;-0.0594862;-0.0597128;-0.059879;-0.0599816;-0.0600173;-0.0599823;-0.0598728;-0.0596844;-0.0594124;-0.0590516;-0.0585963;-0.05804;-0.0573754;-0.0565945;-0.0556877;-0.0546444;-0.0534516;-0.0520943;-0.050554;-0.0488081;-0.0468277;-0.0445751;-0.041999;-0.0390267;-0.0355469;-0.0313739;-0.0261472;-0.018939;0;0.018939;0.0261472;0.0313739;0.0355469;0.0390267;0.041999;0.0445751;0.0468277;0.0488081;0.050554;0.0520943;0.0534516;0.0546444;0.0556877;0.0565945;0.0573754;0.05804;0.0585963;0.0590516;0.0594124;0.0596844;0.0598728;0.0599823;0.0600173;0.0599816;0.059879;0.0597128;0.0594862;0.059202;0.0588629;0.0584715;0.0580301;0.057541;0.0570062;0.0564277;0.0558074;0.0551469;0.0544478;0.0537118;0.0529403;0.0521345;0.0512958;0.0504253;0.0495243;0.0485936;0.0476344;0.0466475;0.0456337;0.0445939;0.0435287;0.0424389;0.041325;0.0401876;0.0390272;0.0378442;0.0366391;0.0354121;0.0341636;0.0328939;0.0316031;0.0302913;0.0289587;0.0276053;0.0262312;0.0248362;0.0234204;0.0219836;0.0205256;0.0190463;0.0175453;0.0160224;0.0144772;0.0129093;0.0113185;0.00970404;0.00806559;0.00640257;0.00471439;0.00300042;0.00126
-
-
-
- cr001sm_il
-
-
- 0.996194;0.993754;0.986971;0.976729;0.963458;0.947066;0.92744;0.904744;0.879219;0.851108;0.820703;0.788289;0.754153;0.718592;0.681903;0.644385;0.606339;0.568023;0.529718;0.491436;0.453179;0.415238;0.377744;0.34092;0.30497;0.270075;0.236471;0.20436;0.173917;0.145373;0.118912;0.0947228;0.0729665;0.0537642;0.0372754;0.0235897;0.0127525;0.00500524;0.00074618;0;0.00123634;0.00614245;0.0144781;0.0258662;0.0400693;0.0570392;0.0766765;0.0988121;0.12332;0.150039;0.178779;0.209361;0.241554;0.275188;0.31006;0.345937;0.382642;0.419975;0.457723;0.495757;0.533793;0.571832;0.609867;0.64762;0.684836;0.721221;0.75648;0.79032;0.822449;0.852586;0.880447;0.905742;0.928227;0.947659;0.963881;0.97701;0.987132;0.993808;0.996194
-
- -0.087172;-0.0868298;-0.085883;-0.0844168;-0.0824767;-0.0800629;-0.0772055;-0.0739912;-0.0705214;-0.0669055;-0.0632255;-0.0595545;-0.0559497;-0.0524611;-0.0491307;-0.0459768;-0.0430096;-0.0402517;-0.0377195;-0.0353821;-0.0332236;-0.0312292;-0.0294105;-0.0277307;-0.0261674;-0.0246966;-0.0232824;-0.0218864;-0.0204838;-0.0190466;-0.0175587;-0.0160367;-0.0144705;-0.0128548;-0.0112034;-0.00952422;-0.00775697;-0.0056658;-0.00271537;0;0.00288723;0.00733255;0.0119677;0.0164964;0.0207306;0.0245782;0.0279355;0.0307048;0.0328249;0.0342857;0.0350959;0.0352712;0.0348237;0.0337442;0.0320103;0.0296103;0.0265677;0.0229159;0.018714;0.0140052;0.00885457;0.00328603;-0.00268361;-0.00899806;-0.0156028;-0.0224158;-0.0293553;-0.0363392;-0.0432618;-0.05001;-0.056487;-0.0625868;-0.0682079;-0.0732808;-0.0776392;-0.081213;-0.084042;-0.0862161;-0.087172
-
-
-
-
-
- NACA0.00.00.12
- NACA 4 Series Profile
-
-
- 1;0.9875;0.975;0.9625;0.95;0.9375;0.925;0.9125;0.9;0.8875;0.875;0.8625;0.85;0.8375;0.825;0.8125;0.8;0.7875;0.775;0.7625;0.75;0.7375;0.725;0.7125;0.7;0.6875;0.675;0.6625;0.65;0.6375;0.625;0.6125;0.6;0.5875;0.575;0.5625;0.55;0.5375;0.525;0.5125;0.5;0.4875;0.475;0.4625;0.45;0.4375;0.425;0.4125;0.4;0.3875;0.375;0.3625;0.35;0.3375;0.325;0.3125;0.3;0.2875;0.275;0.2625;0.25;0.2375;0.225;0.2125;0.2;0.1875;0.175;0.1625;0.15;0.1375;0.125;0.1125;0.1;0.0875;0.075;0.0625;0.05;0.0375;0.025;0.0125;0;0.0125;0.025;0.0375;0.05;0.0625;0.075;0.0875;0.1;0.1125;0.125;0.1375;0.15;0.1625;0.175;0.1875;0.2;0.2125;0.225;0.2375;0.25;0.2625;0.275;0.2875;0.3;0.3125;0.325;0.3375;0.35;0.3625;0.375;0.3875;0.4;0.4125;0.425;0.4375;0.45;0.4625;0.475;0.4875;0.5;0.5125;0.525;0.5375;0.55;0.5625;0.575;0.5875;0.6;0.6125;0.625;0.6375;0.65;0.6625;0.675;0.6875;0.7;0.7125;0.725;0.7375;0.75;0.7625;0.775;0.7875;0.8;0.8125;0.825;0.8375;0.85;0.8625;0.875;0.8875;0.9;0.9125;0.925;0.9375;0.95;0.9625;0.975;0.9875;1
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
-
- -0.00126;-0.00300042;-0.00471439;-0.00640257;-0.00806559;-0.00970404;-0.0113185;-0.0129093;-0.0144772;-0.0160224;-0.0175453;-0.0190463;-0.0205256;-0.0219836;-0.0234204;-0.0248362;-0.0262312;-0.0276053;-0.0289587;-0.0302913;-0.0316031;-0.0328939;-0.0341636;-0.0354121;-0.0366391;-0.0378442;-0.0390272;-0.0401876;-0.041325;-0.0424389;-0.0435287;-0.0445939;-0.0456337;-0.0466475;-0.0476344;-0.0485936;-0.0495243;-0.0504253;-0.0512958;-0.0521345;-0.0529403;-0.0537118;-0.0544478;-0.0551469;-0.0558074;-0.0564277;-0.0570062;-0.057541;-0.0580301;-0.0584715;-0.0588629;-0.059202;-0.0594862;-0.0597128;-0.059879;-0.0599816;-0.0600173;-0.0599823;-0.0598728;-0.0596844;-0.0594124;-0.0590516;-0.0585963;-0.05804;-0.0573754;-0.0565945;-0.0556877;-0.0546444;-0.0534516;-0.0520943;-0.050554;-0.0488081;-0.0468277;-0.0445751;-0.041999;-0.0390267;-0.0355469;-0.0313739;-0.0261472;-0.018939;0;0.018939;0.0261472;0.0313739;0.0355469;0.0390267;0.041999;0.0445751;0.0468277;0.0488081;0.050554;0.0520943;0.0534516;0.0546444;0.0556877;0.0565945;0.0573754;0.05804;0.0585963;0.0590516;0.0594124;0.0596844;0.0598728;0.0599823;0.0600173;0.0599816;0.059879;0.0597128;0.0594862;0.059202;0.0588629;0.0584715;0.0580301;0.057541;0.0570062;0.0564277;0.0558074;0.0551469;0.0544478;0.0537118;0.0529403;0.0521345;0.0512958;0.0504253;0.0495243;0.0485936;0.0476344;0.0466475;0.0456337;0.0445939;0.0435287;0.0424389;0.041325;0.0401876;0.0390272;0.0378442;0.0366391;0.0354121;0.0341636;0.0328939;0.0316031;0.0302913;0.0289587;0.0276053;0.0262312;0.0248362;0.0234204;0.0219836;0.0205256;0.0190463;0.0175453;0.0160224;0.0144772;0.0129093;0.0113185;0.00970404;0.00806559;0.00640257;0.00471439;0.00300042;0.00126
-
-
-
- NACA0.00.00.12
- NACA 4 Series Profile
-
- 1;0.66;0.33;0;0.33;0.66;1
- 0;0;0;0;0;0;0
- -0.133;-0.2;-0.2;0;0.2;0.2;0.133
-
-
-
- NACA0006
-
-
- 1;0.998459;0.993844;0.986185;0.975528;0.96194;0.945503;0.92632;0.904508;0.880203;0.853553;0.824724;0.793893;0.761249;0.726995;0.691342;0.654508;0.616723;0.578217;0.53923;0.5;0.46077;0.421783;0.383277;0.345492;0.308658;0.273005;0.238751;0.206107;0.175276;0.146447;0.119797;0.095492;0.07368;0.054497;0.03806;0.024472;0.013815;0.006156;0.001541;0;0.001541;0.006156;0.013815;0.024472;0.03806;0.054497;0.07368;0.095492;0.119797;0.146447;0.175276;0.206107;0.238751;0.273005;0.308658;0.345492;0.383277;0.421783;0.46077;0.5;0.53923;0.578217;0.616723;0.654508;0.691342;0.726995;0.761249;0.793893;0.824724;0.853553;0.880203;0.904508;0.92632;0.945503;0.96194;0.975528;0.986185;0.993844;0.998459;1
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
-
- 0;-0.000112;-0.000446;-0.000995;-0.001751;-0.002699;-0.003825;-0.005111;-0.006535;-0.008079;-0.009719;-0.011434;-0.013202;-0.015;-0.016805;-0.018594;-0.020343;-0.022027;-0.023621;-0.025098;-0.026431;-0.027592;-0.028554;-0.029291;-0.029778;-0.029994;-0.029921;-0.029544;-0.028856;-0.027854;-0.026541;-0.024927;-0.023024;-0.020853;-0.018433;-0.01579;-0.012947;-0.009927;-0.006752;-0.003438;0;0.003438;0.006752;0.009927;0.012947;0.01579;0.018433;0.020853;0.023024;0.024927;0.026541;0.027854;0.028856;0.029544;0.029921;0.029994;0.029778;0.029291;0.028554;0.027592;0.026431;0.025098;0.023621;0.022027;0.020343;0.018594;0.016805;0.015;0.013202;0.011434;0.009719;0.008079;0.006535;0.005111;0.003825;0.002699;0.001751;0.000995;0.000446;0.000112;0
-
-
-
- Engine pylonR circle profile
-
-
- 0.5;0.4;0.3;0.2;0.1;0;-0.1;-0.2;-0.3;-0.4;-0.5;-0.4;-0.3;-0.2;-0.1;0;0.1;0.2;0.3;0.4;0.5
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
-
- 0;-0.3;-0.4;-0.458258;-0.489898;-0.5;-0.489898;-0.458258;-0.4;-0.3;0;0.3;0.4;0.458258;0.489898;0.5;0.489898;0.458258;0.4;0.3;0
-
-
-
-
-
- Circle
- Profile build up from set of Points on Circle where may Dimensions
- are 1..-1
-
- 0;0;0;0;0
- 0;1;0;-1;0
- 1;0;-1;0;1
-
-
-
-
-
- NACA0.00.00.10
- NACA 4 Series Profile
-
-
- 1;0.9875;0.975;0.9625;0.95;0.9375;0.925;0.9125;0.9;0.8875;0.875;0.8625;0.85;0.8375;0.825;0.8125;0.8;0.7875;0.775;0.7625;0.75;0.7375;0.725;0.7125;0.7;0.6875;0.675;0.6625;0.65;0.6375;0.625;0.6125;0.6;0.5875;0.575;0.5625;0.55;0.5375;0.525;0.5125;0.5;0.4875;0.475;0.4625;0.45;0.4375;0.425;0.4125;0.4;0.3875;0.375;0.3625;0.35;0.3375;0.325;0.3125;0.3;0.2875;0.275;0.2625;0.25;0.2375;0.225;0.2125;0.2;0.1875;0.175;0.1625;0.15;0.1375;0.125;0.1125;0.1;0.0875;0.075;0.0625;0.05;0.0375;0.025;0.0125;0;0.0125;0.025;0.0375;0.05;0.0625;0.075;0.0875;0.1;0.1125;0.125;0.1375;0.15;0.1625;0.175;0.1875;0.2;0.2125;0.225;0.2375;0.25;0.2625;0.275;0.2875;0.3;0.3125;0.325;0.3375;0.35;0.3625;0.375;0.3875;0.4;0.4125;0.425;0.4375;0.45;0.4625;0.475;0.4875;0.5;0.5125;0.525;0.5375;0.55;0.5625;0.575;0.5875;0.6;0.6125;0.625;0.6375;0.65;0.6625;0.675;0.6875;0.7;0.7125;0.725;0.7375;0.75;0.7625;0.775;0.7875;0.8;0.8125;0.825;0.8375;0.85;0.8625;0.875;0.8875;0.9;0.9125;0.925;0.9375;0.95;0.9625;0.975;0.9875;1
-
- 0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0
-
- -0.00105;-0.00250035;-0.00392865;-0.00533547;-0.00672133;-0.0080867;-0.00943205;-0.0107578;-0.0120643;-0.013352;-0.0146211;-0.0158719;-0.0171047;-0.0183197;-0.019517;-0.0206969;-0.0218593;-0.0230044;-0.0241322;-0.0252427;-0.0263359;-0.0274116;-0.0284697;-0.0295101;-0.0305326;-0.0315368;-0.0325226;-0.0334896;-0.0344375;-0.0353657;-0.0362739;-0.0371615;-0.0380281;-0.0388729;-0.0396953;-0.0404947;-0.0412702;-0.0420211;-0.0427465;-0.0434454;-0.0441169;-0.0447598;-0.0453732;-0.0459557;-0.0465061;-0.0470231;-0.0475052;-0.0479508;-0.0483584;-0.0487262;-0.0490524;-0.049335;-0.0495718;-0.0497607;-0.0498992;-0.0499847;-0.0500144;-0.0499853;-0.049894;-0.049737;-0.0495104;-0.0492097;-0.0488303;-0.0483666;-0.0478129;-0.0471621;-0.0464065;-0.045537;-0.044543;-0.0434119;-0.0421284;-0.0406734;-0.0390231;-0.0371459;-0.0349992;-0.0325222;-0.0296224;-0.0261449;-0.0217893;-0.0157825;0;0.0157825;0.0217893;0.0261449;0.0296224;0.0325222;0.0349992;0.0371459;0.0390231;0.0406734;0.0421284;0.0434119;0.044543;0.045537;0.0464065;0.0471621;0.0478129;0.0483666;0.0488303;0.0492097;0.0495104;0.049737;0.049894;0.0499853;0.0500144;0.0499847;0.0498992;0.0497607;0.0495718;0.049335;0.0490524;0.0487262;0.0483584;0.0479508;0.0475052;0.0470231;0.0465061;0.0459557;0.0453732;0.0447598;0.0441169;0.0434454;0.0427465;0.0420211;0.0412702;0.0404947;0.0396953;0.0388729;0.0380281;0.0371615;0.0362739;0.0353657;0.0344375;0.0334896;0.0325226;0.0315368;0.0305326;0.0295101;0.0284697;0.0274116;0.0263359;0.0252427;0.0241322;0.0230044;0.0218593;0.0206969;0.019517;0.0183197;0.0171047;0.0158719;0.0146211;0.013352;0.0120643;0.0107578;0.00943205;0.0080867;0.00672133;0.00533547;0.00392865;0.00250035;0.00105
-
-
-
-
-
-
-
-
-
-
- 2
- 50
- 1
- 0
-
- NO
- 1
- aeromap_empty
-
- False
- False
- -1
-
-
- False
-
-
-
-
- test_apm
-
-
-
- False
- False
- False
- 3.0
-
- 13.0
-
- 5.0
-
-
- 5.0
-
- 0.05
- 0.05
-
- 2.0
- 0.9
- 1.0
- False
- False
- 20.0
- 20.0
-
-
-
-
-
\ No newline at end of file
diff --git a/tests/docker/Centos/Dockerfile b/tests/docker/Centos/Dockerfile
index ca6c8d7f9..8c831bb2a 100644
--- a/tests/docker/Centos/Dockerfile
+++ b/tests/docker/Centos/Dockerfile
@@ -23,7 +23,6 @@ RUN chmod +x install_*.sh
RUN ./install_ceasiompy.sh
RUN ./install_ceasiompy.sh
RUN ./install_pyavl.sh
-RUN ./install_pytornado.sh
RUN ./install_sumo.sh
RUN ./install_su2.sh
RUN source ~/.bashrc
diff --git a/tests/test_integration_tests.py b/tests/test_integration_tests.py
index d2b74c6f7..81d5c73f7 100644
--- a/tests/test_integration_tests.py
+++ b/tests/test_integration_tests.py
@@ -5,122 +5,71 @@
Integration test for some typical CEASIOMpy workflows.
-Python version: >=3.8
-
-| Author: Aidan Jungo
-| Creation: 2022-05-06
-
TODO:
-
- -
+ Test allworking modules
"""
-
# ====================================================================================================================
# IMPORTS
# ====================================================================================================================
import shutil
+import pytest
+
+import streamlit as st
+
from pathlib import Path
+from unittest.mock import MagicMock
-import pytest
-from ceasiompy.utils.ceasiompyutils import change_working_dir
-from ceasiompy.utils.commonpaths import LOGFILE
from src.bin.ceasiompy_exec import run_modules_list
+from ceasiompy.utils.ceasiompyutils import change_working_dir
+
+from ceasiompy.utils.commonpaths import CPACS_FILES_PATH
+
+# =================================================================================================
+# CONSTANTS
+# =================================================================================================
MODULE_DIR = Path(__file__).parent
WORKFLOW_TEST_DIR = Path(MODULE_DIR, "workflow_tests")
-CPACS_IN_PATH = Path(MODULE_DIR, "Test_input.xml")
-CPACS_IN_2_PATH = Path(MODULE_DIR, "Test_input2.xml")
+CPACS_IN_PATH = Path(CPACS_FILES_PATH, "D150_simple.xml")
# Remove previous workflow directory and create new one
if WORKFLOW_TEST_DIR.exists():
shutil.rmtree(WORKFLOW_TEST_DIR)
-WORKFLOW_TEST_DIR.mkdir()
-
-
-# =================================================================================================
-# CLASSES
-# =================================================================================================
+WORKFLOW_TEST_DIR.mkdir()
# =================================================================================================
# FUNCTIONS
# =================================================================================================
-def workflow_ends():
- """Check that the workflow ends correctly"""
-
- with open(LOGFILE, "r") as f:
- if "--- End of" in f.readlines()[-1]:
- return True
-
- return False
-
-
-@pytest.mark.skipif(not shutil.which("pytornado"), reason="PyTornado not installed")
-def test_integration_1():
- modules_to_run = [
- "WeightConventional",
- "PyTornado",
- "SkinFriction",
- "ExportCSV",
- "StaticStability",
- ]
-
+def run_workflow_test(modules_to_run):
+ """Run a workflow test with the given modules."""
+ st.session_state = MagicMock()
with change_working_dir(WORKFLOW_TEST_DIR):
- run_modules_list([str(CPACS_IN_PATH), *modules_to_run])
+ run_modules_list([str(CPACS_IN_PATH), *modules_to_run], test=True)
- assert workflow_ends()
-
-
-@pytest.mark.slow
-@pytest.mark.skipif(not shutil.which("dwfsumo"), reason="SUMO not installed")
-@pytest.mark.skipif(not shutil.which("SU2_CFD"), reason="SU2_CFD not installed")
-def test_integration_2():
- modules_to_run = ["CPACS2SUMO", "SUMOAutoMesh", "SU2Run", "ExportCSV"]
-
- with change_working_dir(WORKFLOW_TEST_DIR):
- run_modules_list([str(CPACS_IN_PATH), *modules_to_run])
-
- assert workflow_ends()
-
-
-@pytest.mark.slow
-@pytest.mark.skipif(not shutil.which("dwfsumo"), reason="SUMO not installed")
-@pytest.mark.skipif(not shutil.which("SU2_CFD"), reason="SU2_CFD not installed")
-def test_integration_3():
- modules_to_run = ["CLCalculator", "CPACS2SUMO", "SUMOAutoMesh", "SU2Run"]
-
- with change_working_dir(WORKFLOW_TEST_DIR):
- run_modules_list([str(CPACS_IN_PATH), *modules_to_run])
-
- assert workflow_ends()
+# =================================================================================================
+# TESTS
+# =================================================================================================
@pytest.mark.slow
@pytest.mark.skipif(not shutil.which("gmsh"), reason="GMSH not installed")
@pytest.mark.skipif(not shutil.which("SU2_CFD"), reason="SU2_CFD not installed")
-def test_integration_4():
- modules_to_run = ["CPACS2GMSH", "SU2Run", "SaveAeroCoefficients"]
-
- with change_working_dir(WORKFLOW_TEST_DIR):
- run_modules_list([str(CPACS_IN_2_PATH), *modules_to_run])
-
- assert workflow_ends()
+def test_integration_1():
+ run_workflow_test(["CPACSUpdater", "CPACS2GMSH", "SU2Run"])
+ assert True
@pytest.mark.slow
@pytest.mark.skipif(not shutil.which("avl"), reason="avl not installed")
-def test_integration_5():
- modules_to_run = ["PyAVL", "SaveAeroCoefficients"]
-
- with change_working_dir(WORKFLOW_TEST_DIR):
- run_modules_list([str(CPACS_IN_PATH), *modules_to_run])
-
- assert workflow_ends()
+def test_integration_2():
+ run_workflow_test(["PyAVL", "SaveAeroCoefficients", "Database"])
+ assert True
# =================================================================================================