diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..898e67a2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[Feature request] - title of the issue" +labels: enhancement +assignees: '' + +--- + +# Is your feature request related to a problem? Please describe. +A clear and concise description of what the problem is. + +# Describe the solution you'd like +A clear and concise description of what you want to happen. + +# Describe alternatives you've considered +A clear and concise description of any alternative solutions or features you've considered. + +# Additional context +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/improve_documentation.md b/.github/ISSUE_TEMPLATE/improve_documentation.md new file mode 100644 index 00000000..8505783b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/improve_documentation.md @@ -0,0 +1,23 @@ +--- +name: Improve documentation +about: Help us improve our documentation +title: "[DOC] - title of the issue" +labels: documentation +assignees: '' + +--- + +# What were you looking for +Please try to describe in a concise way which kind of information you were looking for and where were you looking. + +# Is the information available in the doc but not where you would expect? +let us know if you ended up finding the information you were looking for in another (unexpected) section of the doc. + +# Is information completely lacking or partial? +please specify if you are asking for better explanation/clarification of the text or if the information is completely lacking. + +# Where would you insert the missing information? +describe here where you would put the information. + +# Additional context +you can use this space to give any other additional information that you think would be useful to solve the issue. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..67239921 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,17 @@ +If you are a first-time contributor to GEOUNED, please read our contributing guidelines: +/~https://github.com/GEOUNED-org/GEOUNED/blob/main/CONTRIBUTING.md + +# Description + +Please include a summary of the changes and motivation for the changes + +# Fixes issue + +Please link to any issues that this PR fixes + +# Checklist + +- [ ] I'm making a PR from a feature branch on my fork into GEOUNED-org/GEOUNED/dev branch +- [ ] I have followed [PEP8 style guide]([url](https://peps.python.org/pep-0008/)) for Python or run a formatter such as [black]([url](/~https://github.com/psf/black)) or [ruff format]([url](/~https://github.com/astral-sh/ruff)) on my code +- [ ] I have made corresponding changes to the documentation +- [ ] I have added tests that prove my fix is effective or that my feature works diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml new file mode 100644 index 00000000..48277435 --- /dev/null +++ b/.github/workflows/black.yml @@ -0,0 +1,12 @@ +name: Lint + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: psf/black@stable + with: + version: "24.4.2" \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93422485..197df08c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,12 @@ on: branches: - dev - main + paths-ignore: + - "**.md" + - "**.txt" + - "**.cff" + - 'docs/**' + - '.gitignore' push: branches: - main @@ -44,13 +50,17 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install . + geouned_cadtocsg --help python -c 'import geouned' - python -c 'from geouned import GEOUNED' + python -c 'from geouned import CadToCsg' python -c 'from geouned.GEOReverse import reverse' python -m pip install .[tests] - name: testing GEOUNED functionality - run: python -m pytest -v tests/test_convert.py + run: | + python -m pytest -v tests/test_convert.py + geouned_cadtocsg -i tests/config_complete_defaults.json + geouned_cadtocsg -i tests/config_non_defaults.json - name: install openmc if: ${{ matrix.os == 'ubuntu-latest'}} diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml new file mode 100644 index 00000000..41934563 --- /dev/null +++ b/.github/workflows/documentation.yml @@ -0,0 +1,52 @@ +name: documentation + +on: + pull_request: + branches: + - dev + - main + push: + branches: + - main + - dev + +permissions: + contents: write + +jobs: + testing: + name: Documentation + runs-on: "ubuntu-latest" + defaults: + run: + shell: bash -el {0} + strategy: + matrix: + python-version: ["3.11"] + steps: + - name: checkout actions + uses: actions/checkout@v4 + - uses: conda-incubator/setup-miniconda@v3 + with: + auto-update-conda: true + python-version: ${{ matrix.python-version }} + channels: conda-forge + + - name: install dependencies + run: conda install -c conda-forge freecad -y + + - name: install package + run: | + pip install --upgrade pip + pip install .[docs] + - name: Sphinx build + run: | + sphinx-build docs _build + - name: Deploy to GitHub Pages + if: github.event_name == 'push' + uses: peaceiris/actions-gh-pages@v4 + with: + publish_branch: gh-pages + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: _build/ + force_orphan: true diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 390560bb..65ec8c99 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -1,4 +1,4 @@ -name: Upload Python Package +name: PyPI Python Package on: release: diff --git a/.gitignore b/.gitignore index 28ce5fdd..72c1f57c 100644 --- a/.gitignore +++ b/.gitignore @@ -166,8 +166,16 @@ cython_debug/ # testing folder tests_outputs/ +# debug output folders +fuzzySurfaces +Suspicious_solids/ +Warning_Solids_definition.txt + .vscode **/__pycache__ + +# documentation +_build docs/source/_build/** docs/build/** docs/source/_autosummary/* diff --git a/README.md b/README.md index 4bf5f28a..eb80d808 100644 --- a/README.md +++ b/README.md @@ -1,82 +1,14 @@ [![CI testing](/~https://github.com/GEOUNED-org/GEOUNED/actions/workflows/ci.yml/badge.svg?branch=main)](/~https://github.com/GEOUNED-org/GEOUNED/actions/workflows/ci.yml) +[![Upload Python Package](/~https://github.com/GEOUNED-org/GEOUNED/actions/workflows/python-publish.yml/badge.svg)](/~https://github.com/GEOUNED-org/GEOUNED/actions/workflows/python-publish.yml) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg?style=flat-square)](/~https://github.com/psf/black) +[![documentation](/~https://github.com/GEOUNED-org/GEOUNED/actions/workflows/documentation.yml/badge.svg)](/~https://github.com/GEOUNED-org/GEOUNED/actions/workflows/documentation.yml) + # GEOUNED A tool to convert from CAD to CSG & CSG to CAD for Monte Carlo transport codes (MCNP & OpenMC). This repository contains the implementation of the algorithm presented in the paper [GEOUNED: A new conversion tool from CAD to Monte Carlo geometry](https://doi.org/10.1016/j.net.2024.01.052). -## Installation - -Install directly from the repository (for python versions > 3.7 => FreeCAD >= 0.19 ): - -```bash -pip install git+/~https://github.com/GEOUNED-code/GEOUNED.git -``` - -Otherwise, the source code included in the ~src/ folder can be directly downloaded and properly reached by path variable as follows: - -```python -import sys -GEO_path='the path in your local computer' -sys.path.append('GEO_path') -``` - -the same should be made for FreeCAD libraries. You can also define appropriately the PYTHONPATH variable for both modules. - -If you are using FreeCAD in windows there is included a python distribution within FreeCAD distribution (by default located in C:\Program Files\FreeCAD 0.XX\bin\). -In that case you can install directly de module using: - -```bash -C:\Program Files\FreeCAD 0.XX\bin\python.exe -m pip install git+/~https://github.com/GEOUNED-code/GEOUNED.git -``` -using this option you have directly access to both FreeCAD and GEOUNED python modules. -Furthermore, using this python compatibilities problems between different versions of python are avoided (some dynamic libraries of FreeCAD depends on the version of python used during the built process). - -## How to use - -The code is used via python scripting. - -The first step is to call the python modules of both FreeCAD and GEOUNED (this can be avoided if you have installed GEOUNED as commented in the previous section and using the python distributed with FreeCAD). - -```python -import sys -GEO_path='the path in your local computer, the complete or relative path to the ~src/ folder' -FreeCAD_path='path of FreeCAD python modules' -sys.path.append(GEO_path) -sys.path.append(FreeCAD_path) -``` -The second step is to call or GEOUNED for CAD to CSG conversion or GEOReverse for CSG to CAD. - -From CAD to CSG: - -```python -from geouned import GEOUNED -inifile='Name of config file for forward conversion' -GEO = GEOUNED(inifile) -GEO.SetOptions() -GEO.Start() -``` -From CSG to CAD (so far only for MCNP): - -```python -from GEOReverse.reverse import reverse -inifile='Name of config file for reverse conversion' -reverse(inifile) -``` -In the ~scripts/ folder you can find a script (geouned for linux or geouned.py for windows) to call both modules directly by command line as follows: - -windows (forward and reverse): -```bash -/~>python geouned.py file_config_name -/~>python geouned.py -r file_config_name -``` - -linux (forward and reverse): -```bash -/~>geouned file_config_name -/~>geouned -r file_config_name -``` - -Detailed descriptions of all options of the config files (forward and reverse) are given in the manual located in ~docs/ folder. +See the [online documentation](https://geouned-org.github.io/GEOUNED/index.html) for installation, usage and API reference. ## Citation diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..d4bb2cbb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..4af84bb2 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,67 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys + +sys.path.insert(0, os.path.abspath("../../src")) + +# -- Project information ----------------------------------------------------- + +project = "GEOUNED" +copyright = "2024, UNED" +author = "Juan-Pablo Catalan and Patrick Sauvan" + +# The full version, including alpha/beta/rc tags +release = "1.0.1" + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx_autodoc_typehints", + "sphinx.ext.coverage", + "sphinx.ext.napoleon", + "sphinx.ext.doctest", + "sphinx.ext.viewcode", +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "sphinx_rtd_theme" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] + +html_context = { + "display_github": True, +} +# html_favicon = "favicon.ico" diff --git a/docs/developer_guide.rst b/docs/developer_guide.rst new file mode 100644 index 00000000..555da6f9 --- /dev/null +++ b/docs/developer_guide.rst @@ -0,0 +1,4 @@ +Developer guide +=============== + +TOOD \ No newline at end of file diff --git a/src/geouned/GEOUNED/Conversion/__init__.py b/docs/docutils.conf similarity index 100% rename from src/geouned/GEOUNED/Conversion/__init__.py rename to docs/docutils.conf diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..b0c162b4 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,20 @@ +.. GEOUNED documentation master file, created by + sphinx-quickstart on Tue Apr 30 09:47:11 2024. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +GEOUNED +======= + +GEOUNED converts CAD to Constructive Solid Geometry (CSG) formats for use in +Monte Carlo transport codes. +Supported codes include OpenMC, PHITS, Serpent and MCNP. + +.. toctree:: + :maxdepth: 3 + + install/index + usage/index + python_api + methodology + developer_guide diff --git a/docs/install/index.rst b/docs/install/index.rst new file mode 100644 index 00000000..778fec1d --- /dev/null +++ b/docs/install/index.rst @@ -0,0 +1,21 @@ +.. usage + +Install +======= + +There are several options for installing GEOUNED package. +The installation selected has implications for how you run GEOUNED Python scripts. +Currently the Mamba / Conda install is the recommended method. +The main complication when installing GEOUNED is integration between the main dependency (FreeCAD) and the users system Python. +Mamba / Conda provides a connection between the FreeCAD Python library and your system Python. +Users have also had success installing FreeCAD and making use of the Python version inbuilt into FreeCAD and the freecad.cmd however the integration ofr FrreCAD with the system Python is more challenging when installing in this manner. +.. TODO as well so these installation methods are listed for completeness. + + + +.. toctree:: + :numbered: + :maxdepth: 1 + + install_linux + install_windows \ No newline at end of file diff --git a/docs/install/install_linux.rst b/docs/install/install_linux.rst new file mode 100644 index 00000000..446b65e5 --- /dev/null +++ b/docs/install/install_linux.rst @@ -0,0 +1,267 @@ +Linux +===== + +User install with Mamba (recommended) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +First we need to install a Mamba distribution. There are a few options but here we opt for Miniforge3 as it includes Mamba. + +You can follow the install instructions for `Miniforge3 here `_ or follows the commands below. +Download + +.. code-block:: sh + + curl -L -O "/~https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" + bash Miniforge3-$(uname)-$(uname -m).sh + +Install Miniforge3 + +.. code-block:: sh + + bash Miniforge3-Linux-x86_64.sh + + +Activate the base environment in your current terminal + +.. code-block:: sh + + mamba activate + + +It is recommended to create a new environment + +.. code-block:: sh + + mamba create --name new_env python=3.11 + + +Activate the new environment + +.. code-block:: sh + + mamba activate new_env + +We have aspirations to create a conda-forge package which will combine these final two steps, but for now FreeCAD and GEOUNED can be installed in two commands. +Install FreeCAD which is the main dependency + +.. code-block:: sh + + mamba install -c conda-forge freecad + + +Install GEOUNED with pip, we also prefix this with "python -m" to ensure that pip install uses the correct Python interpreter. + +.. code-block:: sh + + python -m pip install git+/~https://github.com/GEOUNED-code/GEOUNED.git@dev + +Then you will be able to run import GEOUNED from within Python + +.. code-block:: python + + import geouned + +You will also be able to use the GEOUNED command line tool + +.. code-block:: bash + + geouned_cadtocsg --help + +User install with Conda +~~~~~~~~~~~~~~~~~~~~~~~ + +First we need to install a Conda distribution. There are a few options but we here we opt for `MiniConda3 `_ as it downloads quicker than the fuller `AnaConda `_. + +You can follow the install instructions for `MiniConda3 `_ or follow the commands below. +Download. + +.. code-block:: sh + + mkdir -p ~/miniconda3 + wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O ~/miniconda3/miniconda.sh + +Install MiniConda3 + +.. code-block:: sh + + bash ~/miniconda3/miniconda.sh -b -u -p ~/miniconda3 + + +Activate the base environment in your current terminal + +.. code-block:: sh + + ~/miniconda3/bin/conda init bash + + +It is recommended to create a new environment + +.. code-block:: sh + + conda create --name new_env python=3.11 + + +Activate the new environment + +.. code-block:: sh + + conda activate new_env + +We have aspirations to create a conda-forge package which will combine these final two steps, but for now FreeCAD and GEOUNED can be installed in two commands. +Install FreeCAD which is the main dependency + +.. code-block:: sh + + conda install -c conda-forge freecad + + +Install GEOUNED with pip, we also prefix this with "python -m" to ensure that pip install uses the correct Python interpreter. + +.. code-block:: sh + + python -m pip install git+/~https://github.com/GEOUNED-code/GEOUNED.git@dev + +Then you will be able to run import GEOUNED from within Python + +.. code-block:: python + + import geouned + +You will also be able to use the GEOUNED command line tool + +.. code-block:: bash + + geouned_cadtocsg --help + + +Developer install with Mamba +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +First we need to install a Mamba distribution. There are a few options but here we opt for Miniforge3 as it includes Mamba. + +You can follow the install instructions for `Miniforge3 here `_ or follows the commands below. +Download + +.. code-block:: sh + + curl -L -O "/~https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" + bash Miniforge3-$(uname)-$(uname -m).sh + +Install Miniforge3 + +.. code-block:: sh + + bash Miniforge3-Linux-x86_64.sh + + +Activate the base environment in your current terminal + +.. code-block:: sh + + mamba activate + + +It is recommended to create a new environment + +.. code-block:: sh + + mamba create --name new_env python=3.11 + + +Activate the new environment + +.. code-block:: sh + + mamba activate new_env + +We have aspirations to create a conda-forge package which will combine these final two steps, but for now FreeCAD and GEOUNED can be installed in two commands. +Install FreeCAD which is the main dependency + +.. code-block:: sh + + mamba install -c conda-forge freecad + +Fork the GEOUNED-org/GEOUNED repository by clicking this link, unchecking the Copy the main branch only check box and clicking create fork + +`/~https://github.com/GEOUNED-org/GEOUNED/fork `_ + +Assuming that you have `setup `_ and `added `_ SSH keys then we can clone your forked GEOUNED repository. +Replace with your own github username + +.. code-block:: sh + + git clone git@github.com:/GEOUNED.git + +Then change directory into the repository root like this + +.. code-block:: sh + + cd GEOUNED + +Install GEOUNED with pip, we also prefix this with "python -m" to ensure that pip install uses the correct Python interpreter. +We are also adding the -e to get an editable install so that when you make local changes to the repo these are picked up in your Python scripts straight away (without needing to reinstall). +We also include all the optional dependencies so that we can run tests locally and build the docs locally. + +.. code-block:: sh + + python -m pip install -e .[tests,docs] + +Then you will be able to run import GEOUNED from within Python + +.. code-block:: python + + import geouned + +You will also be able to use the GEOUNED command line tool + +.. code-block:: bash + + geouned_cadtocsg --help + +Checkout feature branches from dev and make local changes on you own branch + +.. code-block:: sh + + git checkout dev + git checkout -b 'my_new_feature' + +Pull requests are welcome + +.. Apt-get +.. ~~~~~~~ + +.. Snap +.. ~~~~ + +.. AppImage +.. ~~~~~~~~ + +.. Mac +.. --- + + +.. Mamba +.. ~~~~~ + +.. Conda +.. ~~~~~ + +.. Brew +.. ~~~~ + + +.. Windows +.. ------- + +.. Mamba +.. ~~~~~ + +.. Conda +.. ~~~~~ + +.. Portable FreeCAD installer +.. ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. Windows Subsystem for Linux (WSL) +.. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + diff --git a/docs/install/install_windows.rst b/docs/install/install_windows.rst new file mode 100644 index 00000000..a8199578 --- /dev/null +++ b/docs/install/install_windows.rst @@ -0,0 +1,59 @@ +Windows +======= + + +User install with Mamba (recommended) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +First we need to install a Mamba distribution. There are a few options but here we opt for Miniforge3 as it includes Mamba. + +You can follow the install instructions for `Miniforge3 here `_ or follows the commands below. + +Download and execute the Miniforge3 Windows installer + +You can get it from the Miniforge3 GitHub repository `here `_ or from the link below + +`/~https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Windows-x86_64.exe `_ + +Follow the prompts and complete the installation process + +Open "Miniforge Prompt" which will now be available on your start menu. + + +It is recommended to create a new environment + +.. code-block:: sh + + mamba create --name new_env python=3.11 + +Activate the new environment + +.. code-block:: sh + + mamba activate new_env + +We have aspirations to create a conda-forge package which will combine these final two steps, but for now FreeCAD and GEOUNED can be installed in two commands. +Install FreeCAD which is the main dependency + +.. code-block:: sh + + mamba install -c conda-forge freecad + + +Install GEOUNED with pip, we also prefix this with "python -m" to ensure that pip install uses the correct Python interpreter. + +.. code-block:: sh + + python -m pip install geouned + +Then you will be able to run import GEOUNED from within Python + +.. code-block:: python + + import geouned + +You will also be able to use the GEOUNED command line tool + +.. code-block:: bash + + geouned_cadtocsg --help diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 00000000..153be5e2 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/methodology.rst b/docs/methodology.rst new file mode 100644 index 00000000..78fc03f8 --- /dev/null +++ b/docs/methodology.rst @@ -0,0 +1,13 @@ +Methodology +=========== + + +There are several documents on the methodology of GEOUNED + +`Publication `_ + +`User_Guide_GEOUNED_v2.0.pdf `_ + +`Training_on_GEOUNED_tool `_ + +TODO convert various data sources to rst format for these docs diff --git a/docs/python_api.rst b/docs/python_api.rst new file mode 100644 index 00000000..12fa2c9e --- /dev/null +++ b/docs/python_api.rst @@ -0,0 +1,25 @@ +Python API reference +==================== + + +.. currentmodule:: geouned + +.. autoclass:: CadToCsg + :members: + :show-inheritance: + +.. autoclass:: NumericFormat + :members: + :show-inheritance: + +.. autoclass:: Options + :members: + :show-inheritance: + +.. autoclass:: Settings + :members: + :show-inheritance: + +.. autoclass:: Tolerances + :members: + :show-inheritance: diff --git a/docs/usage/index.rst b/docs/usage/index.rst new file mode 100644 index 00000000..97282c13 --- /dev/null +++ b/docs/usage/index.rst @@ -0,0 +1,14 @@ +.. usage + +Usage +===== + +GEOUNED can be used as a Python package with the API or as a command line tool with the CLI. + + +.. toctree:: + :numbered: + :maxdepth: 1 + + python_api_usage + python_cli_usage \ No newline at end of file diff --git a/docs/usage/python_api_usage.rst b/docs/usage/python_api_usage.rst new file mode 100644 index 00000000..0b59ca62 --- /dev/null +++ b/docs/usage/python_api_usage.rst @@ -0,0 +1,119 @@ +Python Package Usage +==================== + +The Python API has two main classes. +The first main class is ``CadToCsg()`` which converts CAD geometry to Constructive Solid Geometry (CSG). +There are many arguments that can be passed into the ``CadToCsg()`` class which are documented in the `Python API reference section <../python_api.html>`_ of the documentation. + + +If you have install GEOUNED and FreeCAD into your system Python then you can simply run a .py script with Python. +The most minimal use case below shows GEOUNED being imported and the CadToCsg being used to convert a STEP CAD file called 'cuboid.stp' into a vanity of CSG format. +The example makes use of default attributes. + +.. code-block:: python + + import geouned + geo = geouned.CadToCsg(stepFile='cuboid.stp') + geo.start() + geo.export_csg() + +Users can change :meth:`geouned.Options`, :meth:`geouned.Settings`, :meth:`geouned.Tolerances` and :meth:`geouned.NumericFormat` to suit the conversion desired. +The following example shows a usage with every attributes specified. + +.. code-block:: python + + import geouned + + my_options = geouned.Options( + forceCylinder=False, + newSplitPlane=True, + delLastNumber=False, + enlargeBox=2, + nPlaneReverse=0, + splitTolerance=0, + scaleUp=True, + quadricPY=False, + Facets=False, + prnt3PPlane=False, + forceNoOverlap=False, + ) + + my_settings = geouned.Settings( + matFile="", + voidGen=True, + debug=False, + compSolids=True, + simplify="no", + cellRange=[], + exportSolids="", + minVoidSize=200.0, + maxSurf=50, + maxBracket=30, + voidMat=[], + voidExclude=[], + startCell=1, + startSurf=1, + sort_enclosure=False, + ) + + my_tolerances = geouned.Tolerances( + relativeTol=False, + relativePrecision=0.000001, + value=0.000001, + distance=0.0001, + angle=0.0001, + pln_distance=0.0001, + pln_angle=0.0001, + cyl_distance=0.0001, + cyl_angle=0.0001, + sph_distance=0.0001, + kne_distance=0.0001, + kne_angle=0.0001, + tor_distance=0.0001, + tor_angle=0.0001, + min_area=0.01, + ) + my_numeric_format = geouned.NumericFormat( + P_abc="14.7e", + P_d="14.7e", + P_xyz="14.7e", + S_r="14.7e", + S_xyz="14.7e", + C_r="12f", + C_xyz="12f", + K_xyz="13.6e", + K_tan2="12f", + T_r="14.7e", + T_xyz="14.7e", + GQ_1to6="18.15f", + GQ_7to9="18.15f", + GQ_10="18.15f", + ) + + geo = geouned.CadToCsg( + stepFile="cuboid.stp, + options=my_options, + settings=my_settings, + tolerances=my_tolerances, + numeric_format=my_numeric_format, + ) + + geo.start() + + geo.export_csg( + title="Converted with GEOUNED", + geometryName="csg", + outFormat=( + "openmc_xml", + "openmc_py", + "serpent", + "phits", + "mcnp", + ), + volSDEF=True, + volCARD=False, + UCARD=None, + dummyMat=True, + cellCommentFile=False, + cellSummaryFile=False, + ) diff --git a/docs/usage/python_cli_usage.rst b/docs/usage/python_cli_usage.rst new file mode 100644 index 00000000..3db4d750 --- /dev/null +++ b/docs/usage/python_cli_usage.rst @@ -0,0 +1,115 @@ +Command Line Tool Usage +======================= + +GEOUNED can be used in the command line. + +These examples assumes you have a CAD STEP file in the current working directory of the terminal called "cuboid.stp" + +The most minimal use case below shows a minimal config.json file being used. + +First create a JSON file called "config.json" containing the following. + +.. code-block:: json + + { + "stepFile": "cuboid.stp" + } + +Then execute the command line interface tool to convert your STEP file to CSG files with the default configuration. + +.. code-block:: bash + + geouned_cadtocsg -i config.json + +The following example shows a usage with every attributes specified in the config.json file. + +The contents of the JSON file closely matches the Class arguments and method arguments when using the Python package. + +For a full description of each keyword see the `Python API reference section <../python_api.html>`_ of the documentation. + +Here is a complete JSON file specification + +.. code-block:: json + + { + "stepFile": "cuboid.stp", + "Options": { + "forceCylinder": false, + "newSplitPlane": true, + "delLastNumber": false, + "enlargeBox": 2.0, + "nPlaneReverse": 0, + "splitTolerance": 0.0, + "scaleUp": true, + "quadricPY": false, + "Facets": false, + "prnt3PPlane": false, + "forceNoOverlap": false + }, + "Tolerances": { + "relativeTol": false, + "relativePrecision": 1e-06, + "value": 1e-06, + "distance": 0.0001, + "angle": 0.0001, + "pln_distance": 0.0001, + "pln_angle": 0.0001, + "cyl_distance": 0.0001, + "cyl_angle": 0.0001, + "sph_distance": 0.0001, + "kne_distance": 0.0001, + "kne_angle": 0.0001, + "tor_distance": 0.0001, + "tor_angle": 0.0001, + "min_area": 0.01 + }, + "NumericFormat": { + "P_abc": "14.7e", + "P_d": "14.7e", + "P_xyz": "14.7e", + "S_r": "14.7e", + "S_xyz": "14.7e", + "C_r": "12f", + "C_xyz": "12f", + "K_xyz": "13.6e", + "K_tan2": "12f", + "T_r": "14.7e", + "T_xyz": "14.7e", + "GQ_1to6": "18.15f", + "GQ_7to9": "18.15f", + "GQ_10": "18.15f" + }, + "Settings": { + "matFile": "", + "voidGen": true, + "debug": false, + "compSolids": true, + "simplify": "no", + "cellRange": [], + "exportSolids": "", + "minVoidSize": 200.0, + "maxSurf": 50, + "maxBracket": 30, + "voidMat": [], + "voidExclude": [], + "startCell": 1, + "startSurf": 1, + "sort_enclosure": false + }, + "export_csg":{ + "title": "Converted with GEOUNED", + "geometryName": "csg", + "outFormat": ["openmc_xml", "openmc_py", "serpent", "phits", "mcnp"], + "volSDEF": false, + "volCARD": true, + "dummyMat": false, + "cellCommentFile": false, + "cellSummaryFile": true + } + } + +This is converted in the same way as the minimal JSON config file + +.. code-block:: bash + + geouned_cadtocsg -i config.json diff --git a/pyproject.toml b/pyproject.toml index b5ce288e..fb8ab327 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,9 +17,13 @@ readme = "README.md" requires-python = ">=3.8" classifiers = [ "Programming Language :: Python :: 3", - "License :: GNU General Public License v3.0", + "License :: OSI Approved :: European Union Public Licence 1.2 (EUPL 1.2)", "Operating System :: OS Independent", ] +dependencies = [ + "tqdm", + "numpy", +] [project.urls] Homepage = "/~https://github.com/GEOUNED-org" @@ -29,4 +33,17 @@ Documentation = "/~https://github.com/GEOUNED-org/GEOUNED/docs" [project.optional-dependencies] tests = [ "pytest", + "black==24.4.2", +] +docs = [ + "sphinx", + "sphinx_rtd_theme", + "sphinx_autodoc_typehints", + "sphinx_rtd_theme", ] + +[project.scripts] +geouned_cadtocsg = "geouned.GEOUNED.scripts.geouned_cadtocsg:main" + +[tool.black] +line-length = 128 diff --git a/scripts/config.ini b/scripts/config.ini index c8db67a7..c2bc88cd 100644 --- a/scripts/config.ini +++ b/scripts/config.ini @@ -6,8 +6,8 @@ stepFile = stepfilename.stp geometryName = pieza matFile = materials.txt -# format of the converted geometry : mcnp, openMC_XML, openMC_PY, serpent, phits -outFormat = mcnp, openMC_PY, openMC_XML +# format of the converted geometry : mcnp, openmc_xml, openmc_py, serpent, phits +outFormat = mcnp, openmc_py, openmc_xml [Parameters] @@ -62,21 +62,21 @@ cellCommentFile = True # if enclosures are defined in the CAD models, the voids cells of the enclosure are located in the mcnp outputfile # in the location where the enclosure solid is located in the CAD solid tree. -#sortEnclosure =True +#sort_enclosure =True [Tolerances] # change default tolerance values -relativeTolerance = False +relativeTol = False # define the general tolerance is the geouned conversion process -generalDistance = 1e-4 -generalAngle = 1e-4 +distance = 1e-4 +angle = 1e-4 # define the tolerance when comparing surfaces to check if there are the same surface -# keywords are : planeDistance, cylinderDistance, coneDistance, torusDistance, sphereDistance -# planeAngle, cylinderAngle, coneAngle, torusAngle -#planeDistance = 1e-2 -#planeAngle = 1e-2 +# keywords are : pln_distance, cyl_distance, kne_distance, tor_distance, sph_distance +# pln_angle, cyl_angle, kne_angle, tor_angle +#pln_distance = 1e-2 +#pln_angle = 1e-2 [MCNP_Numeric_Format] @@ -102,7 +102,6 @@ forceCylinder = False newSplitPlane = True nPlaneReverse = 10 -verbose = False splitTolerance = 0 # Use specific conversion module for geometry defined with only cells defined by planes. diff --git a/scripts/configReverse.ini b/scripts/configReverse.ini index a4992352..39555fd5 100644 --- a/scripts/configReverse.ini +++ b/scripts/configReverse.ini @@ -2,7 +2,7 @@ inputFile = my_CSG_model CADFile = modelCAD -# CSG format allowed are mcnp or openMC_XML +# CSG format allowed are mcnp or openmc_xml inFormat = mcnp # box dim in cm diff --git a/scripts/geouned b/scripts/geouned index 8de61cd4..254b4095 100644 --- a/scripts/geouned +++ b/scripts/geouned @@ -10,7 +10,7 @@ sys.path.append(geo_path) # linux distribution sys.path.append('/usr/lib64/freecad/lib64/') -import geouned +from geouned import CadToCsg from geouned.GEOReverse import reverse runReverse = False @@ -38,8 +38,8 @@ else: if not runReverse : - GEO = GEOUNED.GEOUNED(inifile) - GEO.SetOptions() + GEO = CadToCsg() + GEO.set_configuration(inifile) GEO.Start() else: diff --git a/scripts/geouned.py b/scripts/geouned.py index 3bacb556..0d588b02 100644 --- a/scripts/geouned.py +++ b/scripts/geouned.py @@ -6,12 +6,13 @@ # only if modules are not in the PYTHONPATH or directly installed in the python distribution used import sys +from geouned import CadToCsg +from geouned.GEOReverse import reverse + # geo_path="C:\\Users\\Juan\\Documents\\work\\GEOUNED\\RepoGit\\GitHub\\GEOUNEDcode\\src" # sys.path.append(geo_path) # sys.path.append('C:\\Program Files\\FreeCAD 0.19\\bin...') -import geouned -from geouned.GEOReverse import reverse runReverse = False if len(sys.argv) < 2: @@ -38,8 +39,8 @@ raise ValueError("Too many input arguments") if not runReverse: - GEO = GEOUNED.GEOUNED(inifile) - GEO.SetOptions() + GEO = CadToCsg() + GEO.set_configuration(inifile) GEO.Start() else: diff --git a/scripts/geounedClassCall.py b/scripts/geounedClassCall.py new file mode 100644 index 00000000..f7d182b4 --- /dev/null +++ b/scripts/geounedClassCall.py @@ -0,0 +1,26 @@ +#!/usr/bin/python + +# Path to GEOUNED Package + + +# only if modules are not in the PYTHONPATH or directly installed in the python distribution used +import sys + +geo_path = "C:\\Users\\Patrick\\Documents\\GitHub\\GEOUNED\\src" +sys.path.append(geo_path) +sys.path.append("C:\\Program Files\\FreeCAD 0.19\\bin...") + +from geouned import CadToCsg + +stepFileName = "placa.stp" + +GEO = CadToCsg("Conversion Example") + +GEO.set("stepFile", stepFileName) +GEO.set("geometryName", "Placa") +GEO.set("outFormat", ("mcnp", "openmc_xml")) +GEO.set("planeDistance", 0.05) +GEO.set("quadricPY", True) +GEO.set("P_abc", "12f") +GEO.set("P_d", "12f") +GEO.Start() diff --git a/src/geouned/GEOReverse/Modules/MCNPinput.py b/src/geouned/GEOReverse/Modules/MCNPinput.py index ac06e20d..60191695 100644 --- a/src/geouned/GEOReverse/Modules/MCNPinput.py +++ b/src/geouned/GEOReverse/Modules/MCNPinput.py @@ -6,12 +6,27 @@ import numpy as np from numpy import linalg as LA -from .Objects import * +from .Objects import ( + Box, + CadCell, + Cone, + Cylinder, + Ellipsoid, + EllipticCone, + EllipticCylinder, + HyperbolicCylinder, + Hyperboloid, + Paraboloid, + Plane, + Sphere, + Torus, +) from .Parser import parser as mp -from .remh import cell_card_string, remove_hash +from .remh import CellCardString, remove_hash -class MCNPinput: +# TODO rename as there are two classes with this name +class McnpInput: def __init__(self, name): if not os.path.isfile(name): raise FileNotFoundError(f"File {name} does not exist") @@ -59,7 +74,7 @@ def GetFilteredCells(self, Surfaces, config): # set cell as CAD cell Object for cname, c in universe.items(): # print(cname,c.geom.str) - universe[cname] = CADCell(c) + universe[cname] = CadCell(c) return levels, FilteredCells, newSurfaces @@ -72,7 +87,7 @@ def GetLevelStructure(self): if c.ctype != mp.CID.cell: continue c.get_values() - cstr = cell_card_string("".join(c.lines)) + cstr = CellCardString("".join(c.lines)) if cstr.TRCL: cstr.TRCL = TransformationMatrix(cstr.TRCL, self.Transformations) @@ -161,6 +176,7 @@ def __getTransList__(self): trl[c.name] = getTransMatrix(trValues, c.unit) return trl + # fmt: off def getTransMatrix(trsf, unit="", scale=10.0): @@ -184,6 +200,7 @@ def getTransMatrix(trsf, unit="", scale=10.0): coeff[2], coeff[5], coeff[8], trsf[2] * scale, 0, 0, 0, 1, ) + return trsfMat # fmt: on @@ -202,7 +219,7 @@ def substituteLikeCell(universe, Surfaces): for c in universe.values(): if not c.TRCL: continue - cellSurf = c.geom.getSurfacesNumbers() + cellSurf = c.geom.get_surfaces_numbers() surfDict = {} for surf in cellSurf: newId += 1 @@ -252,9 +269,7 @@ def selectCells(cellList, config): if c.MAT not in config["mat"][1]: selected[name] = c else: - selected[name] = ( - c # Fill cell are not tested against material number - ) + selected[name] = c # Fill cell are not tested against material number elif config["cell"][0] == "exclude": for name, c in cellList.items(): if c.FILL is None: @@ -263,9 +278,7 @@ def selectCells(cellList, config): selected[name] = c else: if name not in config["cell"][1]: - selected[name] = ( - c # Fill cell are not tested against material number - ) + selected[name] = c # Fill cell are not tested against material number elif config["cell"][0] == "include": for name, c in cellList.items(): if c.FILL is None: @@ -274,9 +287,7 @@ def selectCells(cellList, config): selected[name] = c else: if name in config["cell"][1]: - selected[name] = ( - c # Fill cell are not tested against material number - ) + selected[name] = c # Fill cell are not tested against material number # options are 'include' material elif config["mat"][0] == "include": @@ -286,9 +297,7 @@ def selectCells(cellList, config): if c.MAT in config["mat"][1]: selected[name] = c else: - selected[name] = ( - c # Fill cell are not tested against material number - ) + selected[name] = c # Fill cell are not tested against material number elif config["cell"][0] == "exclude": for name, c in cellList.items(): if c.FILL is None: @@ -297,9 +306,7 @@ def selectCells(cellList, config): selected[name] = c else: if name not in config["cell"][1]: - selected[name] = ( - c # Fill cell are not tested against material number - ) + selected[name] = c # Fill cell are not tested against material number elif config["cell"][0] == "include": for name, c in cellList.items(): if c.FILL is None: @@ -308,18 +315,14 @@ def selectCells(cellList, config): selected[name] = c else: if name in config["cell"][1]: - selected[name] = ( - c # Fill cell are not tested against material number - ) + selected[name] = c # Fill cell are not tested against material number # remove complementary in cell of the universe for cname, c in selected.items(): c.geom = remove_hash(cellList, cname) if not selected: - raise ValueError( - "No cells selected. Check input or selection criteria in config file." - ) + raise ValueError("No cells selected. Check input or selection criteria in config file.") return selected @@ -488,7 +491,7 @@ def Get_primitive_surfaces(mcnp_surfaces, scale=10.0): normal = FreeCAD.Vector(MCNPparams[0:3]) params = (normal, MCNPparams[3] * scale) else: - coeffs = pointsToCoeffs(MCNPparams[0:9]) + coeffs = points_to_coeffs(MCNPparams[0:9]) normal = FreeCAD.Vector(coeffs[0:3]) point = coeffs[3] / normal.Length normal.normalize() @@ -737,9 +740,7 @@ def Get_primitive_surfaces(mcnp_surfaces, scale=10.0): if (abs(MCNPparams[1] - MCNPparams[3])) > 1.0e-12: Stype = "cone" dblsht = False - t = (MCNPparams[3] - MCNPparams[1]) / ( - MCNPparams[2] - MCNPparams[0] - ) + t = (MCNPparams[3] - MCNPparams[1]) / (MCNPparams[2] - MCNPparams[0]) x = MCNPparams[0] - MCNPparams[1] / t if (MCNPparams[0] - x) * (MCNPparams[2] - x) > 0: p = FreeCAD.Vector(x, 0.0, 0.0) @@ -761,9 +762,7 @@ def Get_primitive_surfaces(mcnp_surfaces, scale=10.0): R *= scale params = (origin, X_vec, MCNPparams[1]) else: - print( - "not implemented surfaces defined by point with more than 2couples of value" - ) + print("not implemented surfaces defined by point with more than 2couples of value") elif MCNPtype == "Y": if len(MCNPparams) == 2: @@ -773,9 +772,7 @@ def Get_primitive_surfaces(mcnp_surfaces, scale=10.0): if (abs(MCNPparams[1] - MCNPparams[3])) > 1.0e-12: Stype = "cone" dblsht = False - t = (MCNPparams[3] - MCNPparams[1]) / ( - MCNPparams[2] - MCNPparams[0] - ) + t = (MCNPparams[3] - MCNPparams[1]) / (MCNPparams[2] - MCNPparams[0]) y = MCNPparams[0] - MCNPparams[1] / t if (MCNPparams[0] - y) * (MCNPparams[2] - y) > 0: p = FreeCAD.Vector(0.0, y, 0.0) @@ -797,9 +794,7 @@ def Get_primitive_surfaces(mcnp_surfaces, scale=10.0): R *= scale params = (origin, Y_vec, MCNPparams[1]) else: - print( - "not implemented surfaces defined by point with more than 2couples of value" - ) + print("not implemented surfaces defined by point with more than 2couples of value") elif MCNPtype == "Z": if len(MCNPparams) == 2: @@ -809,9 +804,7 @@ def Get_primitive_surfaces(mcnp_surfaces, scale=10.0): if (abs(MCNPparams[1] - MCNPparams[3])) > 1.0e-12: Stype = "cone" dblsht = False - t = (MCNPparams[3] - MCNPparams[1]) / ( - MCNPparams[2] - MCNPparams[0] - ) + t = (MCNPparams[3] - MCNPparams[1]) / (MCNPparams[2] - MCNPparams[0]) z = MCNPparams[0] - MCNPparams[1] / t if (MCNPparams[0] - z) * (MCNPparams[2] - z) > 0: p = FreeCAD.Vector(0.0, 0.0, z) @@ -833,9 +826,7 @@ def Get_primitive_surfaces(mcnp_surfaces, scale=10.0): R *= scale params = (origin, Z_vec, MCNPparams[1]) else: - print( - "not implemented surfaces defined by point with more than 2couples of value" - ) + print("not implemented surfaces defined by point with more than 2couples of value") elif MCNPtype == "BOX": Stype = "box" @@ -949,7 +940,7 @@ def Get_primitive_surfaces(mcnp_surfaces, scale=10.0): return surfaces -def pointsToCoeffs(scf): +def points_to_coeffs(scf): # mcnp implementation to convert 3 point plane to # plane parameters @@ -960,9 +951,7 @@ def pointsToCoeffs(scf): k -= 1 j -= 1 tpp[i - 1] = ( - scf[j] * (scf[k + 3] - scf[k + 6]) - + scf[j + 3] * (scf[k + 6] - scf[k]) - + scf[j + 6] * (scf[k] - scf[k + 3]) + scf[j] * (scf[k + 3] - scf[k + 6]) + scf[j + 3] * (scf[k + 6] - scf[k]) + scf[j + 6] * (scf[k] - scf[k + 3]) ) tpp[3] += scf[i - 1] * (scf[j + 3] * scf[k + 6] - scf[j + 6] * scf[k + 3]) @@ -1103,7 +1092,7 @@ def get_hyperboloid_parameters(eVal, eVect, T, k, iaxis): else: if ellipsoid: print("ellipical hyperboloid not implemented") - print("single radius from {} eigen Value will be used".format(minorRad)) + print(f"single radius from {minorRad} eigen Value will be used") return "hyperboloid", ( pos, axis, @@ -1168,9 +1157,7 @@ def getGQAxis(eVal, k): e2 = eVal[(iaxis + 2) % 3] if k == 0: # k == 0 - if ( - e0 == 0 - ): # e1*X^2 + e2*Y^2 = 0 Intersecting planes (real or imaginary) + if e0 == 0: # e1*X^2 + e2*Y^2 = 0 Intersecting planes (real or imaginary) ek = None elif np.sign(e0) == np.sign(e1) and np.sign(e1) == np.sign( e2 @@ -1181,9 +1168,7 @@ def getGQAxis(eVal, k): elif np.sign(k) == np.sign(e1): # e1 and k same sign (e1 > 0) if e0 == 0: - if np.sign(e1) == np.sign( - e2 - ): # e1*X^2 + e2*Y^2 + |k| = 0 Imaginary Elliptic cylinder + if np.sign(e1) == np.sign(e2): # e1*X^2 + e2*Y^2 + |k| = 0 Imaginary Elliptic cylinder ek = None else: # e1*X^2 - e2*Y^2 + |k| = 0 Hyperpolic cylinder ek = (0, -1) @@ -1197,9 +1182,7 @@ def getGQAxis(eVal, k): else: # e1 and k different sign if e0 == 0: # e1*X^2 + e2*Y^2 - |k| = 0 Elliptic cylinder ek = (0, -1) - elif np.sign(e0) == np.sign(e1) and np.sign(e1) == np.sign( - e2 - ): # e1*X^2 + e2*Y^2 + e0*Z^2 - |k| = 0 Ellipsoid + elif np.sign(e0) == np.sign(e1) and np.sign(e1) == np.sign(e2): # e1*X^2 + e2*Y^2 + e0*Z^2 - |k| = 0 Ellipsoid ek = (1, -1) else: # e1*X^2 + e2*Y^2 - e0*Z^2 - |k| = 0 Hyperbpoloid ek = (-1, 1) @@ -1265,12 +1248,8 @@ def gq2params(x): eVal, vect = LA.eigh(mat3) XD = np.matmul(X, vect) # X in diagonalised base - Dinv = np.where( - abs(eVal) < 1e-8, eVal, 1 / eVal - ) # get inverse eigen value where eigen< 1e-8 - zero = ( - abs(eVal) < 1e-8 - ).nonzero() # index in eigen value vector where eigen < 1e-8 + Dinv = np.where(abs(eVal) < 1e-8, eVal, 1 / eVal) # get inverse eigen value where eigen< 1e-8 + zero = (abs(eVal) < 1e-8).nonzero() # index in eigen value vector where eigen < 1e-8 TD = -XD * Dinv # Translation vector in diagonalized base k = np.matmul(TD, XD) + x[9] diff --git a/src/geouned/GEOReverse/Modules/Objects.py b/src/geouned/GEOReverse/Modules/Objects.py index f7b843ab..1339056d 100644 --- a/src/geouned/GEOReverse/Modules/Objects.py +++ b/src/geouned/GEOReverse/Modules/Objects.py @@ -5,11 +5,11 @@ import Part from .buildSolidCell import BuildSolid -from .remh import cline -from .Utils.booleanFunction import BoolSequence, outterTerms +from .remh import Cline +from .Utils.booleanFunction import BoolSequence, outer_terms -class CADCell: +class CadCell: def __init__(self, stringCell=None): if not stringCell: @@ -44,14 +44,14 @@ def __init__(self, stringCell=None): self.__setDefinition__(stringCell) def copy(self): - cpCell = CADCell() + cpCell = CadCell() cpCell.surfaceList = self.surfaceList[:] cpCell.surfaces = {} for name, s in self.surfaces.items(): cpCell.surfaces[name] = s.copy() - if type(self.definition) is cline: - cpCell.definition = cline(self.definition.str) + if type(self.definition) is Cline: + cpCell.definition = Cline(self.definition.str) elif type(self.definition) is BoolSequence: cpCell.definition = self.definition.copy() @@ -76,7 +76,7 @@ def getSubCell(self, seq): subCell = self.copy() subCell.definition = seq.copy() subCell.shape = None - subCell.surfaceList = subCell.definition.getSurfacesNumbers() + subCell.surfaceList = subCell.definition.get_surfaces_numbers() for s in tuple(subCell.surfaces.keys()): if s not in subCell.surfaceList: del subCell.surfaces[s] @@ -115,9 +115,9 @@ def getSubCell(self, seq): # subCellList=[] # for df in subDefList: # subCell = self.copy() - # subCell.definition= cline(df) + # subCell.definition= Cline(df) # subCell.shape = None - # subCell.surfaceList = subCell.definition.getSurfacesNumbers() + # subCell.surfaceList = subCell.definition.get_surfaces_numbers() # for s in tuple(subCell.surfaces.keys()) : # if s not in subCell.surfaceList: del(subCell.surfaces[s]) # @@ -127,18 +127,14 @@ def getSubCell(self, seq): def getOuterTerms(self): if not self.__defTerms__: - self.__defTerms__, self.__operator__ = outterTerms(self.definition.str) + self.__defTerms__, self.__operator__ = outer_terms(self.definition.str) return self.__defTerms__, self.__operator__ def makeBox(self, boundBox): box_origin = FreeCAD.Vector(boundBox.XMin, boundBox.YMin, boundBox.ZMin) - return Part.makeBox( - boundBox.XLength, boundBox.YLength, boundBox.ZLength, box_origin - ) + return Part.makeBox(boundBox.XLength, boundBox.YLength, boundBox.ZLength, box_origin) - def buildShape( - self, boundBox, force=False, surfTR=None, simplify=False, fuse=False - ): + def buildShape(self, boundBox, force=False, surfTR=None, simplify=False, fuse=False): if self.shape is not None and not force: return @@ -147,10 +143,12 @@ def buildShape( cutShape = BuildSolid(self, boundBox, simplify=simplify) - if fuse or True: - self.shape = FuseSolid(cutShape) - else: - self.shape = Part.makeCompound(cutShape) + # TODO consider making this step conditional on fuse + # if fuse or True: + # self.shape = FuseSolid(cutShape) + # else: + # self.shape = Part.makeCompound(cutShape) + self.shape = FuseSolid(cutShape) def buildSurfaceShape(self, boundBox): for s in self.surfaces.values(): @@ -177,7 +175,7 @@ def setSurfaces(self, Surfaces): def cleanUndefined(self): undefined = [] - for s in self.definition.getSurfacesNumbers(): + for s in self.definition.get_surfaces_numbers(): if self.surfaces[s].params is None: undefined.append(s) if undefined: @@ -193,7 +191,7 @@ def __setDefinition__(self, stringCell): self.definition.remove_cr() self.definition.remove_multispace() self.definition.remove_redundant() - self.surfaceList = self.definition.getSurfacesNumbers() + self.surfaceList = self.definition.get_surfaces_numbers() class Plane: @@ -206,7 +204,7 @@ def __init__(self, Id, params, tr=None): self.transform(tr) def __str__(self): - return "plane : {}\nParameters : {}".format(self.id, self.params) + return f"plane : {self.id}\nParameters : {self.params}" def copy(self): return Plane(self.id, self.params) @@ -518,9 +516,7 @@ def buildShape(self, boundBox): self.shape = makeEllipticCylinder(point, radii, rAxes, axis, height) else: height = axis.Length - self.shape = makeEllipticCylinder( - center, radii, rAxes, axis / height, height - ) + self.shape = makeEllipticCylinder(center, radii, rAxes, axis / height, height) class HyperbolicCylinder: @@ -613,15 +609,11 @@ def transform(self, matrix): self.params = (p, v, Ra, Rb, Rc) def buildShape(self, boundBox): - center, axis, Ra, Rb, Rc = ( - self.params - ) # Ra distance from torus axis; R radius of toroidal-cylinder + center, axis, Ra, Rb, Rc = self.params # Ra distance from torus axis; R radius of toroidal-cylinder if (abs(Rb - Rc) < 1e-5) and Ra > 0: self.shape = Part.makeTorus(Ra, Rb, center, axis) # FreeCAD circular Torus else: - self.shape = makeEllipticTorus( - Ra, Rb, Rc, center, axis - ) # Home made elliptic Torus + self.shape = makeEllipticTorus(Ra, Rb, Rc, center, axis) # Home made elliptic Torus class Box: @@ -733,15 +725,11 @@ def makeHyperboloid(center, radii, rAxes, axis, onesht, length): Y = length X = radii[1] * math.sqrt((Y / radii[0]) ** 2 + 1) - point = ( - center + X * radii[1] + Y * radii[0] - ) # point in taken as length is always counted on minor axis + point = center + X * radii[1] + Y * radii[0] # point in taken as length is always counted on minor axis parameter = abs(hyperbola.parameter(point)) if onesht: - shape = hyperbola.toBSpline(-parameter, parameter).toShape( - -parameter, parameter - ) + shape = hyperbola.toBSpline(-parameter, parameter).toShape(-parameter, parameter) hyperFace = shape.revolve(center, axis, 360) StartPoint = hyperFace.Surface.BasisCurve.StartPoint - center @@ -792,9 +780,7 @@ def makeHyperbolicCylinder(center, radii, rAxes, axis, length): Y = length X = radii[1] * math.sqrt((Y / radii[0]) ** 2 + 1) - point = ( - center + X * rAxes[1] + Y * rAxes[0] - ) # point in taken as length is always counted on minor axis + point = center + X * rAxes[1] + Y * rAxes[0] # point in taken as length is always counted on minor axis parameter = abs(hyperbola1.parameter(point)) shape1 = hyperbola1.toBSpline(-parameter, parameter).toShape(-parameter, parameter) @@ -862,9 +848,7 @@ def makeEllipticTorus(R, RZ, RX, center, ZAxis): p2 = ellipse.parameter(pz2) if p2 < p1: p2 += 2 * math.pi - shape = ellipse.toBSpline(p1, p2).toShape( - p1, p2 - ) # revolution around Major axis + shape = ellipse.toBSpline(p1, p2).toShape(p1, p2) # revolution around Major axis rev = shape.revolve(center, ZAxis, 360) else: shape = ellipse.toBSpline().toShape() # revolution around Minor axis diff --git a/src/geouned/GEOReverse/Modules/Parser/PartialFormatter.py b/src/geouned/GEOReverse/Modules/Parser/PartialFormatter.py index a087d009..105836f5 100644 --- a/src/geouned/GEOReverse/Modules/Parser/PartialFormatter.py +++ b/src/geouned/GEOReverse/Modules/Parser/PartialFormatter.py @@ -24,9 +24,7 @@ class PartialFormatter(string.Formatter): def format(*args, **kwargs): if not args: - raise TypeError( - "descriptor 'format' of 'Formatter' object " "needs an argument" - ) + raise TypeError("descriptor 'format' of 'Formatter' object " "needs an argument") self, args = args[0], args[1:] # allow the "self" keyword be passed try: format_string, args = ( @@ -37,22 +35,15 @@ def format(*args, **kwargs): if "format_string" in kwargs: format_string = kwargs.pop("format_string") else: - raise TypeError( - "format() missing 1 required positional " - "argument: 'format_string'" - ) + raise TypeError("format() missing 1 required positional " "argument: 'format_string'") return string.Formatter.vformat(self, format_string, args, SafeDict(kwargs)) - def _vformat( - self, format_string, args, kwargs, used_args, recursion_depth, auto_arg_index=0 - ): + def _vformat(self, format_string, args, kwargs, used_args, recursion_depth, auto_arg_index=0): """Clone from Python3 version to fix Python2 mess with unnamed {} format specifiers""" if recursion_depth < 0: raise ValueError("Max string recursion exceeded") result = [] - for literal_text, field_name, format_spec, conversion in self.parse( - format_string - ): + for literal_text, field_name, format_spec, conversion in self.parse(format_string): # output the literal text if literal_text: @@ -66,20 +57,12 @@ def _vformat( # handle arg indexing when empty field_names are given. if field_name == "": if auto_arg_index is False: - raise ValueError( - "cannot switch from manual field " - "specification to automatic field " - "numbering" - ) + raise ValueError("cannot switch from manual field " "specification to automatic field " "numbering") field_name = str(auto_arg_index) auto_arg_index += 1 elif field_name.isdigit(): if auto_arg_index: - raise ValueError( - "cannot switch from manual field " - "specification to automatic field " - "numbering" - ) + raise ValueError("cannot switch from manual field " "specification to automatic field " "numbering") # disable auto arg incrementing, if it gets # used later on, then an exception will be raised auto_arg_index = False diff --git a/src/geouned/GEOReverse/Modules/Parser/parser.py b/src/geouned/GEOReverse/Modules/Parser/parser.py index 927c98b0..c4e13c51 100644 --- a/src/geouned/GEOReverse/Modules/Parser/parser.py +++ b/src/geouned/GEOReverse/Modules/Parser/parser.py @@ -4,23 +4,11 @@ Functions for parsing MCNP input files. """ -import os import re import warnings from .PartialFormatter import PartialFormatter -version = "3.6" - -try: - # This clause define the fallback for cPickle, which is an accelerated - # version of pickle in Python2. In Python3 the acceleration is considered - # to be package-internal details, therefore the whole clause is an overkill - # -- an accelerated version will be imported with pickle, if available. - import cPickle -except ImportError: - import pickle as cPickle - # integer with one prefix character re_int = re.compile(r"\D{0,1}\d+") @@ -36,14 +24,12 @@ re_prm = re.compile(r"([it]mp:*[npe]*[=\s]+)(\S+)", flags=re.IGNORECASE) # fill keyword -re_fll = re.compile( - r"\*{0,1}fill[=\s]+", flags=re.IGNORECASE -) # TODO: this will also match fill=== +re_fll = re.compile(r"\*{0,1}fill[=\s]+", flags=re.IGNORECASE) # TODO: this will also match fill=== # If type specifier not given, any data type can be formatted: def fmt_gen(s): - return "{" + ":<{}".format(len(s)) + "}" + return "{" + f":<{len(s)}" + "}" fmt_d = fmt_gen @@ -53,7 +39,7 @@ def fmt_gen(s): partial_formmatter = PartialFormatter() -class __CIDClass(object): +class CidClass(object): """ There are two levels of card types. 1-st level is purely defined by card position in the input file. There can be: @@ -95,7 +81,7 @@ def get_name(cls, cid): raise ValueError() -CID = __CIDClass() +CID = CidClass() class Card(object): @@ -204,9 +190,7 @@ def print_debug(self, comment, key="tihv"): d = self.debug if d: print( - "Line {}, {} card. {}".format( - self.pos, CID.get_name(self.ctype), comment - ), + f"Line {self.pos}, {CID.get_name(self.ctype)} card. {comment}", file=d, ) if "t" in key: @@ -231,9 +215,7 @@ def get_input(self, check_bad_chars=False): if self.debug: self.print_debug("get_input: bad char in input cards", "") else: - raise ValueError( - "Bad character in input file. " + "Run with --debug option." - ) + raise ValueError("Bad character in input file. " + "Run with --debug option.") if self.ctype in (CID.comment, CID.blankline): # nothing to do for comments or blanklines: @@ -322,11 +304,7 @@ def _protect_nums(self): inpt = inpt.replace(s, "!", 1) d["!"] = sbl - if ( - self.ctype == CID.data - and inpt.lstrip().lower()[0] == "f" - and inpt.lstrip()[1].isdigit() - ): + if self.ctype == CID.data and inpt.lstrip().lower()[0] == "f" and inpt.lstrip()[1].isdigit(): # this is tally card. Hide indexes in square brackets sbl = re_ind.findall(inpt) if sbl: @@ -662,16 +640,12 @@ def card(self, wrap=False, comment=True): il.append(i[:k]) tl.append("\n") i = indent + i[k:] - self.print_debug( - "card wrap=True" + repr(il[-1]) + repr(i), "" - ) + self.print_debug("card wrap=True" + repr(il[-1]) + repr(i), "") break else: # there is no proper place to wrap. self.print_debug("Cannot wrap line " + repr(i), "") - warnings.warn( - "Cannot wrap card" " on line {}".format(self.pos) - ) + warnings.warn(f"Cannot wrap card on line {self.pos}") break else: # input i fits to one line. Do nothing. @@ -989,9 +963,7 @@ def _split_cell(input_, self): # add all strings, values, formats and types: for vs, vv, vf, vt in zip(vsl, vvl, vfl, vtl): - inpt_parm = inpt_parm.replace( - vs, tp, 1 - ) # TODO: here only parm part of inpt should be modified. + inpt_parm = inpt_parm.replace(vs, tp, 1) # TODO: here only parm part of inpt should be modified. vals.append((vv, vt)) fmts.append(vf) @@ -1104,12 +1076,7 @@ def _split_data(input_): inpt = inpt.replace(ns, tp, 1) vals.append((int(ns), "tr")) fmts.append(fmt_d(ns)) - elif ( - t[0][0].lower() == "m" - and "mode" not in t[0].lower() - and "mesh" not in t[0].lower() - and "mphys" not in t[0].lower() - ): + elif t[0][0].lower() == "m" and "mode" not in t[0].lower() and "mesh" not in t[0].lower() and "mphys" not in t[0].lower(): # This is the Mn, MTn or MPNn card ms = _get_int(t[0]) inpt = inpt.replace(ms, tp, 1) @@ -1215,64 +1182,21 @@ def is_blankline(l): return l.strip() == "" -if version == "2.7": - - def get_cards(inp, debug=None, preservetabs=False): - """ - Check first existence of a dump file - - If dump exists and it is newwer than the input file, read the dump file - """ - from os import stat - - iname = inp - dname = ".{}.~".format(os.path.basename(inp)) - try: - it = stat(iname).st_mtime - except OSError as e: - raise e - - try: - dt = stat(dname).st_mtime - except OSError: - # print('No dump file exists') - dt = it - 1.0 - if it < dt and debug is None: - # print('Reading from dump') - # dump is youger - dfile = open(dname, "r") - cl = cPickle.load(dfile) - for c in cl: - yield c - else: - # print('Reading from input') - cl = [] - for c in get_cards_from_input(inp, debug=debug, preservetabs=preservetabs): - yield c - cl.append(c) - if debug is None: - # otherwise the instances of c contain the file object, which - # cannot be dumped. - dfile = open(dname, "w") - cPickle.dump(cl, dfile) - -else: - - def get_cards(inp, debug=None, preservetabs=False): - """ - Check first existence of a dump file +def get_cards(inp, debug=None, preservetabs=False): + """ + Check first existence of a dump file - If dump exists and it is newwer than the input file, read the dump file - """ - for c in get_cards_from_input(inp, debug=debug, preservetabs=preservetabs): - yield c + If dump exists and it is newwer than the input file, read the dump file + """ + for c in get_cards_from_input(inp, debug=debug, preservetabs=preservetabs): + yield c def index_(line, chars="$&"): """ Find the first index of one of the chars in line. """ - r = re.compile("[{}]".format(chars)) + r = re.compile(f"[{chars}]") m = r.search(line) if m: i = m.end() - 1 @@ -1303,7 +1227,7 @@ def replace_tab(l, cln, preserve=False, ts=8): while "\t" in l: i = l.index("\t") ii = (i // ts + 1) * ts - i - print("c Line {}: tab replaced with {} spaces".format(cln + 1, ii)) + print(f"c Line {cln + 1}: tab replaced with {ii} spaces") l = l[:i] + " " * ii + l[i + 1 :] return l[:] @@ -1505,7 +1429,7 @@ def are_close_lists(x, y, re=1e-6, pci=[]): for xx, yy in zip(xl, yl): r = are_close_vals(xx, yy, re) if not r: - m = "diff at {}".format(n) + m = f"diff at {n}" break else: m = "all elements are close or equal" diff --git a/src/geouned/GEOReverse/Modules/Utils/BooleanSolids.py b/src/geouned/GEOReverse/Modules/Utils/BooleanSolids.py index 24ce1bed..45125712 100644 --- a/src/geouned/GEOReverse/Modules/Utils/BooleanSolids.py +++ b/src/geouned/GEOReverse/Modules/Utils/BooleanSolids.py @@ -27,14 +27,12 @@ def __init__(self, val=None, S1=None, S2=None): self.type = val.count(0) self.val = val - def getTranspose(self): + def get_transpose(self): if self.diagonal: return self.val - return CTelement( - (self.val[0], self.val[3], self.val[2], self.val[1]), self.S2, self.S1 - ) + return CTelement((self.val[0], self.val[3], self.val[2], self.val[1]), self.S2, self.S1) - def getDependence(self): + def get_dependence(self): if self.diagonal: if self.val == 0: return True, False @@ -95,70 +93,70 @@ def __str__(self): line = "" for name in varName: element = self[name][name] - line += " {:4d} : {}\n".format(name, element.val) + line += f" {name:4d} : {element.val}\n" return line outstr = " " for name in varName: - outstr = outstr + " {:3d}".format(name) + outstr = outstr + f" {name:3d}" outstr = outstr + "\n" for name1 in varName: - line = " {:3d} ".format(name1) - linenot = "~{:3d} ".format(name1) + line = f" {name1:3d} " + linenot = f"~{name1:3d} " for name2 in varName: elmt = self[name1][name2] if elmt.diagonal: - line += " {:>2d} ".format(elmt.val) + line += f" {elmt.val:>2d} " linenot += " " else: - line += " {}{} ".format(elmt.val[0], elmt.val[1]) - linenot += " {}{} ".format(elmt.val[3], elmt.val[2]) + line += f" {elmt.val[0]}{elmt.val[1]} " + linenot += f" {elmt.val[3]}{elmt.val[2]} " outstr += line + "\n" outstr += linenot + "\n" return outstr - def addElement(self, k1, k2, val): + def add_element(self, k1, k2, val): if k1 in self.keys(): self[k1][k2] = val else: self[k1] = {k2: val} - def fillMissingElements(self): + def fill_missing_elements(self): keys = list(self.keys()) missing = [] for i, k1 in enumerate(keys): for k2 in keys[i + 1 :]: if k2 in self[k1].keys(): elmt = self[k1][k2] - self[k2][k1] = elmt.getTranspose() + self[k2][k1] = elmt.get_transpose() else: missing.append((k1, k2)) for k1, k2 in missing: diag1 = self[k1][k1] diag2 = self[k2][k2] - new = combineDiagElements(diag1, diag2) + new = combine_diag_elements(diag1, diag2) new.S1 = k1 new.S2 = k2 self[k1][k2] = new - self[k2][k1] = new.getTranspose() + self[k2][k1] = new.get_transpose() - def getOutSurfaces(self): + def get_out_surfaces(self): out = [] for k in self.keys(): if self[k][k].val != 0: out.append(k) return out - def getConstraintSet(self, valname): + def get_constraint_set(self, valname): trueSet = {} falseSet = {} TNull = False FNull = False for k in self.keys(): - TValue, FValue = self[valname][k].getDependence() + TValue, FValue = self[valname][k].get_dependence() if TValue == "Null": TNull = True TValue = None @@ -176,8 +174,8 @@ def getConstraintSet(self, valname): falseSet = None return trueSet, falseSet - def solidInBox(self, Seq): # Sequence of the cell - surfs = Seq.getSurfacesNumbers() + def solid_in_box(self, Seq): # Sequence of the cell + surfs = Seq.get_surfaces_numbers() if self.diagonal: seqValues = dict() for s in surfs: @@ -188,7 +186,7 @@ def solidInBox(self, Seq): # Sequence of the cell return Seq.evaluate(seqValues) else: - trueSet, falseSet = self.getConstraintSet(surfs[0]) + trueSet, falseSet = self.get_constraint_set(surfs[0]) if trueSet is not None: trueVal = Seq.evaluate(trueSet) if trueVal is None: @@ -211,7 +209,7 @@ def solidInBox(self, Seq): # Sequence of the cell return False -def combineDiagElements(d1, d2): +def combine_diag_elements(d1, d2): if d1.val == 0 and d2.val == 0: return CTelement((1, 1, 1, 1)) @@ -233,7 +231,7 @@ def combineDiagElements(d1, d2): return CTelement((0, 0, 1, 0)) -def buildCTableFromSolids(Box, SurfInfo, option="diag"): +def build_c_table_from_solids(Box, SurfInfo, option="diag"): if type(SurfInfo) is dict: surfaces = SurfInfo @@ -254,10 +252,10 @@ def buildCTableFromSolids(Box, SurfInfo, option="diag"): CTable.diagonal = False for i, s1 in enumerate(surfaceList): - res, splitRegions = splitSolid_fast(Box, surfaces[s1], True) - # res,splitRegions = splitSolid_fast(Box,Surfaces.getSurface(s1),True) + res, splitRegions = split_solid_fast(Box, surfaces[s1], True) + # res,splitRegions = split_solid_fast(Box,Surfaces.get_surface(s1),True) - CTable.addElement(s1, s1, CTelement(res, s1, s1)) + CTable.add_element(s1, s1, CTelement(res, s1, s1)) if option == "diag": continue if splitRegions is None: @@ -268,9 +266,9 @@ def buildCTableFromSolids(Box, SurfInfo, option="diag"): pos0 = None for solid in posS1: - pos = splitSolid_fast(solid, surfaces[s2], False) + pos = split_solid_fast(solid, surfaces[s2], False) - # pos = splitSolid_fast(solid,Surfaces.getSurface(s2),False) + # pos = split_solid_fast(solid,Surfaces.get_surface(s2),False) if pos == (1, 1): break # s2 intersect S1 Region if pos0 is None: @@ -282,8 +280,8 @@ def buildCTableFromSolids(Box, SurfInfo, option="diag"): neg0 = None for solid in negS1: - # neg = splitSolid_fast(solid,Surfaces.getSurface(s2),False) - neg = splitSolid_fast(solid, surfaces[s2], False) + # neg = split_solid_fast(solid,Surfaces.get_surface(s2),False) + neg = split_solid_fast(solid, surfaces[s2], False) if neg == (1, 1): break # s2 intersect S1 Region if neg0 is None: @@ -294,17 +292,17 @@ def buildCTableFromSolids(Box, SurfInfo, option="diag"): break val = (pos[0], pos[1], neg[1], neg[0]) - CTable.addElement(s1, s2, CTelement(val, s1, s2)) + CTable.add_element(s1, s2, CTelement(val, s1, s2)) # if some surfaces don't cross the box some elements in Constraint table are not filled if option != "diag": - CTable.fillMissingElements() + CTable.fill_missing_elements() return CTable -def removeExtraSurfaces(CellSeq, CTable): +def remove_extra_surfaces(CellSeq, CTable): # checking is make on solid cell definition to be removed from void cell - outSurfaces = set(CTable.getOutSurfaces()) + outSurfaces = set(CTable.get_out_surfaces()) newDef = BoolSequence(operator="OR") # Loop over all compound solids of the metaSolid @@ -317,26 +315,26 @@ def removeExtraSurfaces(CellSeq, CTable): nullcell = False chk = subCell.check() - if chk == False: # the cell doesn't exist + if chk is False: # the cell doesn't exist nullcell = True - elif chk == True: # the cell describe the full universe + elif chk is True: # the cell describe the full universe newDef.elements = True newDef.level = 0 return newDef # if subcell has finite volume check it intersection with the box if not nullcell: - res = CTable.solidInBox(subCell) + res = CTable.solid_in_box(subCell) if res == None: # subcell intersect the box # get the surfaces of the solids out of the box # get reduced definition - removeSurf = outSurfaces & set(subCell.getSurfacesNumbers()) + removeSurf = outSurfaces & set(subCell.get_surfaces_numbers()) for s in removeSurf: val = True if CTable[s][s].val > 0 else False subCell.substitute(s, val) if type(subCell.elements) is bool: - if subCell.elements == False: # cell does not intersect void box + if subCell.elements is False: # cell does not intersect void box continue else: # cell cover fully void box newDef.elements = True @@ -345,7 +343,7 @@ def removeExtraSurfaces(CellSeq, CTable): else: newDef.append(subCell) - elif res == True: + elif res is True: # subcell cover the full box region Void cell doesn't exist newDef.elements = True newDef.level = 0 @@ -356,19 +354,17 @@ def removeExtraSurfaces(CellSeq, CTable): return newDef -def splitSolid_fast(solid, surf, box): +def split_solid_fast(solid, surf, box): if box: if surf.shape: - comsolid = BOPTools.SplitAPI.slice( - solid, [surf.shape], "Split", tolerance=0 - ) + comsolid = BOPTools.SplitAPI.slice(solid, [surf.shape], "Split", tolerance=0) else: - return checkSign(solid, surf), None + return check_sign(solid, surf), None if len(comsolid.Solids) <= 1: - return checkSign(solid, surf), None - # sgn = checkSign(solid,surf) # if "box" and single object => the box is not split, surface s1 out of the box. + return check_sign(solid, surf), None + # sgn = check_sign(solid,surf) # if "box" and single object => the box is not split, surface s1 out of the box. # if sgn == 1 : # The sign is the side of surface s1 where the box is located # # return ((1,0),(0,0)),None # return the diagonal element of the Constraint Table for s1 # return ((1,0),(0,0)),None # return the diagonal element of the Constraint Table for s1 @@ -379,7 +375,7 @@ def splitSolid_fast(solid, surf, box): posSol = [] negSol = [] for s in comsolid.Solids: - sgn = checkSign(s, surf) + sgn = check_sign(s, surf) if sgn == 1: posSol.append(s) else: @@ -397,7 +393,7 @@ def splitSolid_fast(solid, surf, box): else: dist = 1.0 if dist > 1e-6: # face doesn't intersect solid - sgn = checkSign(solid, surf) + sgn = check_sign(solid, surf) if sgn == 1: return ( 1, @@ -438,7 +434,7 @@ def point_inside(solid): BBox = solid.optimalBoundingBox(False) box = [BBox.XMin, BBox.XMax, BBox.YMin, BBox.YMax, BBox.ZMin, BBox.ZMax] - boxes, centers = CutBox(box) + boxes, centers = cut_box(box) n = 0 while True: @@ -450,19 +446,19 @@ def point_inside(solid): subbox = [] centers = [] for b in boxes: - btab, ctab = CutBox(b) + btab, ctab = cut_box(b) subbox.extend(btab) centers.extend(ctab) boxes = subbox n = n + 1 if n == cut_box: - print("Solid not found in bounding Box (Volume : {})".format(solid.Volume)) + print(f"Solid not found in bounding Box (Volume : {solid.Volume})") return None # divide a box into 8 smaller boxes -def CutBox(Box): +def cut_box(Box): xmid = (Box[1] + Box[0]) * 0.5 ymid = (Box[3] + Box[2]) * 0.5 zmid = (Box[5] + Box[4]) * 0.5 @@ -494,7 +490,7 @@ def CutBox(Box): return (b1, b2, b3, b4, b5, b6, b7, b8), (p1, p2, p3, p4, p5, p6, p7, p8) -def checkSign(solid, surf): +def check_sign(solid, surf): point = point_inside(solid) diff --git a/src/geouned/GEOReverse/Modules/Utils/booleanFunction.py b/src/geouned/GEOReverse/Modules/Utils/booleanFunction.py index e9382230..464704df 100644 --- a/src/geouned/GEOReverse/Modules/Utils/booleanFunction.py +++ b/src/geouned/GEOReverse/Modules/Utils/booleanFunction.py @@ -13,19 +13,19 @@ class BoolSequence: def __init__(self, definition=None, operator=None): if definition: self.elements = [] - self.setDef(definition) + self.set_def(definition) else: self.elements = [] self.operator = operator self.level = 0 def __str__(self): - out = "{}[".format(self.operator) + out = f"{self.operator}[" if type(self.elements) is bool: return " True " if self.elements else " False " for e in self.elements: if type(e) is int or type(e) is bool or type(e) is str: - out += " {} ".format(e) + out += f" {e} " else: out += e.__str__() @@ -39,12 +39,7 @@ def append(self, *seq): else: level = s.level if type(s.elements) is bool: - if ( - self.operator == "AND" - and s.elements == False - or self.operator == "OR" - and s.elements == True - ): + if self.operator == "AND" and s.elements is False or self.operator == "OR" and s.elements is True: self.level = 0 self.elements = s.elements return @@ -73,9 +68,9 @@ def copy(self): cp.elements.append(e.copy()) return cp - def getComplementary(self): + def get_complementary(self): - c = BoolSequence(operator=self.compOperator()) + c = BoolSequence(operator=self.comp_operator()) c.level = self.level if self.level == 0: @@ -83,19 +78,19 @@ def getComplementary(self): c.elements.append(-e) return c else: - self.groupSingle() + self.group_single() for e in self.elements: - c.elements.append(e.getComplementary()) + c.elements.append(e.get_complementary()) return c - def compOperator(self): + def comp_operator(self): if self.operator == "AND": return "OR" else: return "AND" def simplify(self, CT, loop=0): - surfNames = self.getSurfacesNumbers() + surfNames = self.get_surfaces_numbers() if not surfNames: return # print(CT) @@ -107,8 +102,8 @@ def simplify(self, CT, loop=0): if valname in newNames: chg = self.factorize(valname, CT) simplified = simplified or chg - newNames = self.getSurfacesNumbers() - self.joinOperators() + newNames = self.get_surfaces_numbers() + self.join_operators() if self.level == 0 or (self.elements) is bool: return @@ -136,18 +131,13 @@ def simplify(self, CT, loop=0): newSeq.append(ANDSeq) if ORSeq.elements: newSeq.append(ORSeq) - newSeq.joinOperators() + newSeq.join_operators() self.assign(newSeq) else: for e in reversed(self.elements): e.simplify(CT, loop=0) if type(e.elements) is bool: - if ( - self.operator == "AND" - and e.elements == False - or self.operator == "OR" - and e.elements == True - ): + if self.operator == "AND" and e.elements is False or self.operator == "OR" and e.elements is True: self.elements = e.elements self.level = 0 break @@ -169,7 +159,7 @@ def check(self): return self.elements signedSurf = set(self.elements) - surfname = self.getSurfacesNumbers() + surfname = self.get_surfaces_numbers() if len(signedSurf) == len(surfname): return None # means same surface has not positive and negative value elif self.operator == "AND": @@ -180,15 +170,15 @@ def check(self): if type(self.elements) is bool: return self.elements - self.groupSingle() + self.group_single() noneVal = False for e in self.elements: res = e.check() if res is None: noneVal = True - elif self.operator == "AND" and res == False: + elif self.operator == "AND" and res is False: return False - elif self.operator == "OR" and res == True: + elif self.operator == "OR" and res is True: return True if noneVal: @@ -233,7 +223,7 @@ def substitute(self, var, val): e.substitute(var, val) self.clean() - self.levelUpdate() + self.level_update() def removeSurf(self, name): if type(self.elements) is bool: @@ -253,7 +243,7 @@ def removeSurf(self, name): e.removeSurf(name) self.clean() - self.levelUpdate() + self.level_update() # remove sequence whom elements are boolean values instead of list def clean(self): @@ -285,12 +275,12 @@ def clean(self): return None # join redundant operators in sequence - def joinOperators(self): + def join_operators(self): if self.level == 0: return if type(self.elements) is bool: return - self.groupSingle() + self.group_single() ANDop = [] ORop = [] @@ -305,7 +295,7 @@ def joinOperators(self): for s in ANDop: newSeq.elements.extend(s.elements) self.elements.remove(s) - newSeq.levelUpdate() + newSeq.level_update() self.append(newSeq) elif len(ORop) > 1 and self.operator == "OR": @@ -313,19 +303,19 @@ def joinOperators(self): for s in ORop: newSeq.elements.extend(s.elements) self.elements.remove(s) - newSeq.levelUpdate() + newSeq.level_update() self.append(newSeq) if self.level > 0 and len(self.elements) == 1: self.operator = self.elements[0].operator self.elements = self.elements[0].elements self.level -= 1 - self.joinOperators() + self.join_operators() if self.level == 0: return for e in self.elements: - e.joinOperators() + e.join_operators() def factorize(self, valname, CT=None): @@ -333,7 +323,7 @@ def factorize(self, valname, CT=None): trueSet = {abs(valname): True} falseSet = {abs(valname): False} else: - trueSet, falseSet = CT.getConstraintSet(valname) + trueSet, falseSet = CT.get_constraint_set(valname) if trueSet is None: self.substitute(valname, False) @@ -344,7 +334,7 @@ def factorize(self, valname, CT=None): return True funcVal = self.evaluate(trueSet, CT) - if funcVal == False: + if funcVal is False: newSeq = BoolSequence(operator="AND") # self.substitute(valname,False) for name, value in falseSet.items(): @@ -352,7 +342,7 @@ def factorize(self, valname, CT=None): newSeq.append(-valname, self.copy()) self.assign(newSeq) return True - elif funcVal == True: + elif funcVal is True: newSeq = BoolSequence(operator="OR") # self.substitute(valname,False) for name, value in falseSet.items(): @@ -362,7 +352,7 @@ def factorize(self, valname, CT=None): return True funcVal = self.evaluate(falseSet, CT) - if funcVal == False: + if funcVal is False: newSeq = BoolSequence(operator="AND") # self.substitute(valname,True) for name, value in trueSet.items(): @@ -370,7 +360,7 @@ def factorize(self, valname, CT=None): newSeq.append(valname, self.copy()) self.assign(newSeq) return True - elif funcVal == True: + elif funcVal is True: newSeq = BoolSequence(operator="OR") # self.substitute(valname,True) for name, value in trueSet.items(): @@ -385,7 +375,7 @@ def evaluate_newbad(self, valueSet, CT=None): if type(self.elements) is bool: return self.elements - self.groupSingle() + self.group_single() newSeq = self.copy() for name, value in valueSet.items(): newSeq.substitute(name, value) @@ -393,13 +383,13 @@ def evaluate_newbad(self, valueSet, CT=None): if type(newSeq.elements) is bool: return newSeq.elements else: - surfNames = newSeq.getSurfacesNumbers() + surfNames = newSeq.get_surfaces_numbers() valname = surfNames[0] if CT is None: trueSet = {abs(valname): True} falseSet = {abs(valname): False} else: - trueSet, falseSet = CT.getConstraintSet(valname) + trueSet, falseSet = CT.get_constraint_set(valname) if trueSet is None: trueVal = True @@ -423,7 +413,7 @@ def evaluate_newbad(self, valueSet, CT=None): def evaluate(self, valueSet, CT=None): if type(self.elements) is bool: return self.elements - self.groupSingle() + self.group_single() op = self.operator noneVal = False for e in self.elements: @@ -444,9 +434,9 @@ def evaluate(self, valueSet, CT=None): if val is None: noneVal = True - elif op == "AND" and val == False: + elif op == "AND" and val is False: return False - elif op == "OR" and val == True: + elif op == "OR" and val is True: return True if noneVal: @@ -456,20 +446,20 @@ def evaluate(self, valueSet, CT=None): else: return False - def setDef(self, expression): - terms, operator = outterTerms(expression) + def set_def(self, expression): + terms, operator = outer_terms(expression) self.operator = operator self.level = 0 for t in terms: - if isInteger(t): + if is_integer(t): self.elements.append(int(t.strip("(").strip(")"))) else: x = BoolSequence(t) self.level = max(x.level + 1, self.level) self.elements.append(x) - self.groupSingle() + self.group_single() - def groupSingle(self): + def group_single(self): if self.level == 0: return if type(self.elements) is bool: @@ -511,11 +501,11 @@ def sameOperator(self): for i in reversed(changed): del self.elements[i] self.append(*new) - self.levelUpdate() + self.level_update() return - def getSurfacesNumbers(self): + def get_surfaces_numbers(self): if type(self.elements) is bool: return tuple() surf = set() @@ -523,10 +513,10 @@ def getSurfacesNumbers(self): if type(e) is int: surf.add(abs(e)) else: - surf.update(e.getSurfacesNumbers()) + surf.update(e.get_surfaces_numbers()) return tuple(surf) - def levelUpdate(self): + def level_update(self): if type(self.elements) is bool: self.level = 0 return @@ -541,7 +531,7 @@ def levelUpdate(self): self.level = newlev -def outterTerms(expression, value="number"): +def outer_terms(expression, value="number"): if value == "number": # reValue = number reValue = mix @@ -566,13 +556,7 @@ def outterTerms(expression, value="number"): cont = True if redundant(m, expr): # remove redundant parentheses - expr = ( - expr[: m.start()] - + " " - + expr[m.start() + 1 : m.end() - 1] - + " " - + expr[m.end() :] - ) + expr = expr[: m.start()] + " " + expr[m.start() + 1 : m.end() - 1] + " " + expr[m.end() :] else: # replace no redundant parentheses by 0 and : by ; zeros = "[" + nullVal * (m.end() - m.start() - 2) + "]" @@ -644,7 +628,7 @@ def redundant(m, geom): return False -def isInteger(x): +def is_integer(x): try: int(x.strip("(").strip(")")) return True diff --git a/src/geouned/GEOReverse/Modules/XMLParser.py b/src/geouned/GEOReverse/Modules/XMLParser.py index e2b9e7f5..8443eb0b 100644 --- a/src/geouned/GEOReverse/Modules/XMLParser.py +++ b/src/geouned/GEOReverse/Modules/XMLParser.py @@ -1,7 +1,7 @@ -from .remh import cline +from .remh import Cline -class cellCARD: +class CellCard: def __init__(self, data): self.type = "cell" @@ -27,10 +27,10 @@ def processData(self, data): else: self.FILL = None - self.geom = cline(data["region"].replace("|", ":")) + self.geom = Cline(data["region"].replace("|", ":")) -class surfCARD: +class SurfCard: def __init__(self, data): self.type = "surface" @@ -50,7 +50,7 @@ def get_cards(root): def process_card(card): ctype = card.tag if ctype == "cell": - return cellCARD(card.attrib) + return CellCard(card.attrib) elif ctype == "surface": - return surfCARD(card.attrib) + return SurfCard(card.attrib) diff --git a/src/geouned/GEOReverse/Modules/XMLinput.py b/src/geouned/GEOReverse/Modules/XMLinput.py index 8b22a5fb..9585c7c4 100644 --- a/src/geouned/GEOReverse/Modules/XMLinput.py +++ b/src/geouned/GEOReverse/Modules/XMLinput.py @@ -7,11 +7,11 @@ import numpy as np from numpy import linalg as LA -from .Objects import * +from .Objects import CadCell, Cone, Cylinder, Plane, Sphere, Torus from .XMLParser import get_cards -class XMLinput: +class XmlInput: def __init__(self, name): if not os.path.isfile(name): raise FileNotFoundError(f"File {name} does not exist") @@ -61,7 +61,7 @@ def GetFilteredCells(self, Surfaces, config): # set cell as CAD cell Object for cname, c in universe.items(): # print(cname,c.geom.str) - universe[cname] = CADCell(c) + universe[cname] = CadCell(c) return levels, FilteredCells, newSurfaces @@ -157,9 +157,7 @@ def selectCells(cellList, config): if c.MAT not in config["mat"][1]: selected[name] = c else: - selected[name] = ( - c # Fill cell are not tested against material number - ) + selected[name] = c # Fill cell are not tested against material number elif config["cell"][0] == "exclude": for name, c in cellList.items(): if c.FILL is None: @@ -168,9 +166,7 @@ def selectCells(cellList, config): selected[name] = c else: if name not in config["cell"][1]: - selected[name] = ( - c # Fill cell are not tested against material number - ) + selected[name] = c # Fill cell are not tested against material number elif config["cell"][0] == "include": for name, c in cellList.items(): if c.FILL is None: @@ -179,9 +175,7 @@ def selectCells(cellList, config): selected[name] = c else: if name in config["cell"][1]: - selected[name] = ( - c # Fill cell are not tested against material number - ) + selected[name] = c # Fill cell are not tested against material number # options are 'include' material elif config["mat"][0] == "include": @@ -191,9 +185,7 @@ def selectCells(cellList, config): if c.MAT in config["mat"][1]: selected[name] = c else: - selected[name] = ( - c # Fill cell are not tested against material number - ) + selected[name] = c # Fill cell are not tested against material number elif config["cell"][0] == "exclude": for c in cellList: if c.FILL is None: @@ -202,9 +194,7 @@ def selectCells(cellList, config): selected[name] = c else: if name not in config["cell"][1]: - selected[name] = ( - c # Fill cell are not tested against material number - ) + selected[name] = c # Fill cell are not tested against material number elif config["cell"][0] == "include": for name, c in cellList.items(): if c.FILL is None: @@ -213,18 +203,14 @@ def selectCells(cellList, config): selected[name] = c else: if name in config["cell"][1]: - selected[name] = ( - c # Fill cell are not tested against material number - ) + selected[name] = c # Fill cell are not tested against material number # remove complementary in cell of the universe # for cname,c in selected.items() : # c.geom = remove_hash(cellList,cname) if not selected: - raise ValueError( - "No cells selected. Check input or selection criteria in config file." - ) + raise ValueError("No cells selected. Check input or selection criteria in config file.") return selected diff --git a/src/geouned/GEOReverse/Modules/buildCAD.py b/src/geouned/GEOReverse/Modules/buildCAD.py index f0064ccd..79dba240 100644 --- a/src/geouned/GEOReverse/Modules/buildCAD.py +++ b/src/geouned/GEOReverse/Modules/buildCAD.py @@ -20,9 +20,7 @@ def buildCAD(UnivCell, data, config): else: factor = 1 - modelSurfaces = data.GetSurfaces( - scale=factor - ) # scale change cm in mcnp to mm in CAD Obj + modelSurfaces = data.GetSurfaces(scale=factor) # scale change cm in mcnp to mm in CAD Obj # read Cells and group into universes print(config) @@ -33,7 +31,7 @@ def buildCAD(UnivCell, data, config): # print(UniverseCells[0][120].definition.str) # print(UniverseCells[0][120].surfaces) - # CT=buildCTableFromSolids(UnivCell.shape,UniverseCells[0][70],option='full') + # CT=build_c_table_from_solids(UnivCell.shape,UniverseCells[0][70],option='full') # print(CT) # simply = BoolSequence(UniverseCells[0][70].definition.str) # print('antesSimply',simply) @@ -42,7 +40,7 @@ def buildCAD(UnivCell, data, config): # exit() # dictionnary of the cells filled with a given Universe U - # universeContainers = GetUniverseContainers(levels,UniverseCells) + # universeContainers = get_universe_containers(levels,UniverseCells) UnivCell.level = None levelMax = config["levelMax"] @@ -86,7 +84,7 @@ def AssignSurfaceToCell(UniverseCells, modelSurfaces): c.setSurfaces(modelSurfaces) -def GetUniverseContainers(levels, Universes): +def get_universe_containers(levels, Universes): Ucontainer = {} for lev in range(1, len(levels)): for U, name in levels[lev]: @@ -98,19 +96,13 @@ def GetUniverseContainers(levels, Universes): return Ucontainer -def BuildUniverse( - startInfo, ContainerCell, AllUniverses, universeCut=True, duplicate=False -): +def BuildUniverse(startInfo, ContainerCell, AllUniverses, universeCut=True, duplicate=False): CADUniverse = [] Ustart, levelMax = startInfo Universe = AllUniverses[Ustart] - print( - "Build Universe {} in container cell {}".format( - ContainerCell.FILL, ContainerCell.name - ) - ) + print(f"Build Universe {ContainerCell.FILL} in container cell {ContainerCell.name}") fails = [] for NTcell in Universe.values(): if duplicate: @@ -136,7 +128,7 @@ def BuildUniverse( NTcell = NTcell.copy() if buildShape: - print("Level :{} build Cell {} ".format(CC.level + 1, NTcell.name)) + print(f"Level :{CC.level + 1} build Cell {NTcell.name} ") if type(NTcell.definition) is not BoolSequence: NTcell.definition = BoolSequence(NTcell.definition.str) @@ -173,9 +165,7 @@ def BuildUniverse( if ContainerCell.CurrentTR: cell.CurrentTR = ContainerCell.CurrentTR.multiply(cell.TRFL) cell.level = ContainerCell.level + 1 - univ, ff = BuildUniverse( - (cell.FILL, levelMax), cell, AllUniverses, universeCut=universeCut - ) + univ, ff = BuildUniverse((cell.FILL, levelMax), cell, AllUniverses, universeCut=universeCut) CADUniverse.append(univ) fails.extend(ff) @@ -187,15 +177,15 @@ def makeTree(CADdoc, CADCells): label, universeCADCells = CADCells groupObj = CADdoc.addObject("App::Part", "Materials") - groupObj.Label = "Universe_{}_Container_{}".format(label[1], label[0]) + groupObj.Label = f"Universe_{label[1]}_Container_{label[0]}" CADObj = {} for i, c in enumerate(universeCADCells): if type(c) is tuple: groupObj.addObject(makeTree(CADdoc, c)) else: - featObj = CADdoc.addObject("Part::FeaturePython", "solid{}".format(i)) - featObj.Label = "Cell_{}_{}".format(c.name, c.MAT) + featObj = CADdoc.addObject("Part::FeaturePython", f"solid{i}") + featObj.Label = f"Cell_{c.name}_{c.MAT}" featObj.Shape = c.shape if c.MAT not in CADObj.keys(): CADObj[c.MAT] = [featObj] @@ -204,7 +194,7 @@ def makeTree(CADdoc, CADCells): for mat, matGroup in CADObj.items(): groupMatObj = CADdoc.addObject("App::Part", "Materials") - groupMatObj.Label = "Material_{}".format(mat) + groupMatObj.Label = f"Material_{mat}" groupMatObj.addObjects(matGroup) groupObj.addObject(groupMatObj) diff --git a/src/geouned/GEOReverse/Modules/buildSolidCell.py b/src/geouned/GEOReverse/Modules/buildSolidCell.py index 7c8c8178..a4bcfd9a 100644 --- a/src/geouned/GEOReverse/Modules/buildSolidCell.py +++ b/src/geouned/GEOReverse/Modules/buildSolidCell.py @@ -1,7 +1,7 @@ import Part from .options import Options -from .splitFunction import SplitSolid, joinBase, splitBase +from .splitFunction import SplitBase, SplitSolid, joinBase from .Utils.booleanFunction import BoolSequence @@ -21,7 +21,7 @@ def BuildSolid(cell, boundBox, mode="oneByOne", simplify=False): # cell.definition = BoolSequence(cell.definition.str) cell.cleanUndefined() - celParts = BuildDepth(cell, splitBase(cutCell), mode, True, simplify) + celParts = BuildDepth(cell, SplitBase(cutCell), mode, True, simplify) celParts = getPart(celParts) # print('celparts',len(celParts)) @@ -55,9 +55,9 @@ def BuildDepth(cell, cutShape, mode, baseBox, simplify=False, loop=0): for i, CS in enumerate(cutShape): cbaseBox = baseBox # CS.base.exportStep('CS_{}_{}.stp'.format(i,str(cell.definition))) - # CTable =buildCTableFromSolids(cell.makeBox(CS.base.BoundBox),cell.surfaces,option='full') + # CTable =build_c_table_from_solids(cell.makeBox(CS.base.BoundBox),cell.surfaces,option='full') # cell.definition.simplify(CTable) - cell.definition.groupSingle() + cell.definition.group_single() if type(cell.definition.elements) is not bool: if cell.definition.level == 0: @@ -68,9 +68,7 @@ def BuildDepth(cell, cutShape, mode, baseBox, simplify=False, loop=0): if seq.operator == "AND": part = CS for e in cell.definition.elements: - part = BuildDepth( - cell.getSubCell(e), part, mode, cbaseBox, simplify, loop=loop - ) + part = BuildDepth(cell.getSubCell(e), part, mode, cbaseBox, simplify, loop=loop) cbaseBox = False newCutShape.extend(part) else: @@ -110,14 +108,16 @@ def BuildSolidParts(cell, base, mode): # print(boundBox) if mode == "solids": - # boundBox.enlarge(10) - if cell.definition.operator == "OR" and False: - Def = cell.definition - cell.definition = cell.definition.getComplementary() - cell.buildShape(boundBox, force=False, simplify=False) - cell.definition = Def - else: - cell.buildShape(boundBox, force=True, simplify=False, fuse=True) + + # TODO consider making this buildShape call conditional + # if cell.definition.operator == "OR" and False: + # Def = cell.definition + # cell.definition = cell.definition.get_complementary() + # cell.buildShape(boundBox, force=False, simplify=False) + # cell.definition = Def + # else: + # cell.buildShape(boundBox, force=True, simplify=False, fuse=True) + cell.buildShape(boundBox, force=True, simplify=False, fuse=True) # print('export') # base.base.exportStep('base.stp') @@ -130,9 +130,7 @@ def BuildSolidParts(cell, base, mode): print("not cutting surfaces") return tuple(base.base), tuple() if mode == "solids": - full, cut = SplitSolid( - base, surfaces, cell, solidTool=True, tolerance=Options.splitTolerance - ) + full, cut = SplitSolid(base, surfaces, cell, solidTool=True, tolerance=Options.splitTolerance) elif mode == "allSurfaces": full, cut = SplitSolid(base, surfaces, cell, tolerance=Options.splitTolerance) diff --git a/src/geouned/GEOReverse/Modules/processInp.py b/src/geouned/GEOReverse/Modules/processInp.py index 2507ea67..e6cf524a 100644 --- a/src/geouned/GEOReverse/Modules/processInp.py +++ b/src/geouned/GEOReverse/Modules/processInp.py @@ -88,7 +88,7 @@ def getSetting(config): elif key == "inFormat": inFormat = config.get("Setting", key) else: - print("{} bad keyword. Ignored".format(key)) + print(f"{key} bad keyword. Ignored") return fileIn, fileOut, outBox, inFormat @@ -108,7 +108,7 @@ def getLevel(config): elif key == "UStart": UStart = config.getint("Levels", key) else: - print("{} bad keyword. Ignored".format(key)) + print(f"{key} bad keyword. Ignored") return UStart, levMax @@ -122,7 +122,7 @@ def getRange(section, config): elif key == "range": rawRange = config.get(section, key) else: - print("{} bad keyword. Ignored".format(key)) + print(f"{key} bad keyword. Ignored") if rType is None: return None @@ -132,7 +132,7 @@ def getRange(section, config): elif rType.lower() == "exclude" or rType.lower() == "include": ctRange = getRangeData(rawRange) if ctRange is None: - print("bad range in section {}".format(section)) + print(f"bad range in section {section}") return None else: return (rType.lower(), ctRange) @@ -198,7 +198,7 @@ def setSecOptions(config): tolerance = config.getfloat("Options", key) setattr(Options, key, tolerance) else: - print("{} bad keyword. Ignored".format(key)) + print(f"{key} bad keyword. Ignored") return diff --git a/src/geouned/GEOReverse/Modules/remh.py b/src/geouned/GEOReverse/Modules/remh.py index 0abf384f..dd473996 100644 --- a/src/geouned/GEOReverse/Modules/remh.py +++ b/src/geouned/GEOReverse/Modules/remh.py @@ -40,9 +40,7 @@ intercls = re.compile( r"(?P\))(?P(( *| *((\n *)?\$|\nC)*\n *)[-+]?\d))" ) # closed parenthesis followed by number -interopn = re.compile( - r"(?P\d)(?P(( *| *((\n *)?\$|\nC)*\n *)\())" -) # number followed by opened parenthesis +interopn = re.compile(r"(?P\d)(?P(( *| *((\n *)?\$|\nC)*\n *)\())") # number followed by opened parenthesis intercop = re.compile( r"(?P\))(?P(( *| *((\n *)?\$|\nC)*\n *)\())" ) # closed parenthesis followed by opened parenthesis @@ -52,39 +50,25 @@ mostinner = re.compile(r"\([^\(^\)]*\)") # identify most inner parentheses bracketsemi = re.compile(r"[\]\[;]") # square bracket or semicolon blnkline = re.compile(r"^ *\n", re.M) # identify blank line -contline = re.compile( - r"\n {0,4}(?P[^c^ ])", re.I -) # identify character other than 'C' in fisrt 5 columns +contline = re.compile(r"\n {0,4}(?P[^c^ ])", re.I) # identify character other than 'C' in fisrt 5 columns comdollar = re.compile(r"\n(?P *)\$") # identify dollar on 'blank line' -startgeom = re.compile( - r"(?P^ *)(?P[\-\+\d])" -) # identify beginning of the geomtric part -endgeom = re.compile( - r"(?P\d)(?P *((\n *)?\$|\nc)?(\n *)?$)", re.I -) # identify end of the geomtric part +startgeom = re.compile(r"(?P^ *)(?P[\-\+\d])") # identify beginning of the geomtric part +endgeom = re.compile(r"(?P\d)(?P *((\n *)?\$|\nc)?(\n *)?$)", re.I) # identify end of the geomtric part # endgeom=re.compile(r"(?P\d)(?P *(\$|\nc)?(\n *)?$)",re.I) # identify end of the geomtric part # other -rehash = re.compile( - r"# *(\d+|\()" -) # find beginning of complementary operator (both cell and surf) +rehash = re.compile(r"# *(\d+|\()") # find beginning of complementary operator (both cell and surf) parent = re.compile(r"[\(|\)]") # position of open and close parenthesis (get_hashcell) gline = re.compile( r"(^ ?[\(\):\-\+\d+\.\# ]+|\n {5}[\(\):\-\+\d+\.\# ]+)", re.I ) # valid geometric part of the line (remove/restore_comments) -comments = re.compile( - r"((\n *)?\$|\n *c)", re.I -) # begining of comment part (remove/restore_comments) +comments = re.compile(r"((\n *)?\$|\n *c)", re.I) # begining of comment part (remove/restore_comments) # comments=re.compile(r"\$|\n *c",re.I) # begining of comment part (remove/restore_comments) celtrsf = re.compile(r"TRCL *= *", re.I) celuniverse = re.compile(r"U *= *", re.I) celfill = re.compile(r"FILL *= *", re.I) -trfnumber = re.compile( - r"([-+]?(\d+\.\d+|\.\d+|\d+\.?)(e[+-]\d+)?)|\)", re.I -) # search for general number or close bracket ) -likemat = re.compile( - r"MAT *= *(?P\d+)", re.I -) # identify material value on like but card +trfnumber = re.compile(r"([-+]?(\d+\.\d+|\.\d+|\d+\.?)(e[+-]\d+)?)|\)", re.I) # search for general number or close bracket ) +likemat = re.compile(r"MAT *= *(?P\d+)", re.I) # identify material value on like but card dollar = re.compile(r"\$.*\n", re.I) @@ -190,7 +174,7 @@ def reverse_repl(m): def complementary(ccell, outter=True): """return the complementary cell""" - wrkcell = cline(ccell.str) + wrkcell = Cline(ccell.str) if wrkcell.str[-1] == "\n": wrkcell.str = wrkcell.str[:-1] @@ -205,9 +189,7 @@ def complementary(ccell, outter=True): wrkcell.str = re.sub( interblk, r"\g&\g", wrkcell.str ) # change intersection separate by blank space ie: "number number" - wrkcell.str = re.sub( - interblk, r"\g&\g", wrkcell.str - ) # 2nd pass intersection blank space (require 2 pass) + wrkcell.str = re.sub(interblk, r"\g&\g", wrkcell.str) # 2nd pass intersection blank space (require 2 pass) wrkcell.str = re.sub( intercls, r"\g&\g", wrkcell.str ) # change intersection close parenthesis ie: ") number" @@ -237,14 +219,14 @@ def complementary(ccell, outter=True): ############################################################ -class cline: +class Cline: def __init__(self, line): self.str = line def copy(self): - return cline(self.str) + return Cline(self.str) - def getSurfacesNumbers(self): + def get_surfaces_numbers(self): s = set(unsignedint.findall(self.str)) return tuple(map(int, s)) @@ -300,8 +282,8 @@ def restore_comments(self): self.str = "".join(celltab) return - def outterTerms(self): - cgeom = cline(self.str) + def outer_terms(self): + cgeom = Cline(self.str) cgeom.remove_comments(full=True) cgeom.remove_redundant() @@ -322,13 +304,7 @@ def outterTerms(self): cont = True if redundant(m, geom): # remove redundant parentheses - geom = ( - geom[: m.start()] - + " " - + geom[m.start() + 1 : m.end() - 1] - + " " - + geom[m.end() :] - ) + geom = geom[: m.start()] + " " + geom[m.start() + 1 : m.end() - 1] + " " + geom[m.end() :] else: # replace no redundant parentheses by 0 and : by ; zeros = "0" * (m.end() - m.start()) @@ -384,13 +360,7 @@ def remove_redundant(self, remove_com=True, remopt="nochg"): cont = True if redundant(m, geom): # remove redundant parentheses - geom = ( - geom[: m.start()] - + " " - + geom[m.start() + 1 : m.end() - 1] - + " " - + geom[m.end() :] - ) + geom = geom[: m.start()] + " " + geom[m.start() + 1 : m.end() - 1] + " " + geom[m.end() :] else: # replace no redundant parentheses by [] and : by ; term = geom[m.start() + 1 : m.end() - 1].replace(":", ";") @@ -434,7 +404,7 @@ def get_hashcell(self, start=0): count -= 1 if count == 0: end = p.end() - cell = cline(self.str[start + 1 : end]) + cell = Cline(self.str[start + 1 : end]) break return cell, end @@ -452,7 +422,7 @@ def replace(self, surf, new, pos=0): self.str = self.str[0 : m.start()] + s2 + self.str[m.end() :] return m.start() + len(s2) else: - print("number {} not found".format(surf)) + print(f"number {surf} not found") return -1 def countP(self): @@ -470,18 +440,10 @@ def SplitCell(self): geom = re.sub( interblk, r"\g&\g", geom ) # change intersection separate by blank space ie: "number number" - geom = re.sub( - interblk, r"\g&\g", geom - ) # 2nd pass intersection blank space (require 2 pass) - geom = re.sub( - intercls, r"\g&\g", geom - ) # change intersection close parenthesis ie: ") number" - geom = re.sub( - interopn, r"\g&\g", geom - ) # change intersection open parenthesis ie: "number (" - geom = re.sub( - intercop, r"\g&\g", geom - ) # change intersection close-open parenthesis ie: ") (" + geom = re.sub(interblk, r"\g&\g", geom) # 2nd pass intersection blank space (require 2 pass) + geom = re.sub(intercls, r"\g&\g", geom) # change intersection close parenthesis ie: ") number" + geom = re.sub(interopn, r"\g&\g", geom) # change intersection open parenthesis ie: "number (" + geom = re.sub(intercop, r"\g&\g", geom) # change intersection close-open parenthesis ie: ") (" parts = [] block = "" @@ -532,7 +494,7 @@ def SplitCell(self): ############################################################ -class cell_card_string: +class CellCardString: def __init__(self, card): self.stat = {"word": None, "hashcell": None, "hashsurf": None, "hash": None} @@ -549,8 +511,8 @@ def __init__(self, card): def __card_split__(self, cardin): """Split the card string in three parts : - headstr : string containing the cell name, mat number and density (if mat != 0) of the cell - - geom : cline class containing the part of the geometric definition of the cell - - param : cline class containing the cell parameters part + - geom : Cline class containing the part of the geometric definition of the cell + - param : Cline class containing the cell parameters part hproc is true if the complementary operator of the cell can be substituted""" m = celmat.match(cardin) @@ -560,8 +522,8 @@ def __card_split__(self, cardin): if m.group("scnd").lower() == "like": self.headstr = cardin[: m.start("scnd")] s = likebut.search(cardin, m.end("scnd")) - self.geom = cline(cardin[m.start("scnd") : s.end()]) - self.parm = cline(cardin[s.end() :]) + self.geom = Cline(cardin[m.start("scnd") : s.end()]) + self.parm = Cline(cardin[s.end() :]) self.hproc = False mc = unsignedint.search(self.geom.str) self.likeCell = int(mc.group()) @@ -578,7 +540,7 @@ def __card_split__(self, cardin): if self.hproc: self.headstr = cardin[:cstart] - cellcard = cline(cardin[cstart:]) + cellcard = Cline(cardin[cstart:]) cellcard.remove_comments() m = param.search(cellcard.str) if m: @@ -599,8 +561,8 @@ def __card_split__(self, cardin): if m: start = m.end(1) - self.geom = cline(cellcard.str[:start]) - self.parm = cline(cellcard.str[start:]) + self.geom = Cline(cellcard.str[:start]) + self.parm = Cline(cellcard.str[start:]) # look for transformation in cell parameters self.parm.remove_comments() @@ -609,8 +571,8 @@ def __card_split__(self, cardin): self.hproc = False self.parm.restore_comments() else: - self.geom = cline(cellcard.str) - self.parm = cline("") + self.geom = Cline(cellcard.str) + self.parm = Cline("") return @@ -717,16 +679,14 @@ def remove(card, cname, keepComments): """remove complementary operator and subtitute by complementary cell""" if "parser.Card" in str(type(card)): celline = "".join(card.lines) - cardstr = cell_card_string(celline) + cardstr = CellCardString(celline) else: cardstr = card cardstr.get_stat() cardstr.geom.remove_comments(full=not keepComments) if (not cardstr.hproc) or (cardstr.stat["hash"] == 0): - return ( - cardstr.geom - ) # no complementary operator or cannot be # cannot be removed - cell = cline(cardstr.geom.str) + return cardstr.geom # no complementary operator or cannot be # cannot be removed + cell = Cline(cardstr.geom.str) # find all operators in the cell and # substitute all complementary operators # locate object in list to reverse iteration @@ -753,24 +713,14 @@ def remove(card, cname, keepComments): if m.group(1) == "(": # complementary cell defined as surface intersections hcell, end = cell.get_hashcell(start) cellmod = cell.str[0:start] + complementary(hcell) + cell.str[end:] - cell = cline(cellmod) + cell = Cline(cellmod) else: - hcname = int( - m.group(1) - ) # complementary cell defined with other cell index - newdef = remove( - cards[hcname], hcname, keepComments - ) # remove complementary operator in new cell if necessary + hcname = int(m.group(1)) # complementary cell defined with other cell index + newdef = remove(cards[hcname], hcname, keepComments) # remove complementary operator in new cell if necessary end = m.end() - cellmod = ( - cell.str[0:start] - + " " - + complementary(newdef) - + " " - + cell.str[end:] - ) - cell = cline(cellmod) + cellmod = cell.str[0:start] + " " + complementary(newdef) + " " + cell.str[end:] + cell = Cline(cellmod) return cell newcell = remove(cards[cname], cname, keepComments) - return cline(newcell.str) + return Cline(newcell.str) diff --git a/src/geouned/GEOReverse/Modules/splitFunction.py b/src/geouned/GEOReverse/Modules/splitFunction.py index 2a688d42..99c4e8b3 100644 --- a/src/geouned/GEOReverse/Modules/splitFunction.py +++ b/src/geouned/GEOReverse/Modules/splitFunction.py @@ -5,7 +5,7 @@ import Part -class splitBase: +class SplitBase: def __init__(self, base, knownSurf={}): self.base = base self.knownSurf = knownSurf @@ -31,9 +31,10 @@ def joinBase(baseList): removedKeys.append(k) newbase = FuseSolid(shape) - return splitBase(newbase, surf) + return SplitBase(newbase, surf) +# TODO rename this function as there are two with the name name def SplitSolid(base, surfacesCut, cellObj, solidTool=False, tolerance=0.01): # 1e-2 # split Base (shape Object or list/tuple of shapes) # with selected surfaces (list of surfaces objects) cutting the base(s) (surfacesCut) @@ -63,9 +64,7 @@ def SplitSolid(base, surfacesCut, cellObj, solidTool=False, tolerance=0.01): # # print(s.type,s.params,s.id) # s.shape.exportStep('tool{}.stp'.format(s.id)) # base.base.exportStep('base.stp') - Solids = BOPTools.SplitAPI.slice( - base.base, Tools, "Split", tolerance=tolerance - ).Solids + Solids = BOPTools.SplitAPI.slice(base.base, Tools, "Split", tolerance=tolerance).Solids if not Solids: Solids = [base.base] partPositions, partSolids = space_decomposition(Solids, surfacesCut) @@ -88,9 +87,9 @@ def SplitSolid(base, surfacesCut, cellObj, solidTool=False, tolerance=0.01): # # sol.exportStep('solid_{}{}.stp'.format(name,ii)) if inSolid: - fullPart.append(splitBase(sol, pos)) + fullPart.append(SplitBase(sol, pos)) elif inSolid is None: - cutPart.append(splitBase(sol, pos)) + cutPart.append(SplitBase(sol, pos)) return fullPart, cutPart @@ -164,7 +163,7 @@ def point_inside(solid): BBox = solid.optimalBoundingBox(False) box = [BBox.XMin, BBox.XMax, BBox.YMin, BBox.YMax, BBox.ZMin, BBox.ZMax] - boxes, centers = CutBox(box) + boxes, centers = cut_box(box) n = 0 while True: @@ -176,20 +175,20 @@ def point_inside(solid): subbox = [] centers = [] for b in boxes: - btab, ctab = CutBox(b) + btab, ctab = cut_box(b) subbox.extend(btab) centers.extend(ctab) boxes = subbox n = n + 1 if n == cut_box: - print("Solid not found in bounding Box (Volume : {})".format(solid.Volume)) + print(f"Solid not found in bounding Box (Volume : {solid.Volume})") print("Valid Solid : ", solid.isValid()) return None # divide a box into 8 smaller boxes -def CutBox(Box): +def cut_box(Box): xmid = (Box[1] + Box[0]) * 0.5 ymid = (Box[3] + Box[2]) * 0.5 zmid = (Box[5] + Box[4]) * 0.5 @@ -371,7 +370,7 @@ def surface_side(p, surf): break else: - print("surface type {} not considered".format(surf[0])) + print(f"surface type {surf[0]} not considered") return return inout > 0 diff --git a/src/geouned/GEOReverse/__init__.py b/src/geouned/GEOReverse/__init__.py index 3d9862c7..e69de29b 100644 --- a/src/geouned/GEOReverse/__init__.py +++ b/src/geouned/GEOReverse/__init__.py @@ -1,8 +0,0 @@ -# this try except attempts to import freecad (lowercase) which is the conda -# package name for FreeCAD (mixed case) upon import the conda package appends -# the sys path for Conda installed FreeCAD, consequently FreeCAD can then be -# found by subsequent import statements through out the code base -try: - import freecad -except: - pass diff --git a/src/geouned/GEOReverse/reverse.py b/src/geouned/GEOReverse/reverse.py index 6ca32ca8..faaf432a 100644 --- a/src/geouned/GEOReverse/reverse.py +++ b/src/geouned/GEOReverse/reverse.py @@ -3,10 +3,10 @@ from .CodeVersion import * from .Modules.buildCAD import buildCAD, makeTree -from .Modules.MCNPinput import MCNPinput -from .Modules.Objects import CADCell +from .Modules.MCNPinput import McnpInput +from .Modules.Objects import CadCell from .Modules.processInp import setOptions -from .Modules.XMLinput import XMLinput +from .Modules.XMLinput import XmlInput def reverse(optFile="configRevese.ini"): @@ -28,19 +28,16 @@ def reverse(optFile="configRevese.ini"): "format": setting["inFormat"], } - UnivCell = CADCell() + UnivCell = CadCell() UnivCell.shape = UnivCell.makeBox(FreeCAD.BoundBox(*outBox)) # get geometry definition from MCNP input if inFormat == "mcnp": - geom = MCNPinput(geomfile) - elif inFormat == "openMC_XML": - geom = XMLinput(geomfile) + geom = McnpInput(geomfile) + elif inFormat == "openmc_xml": + geom = XmlInput(geomfile) else: - msg = ( - f"input format type {inFormat} is not supported." - 'Supported options are "mcnp" or "openMC_XML"' - ) + msg = f"input format type {inFormat} is not supported." 'Supported options are "mcnp" or "openmc_xml"' raise ValueError(msg) CADCells, fails = buildCAD(UnivCell, geom, CADselection) diff --git a/src/geouned/GEOUNED/Conversion/CellDefinition.py b/src/geouned/GEOUNED/Conversion/CellDefinition.py deleted file mode 100644 index b8336990..00000000 --- a/src/geouned/GEOUNED/Conversion/CellDefinition.py +++ /dev/null @@ -1,1195 +0,0 @@ -############################ -# Module for Cell definiton # -############################# -import math - -import FreeCAD -import Part - -from ..Utils import BasicFunctions_part2 as BF -from ..Utils import Functions as UF -from ..Utils import Geometry_GU as GU -from ..Utils.BasicFunctions_part1 import ( - isInLine, - isOposite, - isParallel, - isSameValue, - signPlane, -) -from ..Utils.booleanFunction import BoolSequence, insertInSequence -from ..Utils.BooleanSolids import buildCTableFromSolids, removeExtraSurfaces -from ..Utils.Functions import GEOUNED_Surface -from ..Utils.Options.Classes import Options as opt -from ..Utils.Options.Classes import Tolerances as tol - - -def getId(facein, Surfaces): - - surfin = str(facein) - if surfin == "": - if isParallel(facein.Axis, FreeCAD.Vector(1, 0, 0), tol.pln_angle): - P = "PX" - elif isParallel(facein.Axis, FreeCAD.Vector(0, 1, 0), tol.pln_angle): - P = "PY" - elif isParallel(facein.Axis, FreeCAD.Vector(0, 0, 1), tol.pln_angle): - P = "PZ" - else: - P = "P" - - for s in Surfaces[P]: - if BF.isSamePlane( - facein, - s.Surf, - dtol=tol.pln_distance, - atol=tol.pln_angle, - relTol=tol.relativeTol, - ): - return s.Index - - elif surfin == "": - for s in Surfaces["Cyl"]: - if BF.isSameCylinder( - facein, - s.Surf, - dtol=tol.cyl_distance, - atol=tol.cyl_angle, - relTol=tol.relativeTol, - ): - return s.Index - - elif surfin == "": - for s in Surfaces["Cone"]: - if BF.isSameCone( - facein, - s.Surf, - dtol=tol.kne_distance, - atol=tol.kne_angle, - relTol=tol.relativeTol, - ): - return s.Index - - elif surfin[0:6] == "Sphere": - for s in Surfaces["Sph"]: - if BF.isSameSphere( - facein, s.Surf, tol.sph_distance, relTol=tol.relativeTol - ): - return s.Index - - elif surfin == "": - for s in Surfaces["Tor"]: - if BF.isSameTorus( - facein, - s.Surf, - dtol=tol.tor_distance, - atol=tol.tor_angle, - relTol=tol.relativeTol, - ): - return s.Index - - return 0 - - -def isInverted(solid): - - face = solid.Faces[0] - - # u=(face.Surface.bounds()[0]+face.Surface.bounds()[1])/2.0 # entre 0 y 2pi si es completo - # v=face.Surface.bounds()[0]+(face.Surface.bounds()[3]-face.Surface.bounds()[2])/3.0 # a lo largo del eje - Range = face.ParameterRange - u = (Range[1] + Range[0]) / 2.0 - v = (Range[3] + Range[2]) / 2.0 - - if str(face.Surface) == "": - dist1 = face.Surface.value(u, v).distanceToLine( - face.Surface.Center, face.Surface.Axis - ) - dist2 = ( - face.Surface.value(u, v) - .add(face.Surface.normal(u, v).multiply(1.0e-6)) - .distanceToLine(face.Surface.Center, face.Surface.Axis) - ) - if (dist2 - dist1) < 0.0: - # The normal of the cylinder is going inside - return True - elif str(face.Surface) == "": - dist1 = face.Surface.value(u, v).distanceToLine( - face.Surface.Apex, face.Surface.Axis - ) - dist2 = ( - face.Surface.value(u, v) - .add(face.Surface.normal(u, v).multiply(1.0e-6)) - .distanceToLine(face.Surface.Apex, face.Surface.Axis) - ) - if (dist2 - dist1) < 0.0: - # The normal of the cylinder is going inside - return True - # MIO - elif str(face.Surface)[0:6] == "Sphere": - # radii = point - center - radii = face.Surface.value(u, v).add(face.Surface.Center.multiply(-1)) - radiiB = ( - face.Surface.value(u, v) - .add(face.Surface.normal(u, v).multiply(1.0e-6)) - .add(face.Surface.Center.multiply(-1)) - ) - # radiiB = radii.add( face.Surface.normal(u,v).multiply(1.0e-6) ) - if (radiiB.Length - radii.Length) < 0.0: - # An increasing of the radii vector in the normal direction decreases the radii: oposite normal direction - return True - - elif str(face.Surface) == "": - dist1 = face.CenterOfMass.distanceToPoint(solid.BoundBox.Center) - dist2 = face.CenterOfMass.add( - face.normalAt(u, v).multiply(1.0e-6) - ).distanceToPoint(solid.BoundBox.Center) - point2 = face.CenterOfMass.add(face.normalAt(u, v).multiply(1.0e-6)) - if solid.isInside(point2, 1e-7, False): - return True - - return False - - -def GenPlane(face, solid): - """Generate an additional plane when convex surfaces of second order are presented as a face of the solid""" - - surf = face.Surface - if str(surf) == "": - return GenPlaneCylinder(face, solid) - if str(surf) == "": - return GenPlaneCone(face, solid) - if str(surf) == "Sphere": - return GenPlaneSphere(face, solid) - - -def getClosedRanges(solid, face_index): - - UNodes = [] - for index in face_index: - URange = solid.Faces[index].ParameterRange - UNodes.append((URange[0], index)) - UNodes.append((URange[1], index)) - UNodes.sort() - - closedRange = getIntervals(UNodes) - - aMin = closedRange[0][0][0] - aMax = closedRange[-1][1][0] - - if abs(aMax - aMin - 2.0 * math.pi) < 1e-2: - if len(closedRange) == 1: - closedFace = True - else: - endPoint = (closedRange[-1][0][0] - 2 * math.pi, closedRange[-1][0][1]) - closedRange[0][0] = endPoint - closedRange[0][2].update(closedRange[-1][2]) - del closedRange[-1] - - if len(closedRange) == 1: - if ( - abs(closedRange[0][1][0] - closedRange[0][0][0] - 2.0 * math.pi) - < 1e-2 - ): - closedFace = True - else: - closedFace = False - else: - closedFace = False - else: - closedFace = False - return closedRange, closedFace - - -def getIntervals(UNodes): - closedRange = [] - posMin = dict() - posMax = dict() - for i, node in enumerate(UNodes): - if node[1] not in posMin.keys(): - posMin[node[1]] = i - else: - posMax[node[1]] = i - - UMin = UNodes[0] - iPos = posMax[UMin[1]] - - while True: - x = UNodes[iPos] - end = True - for i in range(iPos + 1, len(UNodes)): - nxtInt = UNodes[i][1] - if ( - UNodes[posMin[nxtInt]][0] - x[0] - ) < 1e-5: # x pos is > min boundary of the next inteval inside precision 1e-5 - iPos = posMax[nxtInt] - end = False - break - - if end: - UMax = x - closedRange.append([UMin, UMax]) - iPos += 1 - if iPos < len(UNodes): - UMin = UNodes[iPos] - iPos = posMax[UMin[1]] - else: - break - - for rnge in closedRange: - index = set() - xmin = rnge[0][0] - xmax = rnge[1][0] - for interval in UNodes: - x = interval[0] - if (xmin - x) < 1.0e-5 and (x - xmax) < 1.0e-5: - index.add(interval[1]) - rnge.append(index) - - return closedRange - - -def getUValueBoundary(solid, face_index, myIndex): - - faceURange, closedFace = getClosedRanges(solid, face_index) - if closedFace: - return None, None - - for rnge in faceURange: - if myIndex in rnge[2]: - UMin, UMax = rnge[0:2] - return UMin, UMax - - -def GenPlaneSphere(face, solid): - Same_Faces = [] - Same_Faces.append(face) - - for f in solid.Faces: - if f.isEqual(face) or str(f.Surface) != "Sphere": - continue - if ( - f.Surface.Center == face.Surface.Center - and f.Surface.Radius == face.Surface.Radius - ): - # print 'Warning: coincident sphere faces are the same' - for f2 in Same_Faces: - if f.distToShape(f2)[0] < 1e-6: - Same_Faces.append(f) - break - - # print Same_Faces - normal = FreeCAD.Vector(0, 0, 0) - for f in Same_Faces: - normal += f.Area * (f.CenterOfMass - face.Surface.Center) - - return Part.Plane(face.Surface.Center, normal).toShape() - - -def GenPlaneCylinder(face, solid): - - Surf = face.Surface - rad = Surf.Radius - - if str(Surf) != "": - return None - - myIndex = solid.Faces.index(face) - face_index = [myIndex] - - for i, face2 in enumerate(solid.Faces): - if face2.Area < tol.min_area: - if opt.verbose: - print( - f"Warning: {str(Surf)} surface removed from cell definition. Face area < Min area ({face2.Area} < {tol.min_area}) " - ) - continue - if str(face2.Surface) == "" and not (face2.isEqual(face)): - if ( - face2.Surface.Axis.isEqual(face.Surface.Axis, 1e-5) - and face2.Surface.Radius == rad - and isInLine( - face2.Surface.Center, face.Surface.Axis, face.Surface.Center - ) - ): - # print 'Warning: coincident cylinder faces are the same' - face_index.append(i) - - UMin, UMax = getUValueBoundary(solid, face_index, myIndex) - if UMin is None: - return None - - U1, i1 = UMin - U2, i2 = UMax - - V1 = solid.Faces[i1].ParameterRange[2] - V2 = solid.Faces[i2].ParameterRange[2] - - P1 = solid.Faces[i1].valueAt(U1, V1) - P2 = solid.Faces[i2].valueAt(U2, V2) - - if P1.isEqual(P2, 1e-5): - if opt.verbose: - print("Error in the additional place definition") - return None - - normal = P2.sub(P1).cross(face.Surface.Axis) - plane = Part.Plane(P1, normal).toShape() - - return plane - - -def GenPlaneCylinder_old(face, solid): - - Surf = face.Surface - rad = Surf.Radius - - if str(Surf) != "": - return None - - face_index = [solid.Faces.index(face)] - - for i, face2 in enumerate(solid.Faces): - if face2.Area < tol.min_area: - if opt.verbose: - print( - f"Warning: {str(Surf)} surface removed from cell definition. Face area < Min area ({face2.Area} < {tol.min_area}) " - ) - continue - if str(face2.Surface) == "" and not (face2.isEqual(face)): - if ( - face2.Surface.Axis.isEqual(face.Surface.Axis, 1e-5) - and face2.Surface.Radius == rad - and isInLine( - face2.Surface.Center, face.Surface.Axis, face.Surface.Center - ) - ): - # print 'Warning: coincident cylinder faces are the same' - face_index.append(i) - - AngleRange = 0.0 - Uval = [] - for index in face_index: - Range = solid.Faces[index].ParameterRange - AngleRange = AngleRange + abs(Range[1] - Range[0]) - if not (Range[0] in Uval) and not (Range[1] in Uval): - Uval.append(Range[0]) - Uval.append(Range[1]) - if 2.0 * math.pi - AngleRange < 1e-2: - return None - - UVNodes = [] - for index in face_index: - face2 = solid.Faces[index] - try: - UVNodes.append(face2.getUVNodes()) - except RuntimeError: - UVNodes.append(face2.getUVNodes()) - - Uval_str_cl = [] - for i, elem1 in enumerate(Uval): - num_str1 = "%11.4E" % elem1 - if abs(elem1) < 1.0e-5: - num_str1 = "%11.4E" % 0.0 - if not (BF.isDuplicateInList(num_str1, i, Uval)): - Uval_str_cl.append(num_str1) - - face_index_2 = [face_index[0], face_index[0]] - - Node_min = UVNodes[0][0] - Node_max = UVNodes[0][1] - - dif1_0 = abs(float(Uval_str_cl[0]) - Node_min[0]) - dif2_0 = abs(float(Uval_str_cl[1]) - Node_max[0]) - - # searching for minimum and maximum angle points - for j, Nodes in enumerate(UVNodes): - for elem in Nodes: - dif1 = abs(float(Uval_str_cl[0]) - elem[0]) - dif2 = abs(float(Uval_str_cl[1]) - elem[0]) - - if dif1 < dif1_0: - Node_min = elem - face_index_2[0] = face_index[j] - dif1_0 = dif1 - if dif2 < dif2_0: - Node_max = elem - face_index_2[1] = face_index[j] - dif2_0 = dif2 - - V1 = solid.Faces[face_index_2[0]].valueAt(Node_min[0], Node_min[1]) - V2 = solid.Faces[face_index_2[1]].valueAt(Node_max[0], Node_max[1]) - - if V1.isEqual(V2, 1e-5): - if opt.verbose: - print("Error in the additional place definition") - return None - - normal = V2.sub(V1).cross(face.Surface.Axis) - plane = Part.Plane(V1, normal).toShape() - - return plane - - -def GenPlaneCone(face, solid): - - Surf = face.Surface - if str(Surf) != "": - return None - - myIndex = solid.Faces.index(face) - face_index = [myIndex] - - for i, face2 in enumerate(solid.Faces): - if face2.Area < tol.min_area: - if opt.verbose: - print( - f"Warning: {str(Surf)} surface removed from cell definition. Face area < Min area ({face2.Area} < {tol.min_area}) " - ) - continue - if str(face2.Surface) == "" and not (face2.isEqual(face)): - if ( - face2.Surface.Axis.isEqual(face.Surface.Axis, 1e-5) - and face2.Surface.Apex.isEqual(face.Surface.Apex, 1e-5) - and (face2.Surface.SemiAngle - face.Surface.SemiAngle) < 1e-6 - ): - face_index.append(i) - - UMin, UMax = getUValueBoundary(solid, face_index, myIndex) - if UMin is None: - return None - - U1, i1 = UMin - U2, i2 = UMax - - V1 = solid.Faces[i1].ParameterRange[2] - V2 = solid.Faces[i2].ParameterRange[2] - - P1 = solid.Faces[i1].valueAt(U1, V1) - P2 = solid.Faces[i2].valueAt(U2, V2) - - if P1.isEqual(P2, 1e-5): - if opt.verbose: - print("Error in the additional place definition") - return None - - plane = Part.Plane(P1, P2, face.Surface.Apex).toShape() - - return plane - - -def GenPlaneCone_old(face, solid): - - Surf = face.Surface - if str(Surf) != "": - return None - - face_index = [solid.Faces.index(face)] - - for i, face2 in enumerate(solid.Faces): - if face2.Area < tol.min_area: - if opt.verbose: - print( - f"Warning: {str(Surf)} surface removed from cell definition. Face area < Min area ({face2.Area} < {tol.min_area}) " - ) - continue - if str(face2.Surface) == "" and not (face2.isEqual(face)): - if ( - face2.Surface.Axis.isEqual(face.Surface.Axis, 1e-5) - and face2.Surface.Apex.isEqual(face.Surface.Apex, 1e-5) - and (face2.Surface.SemiAngle - face.Surface.SemiAngle) < 1e-6 - ): - face_index.append(i) - - AngleRange = 0.0 - Uval = [] - for index in face_index: - Range = solid.Faces[index].ParameterRange - AngleRange = AngleRange + abs(Range[1] - Range[0]) - Uval.append(Range[0]) - Uval.append(Range[1]) - - if 2.0 * math.pi - AngleRange < 1e-2: - return None - - UVNodes = [] - for index in face_index: - face2 = solid.Faces[index] - try: - UVNodes.append(face2.getUVNodes()) - except RuntimeError: - face.tessellate(1.0, True) - UVNodes.append(face2.getUVNodes()) - - Uval_str_cl = [] - - for i, elem1 in enumerate(Uval): - num_str1 = "%11.4E" % elem1 - if abs(elem1) < 1.0e-5: - num_str1 = "%11.4E" % 0.0 - if not (BF.isDuplicateInList(num_str1, i, Uval)): - Uval_str_cl.append(num_str1) - - face_index_2 = [face_index[0], face_index[0]] - - Node_min = UVNodes[0][0] - Node_max = UVNodes[0][1] - dif1_0 = abs(float(Uval_str_cl[0]) - Node_min[0]) - dif2_0 = abs(float(Uval_str_cl[1]) - Node_max[0]) - - # searching for minimum and maximum angle points - for j, Nodes in enumerate(UVNodes): - for elem in Nodes: - dif1 = abs(float(Uval_str_cl[0]) - elem[0]) - dif2 = abs(float(Uval_str_cl[1]) - elem[0]) - if dif1 < dif1_0: - Node_min = elem - face_index_2[0] = face_index[j] - dif1_0 = dif1 - if dif2 < dif2_0: - Node_max = elem - face_index_2[1] = face_index[j] - dif2_0 = dif2 - - V1 = solid.Faces[face_index_2[0]].valueAt(Node_min[0], Node_min[1]) - V2 = solid.Faces[face_index_2[1]].valueAt(Node_max[0], Node_max[1]) - - if V1.isEqual(V2, 1e-5): - if opt.verbose: - print("Error in the additional place definition") - return None - - plane = Part.Plane(V1, V2, face.Surface.Apex).toShape() - - return plane - - -def GenTorusAnnexUPlanes(face, Uparams): - - if isParallel(face.Surface.Axis, FreeCAD.Vector(1, 0, 0), tol.tor_angle): - axis = FreeCAD.Vector(1, 0, 0) - elif isParallel(face.Surface.Axis, FreeCAD.Vector(0, 1, 0), tol.tor_angle): - axis = FreeCAD.Vector(0, 1, 0) - elif isParallel(face.Surface.Axis, FreeCAD.Vector(0, 0, 1), tol.tor_angle): - axis = FreeCAD.Vector(0, 0, 1) - - center = face.Surface.Center - p1 = face.valueAt(Uparams[0], 0.0) - p2 = face.valueAt(Uparams[1], 0.0) - pmid = face.valueAt(0.5 * (Uparams[0] + Uparams[1]), 0.0) - - if isSameValue(abs(Uparams[1] - Uparams[0]), math.pi, tol.value): - d = axis.cross(p2 - p1) - d.normalize() - if d.dot(pmid - center) < 0: - d = -d - return ( - (center, d, face.Surface.MajorRadius, face.Surface.MajorRadius), - None, - ), False - - elif Uparams[1] - Uparams[0] < math.pi: - d = axis.cross(p2 - p1) - d.normalize() - if d.dot(pmid - center) < 0: - d = -d - return ( - (center, d, face.Surface.MajorRadius, face.Surface.MajorRadius), - None, - ), False - - else: - d1 = axis.cross(p1) - d1.normalize() - if d1.dot(pmid - center) < 0: - d1 = -d1 - - d2 = axis.cross(p2) - d2.normalize() - if d2.dot(pmid - center) < 0: - d2 = -d2 - - return ( - (center, d1, face.Surface.MajorRadius, face.Surface.MajorRadius), - (center, d2, face.Surface.MajorRadius, face.Surface.MajorRadius), - ), True # (d1 : d2) - - -def GenTorusAnnexUPlanes_org(face, Uparams): - - if isParallel(face.Surface.Axis, FreeCAD.Vector(1, 0, 0), tol.tor_angle): - axis = FreeCAD.Vector(1, 0, 0) - elif isParallel(face.Surface.Axis, FreeCAD.Vector(0, 1, 0), tol.tor_angle): - axis = FreeCAD.Vector(0, 1, 0) - elif isParallel(face.Surface.Axis, FreeCAD.Vector(0, 0, 1), tol.tor_angle): - axis = FreeCAD.Vector(0, 0, 1) - - center = face.Surface.Center - p1 = face.valueAt(Uparams[0], 0.0) - p2 = face.valueAt(Uparams[1], 0.0) - pmid = face.valueAt(0.5 * (Uparams[0] + Uparams[1]), 0.0) - - if isSameValue(abs(Uparams[1] - Uparams[0]), math.pi, tol.value): - d = axis.cross(p2 - p1) - d.normalize() - if pmid.dot(d) < 0: - d = -d - return ((center, d, face.Surface.MajorRadius), None), False - - else: - d1 = axis.cross(p1) - d1.normalize() - if pmid.dot(d1) < 0: - d1 = -d1 - - d2 = axis.cross(p2) - d2.normalize() - if pmid.dot(d2) < 0: - d2 = -d2 - - if Uparams[1] - Uparams[0] < math.pi: - return ( - (center, d1, face.Surface.MajorRadius, face.Surface.MajorRadius), - (center, d2, face.Surface.MajorRadius, face.Surface.MajorRadius), - ), False # ( d1 d2 ) - else: - return ( - (center, d1, face.Surface.MajorRadius, face.Surface.MajorRadius), - (center, d2, face.Surface.MajorRadius, face.Surface.MajorRadius), - ), True # (d1 : d2) - - -def GenTorusAnnexVSurface(face, Vparams, forceCylinder=False): - if isParallel(face.Surface.Axis, FreeCAD.Vector(1, 0, 0), tol.tor_angle): - axis = FreeCAD.Vector(1, 0, 0) - elif isParallel(face.Surface.Axis, FreeCAD.Vector(0, 1, 0), tol.tor_angle): - axis = FreeCAD.Vector(0, 1, 0) - elif isParallel(face.Surface.Axis, FreeCAD.Vector(0, 0, 1), tol.tor_angle): - axis = FreeCAD.Vector(0, 0, 1) - - p1 = face.valueAt(0.0, Vparams[0]) - face.Surface.Center - z1 = p1.dot(axis) - d1 = p1.cross(axis).Length - - p2 = face.valueAt(0.0, Vparams[1]) - face.Surface.Center - z2 = p2.dot(axis) - d2 = p2.cross(axis).Length - - if isSameValue(z1, z2, tol.distance): - surfType = "Plane" - center = face.Surface.Center + z1 * axis - Vmid = (Vparams[0] + Vparams[1]) * 0.5 - pMid = face.valueAt(0, Vmid) - face.Surface.Center - if pMid.dot(axis) < z1: - inSurf = True - else: - inSurf = False - return ( - (center, axis, face.Surface.MajorRadius, face.Surface.MajorRadius), - surfType, - inSurf, - ) - - elif isSameValue(d1, d2, tol.distance) or forceCylinder: - surfType = "Cylinder" - radius = min(d1, d2) - center = face.Surface.Center - if isSameValue(d1, face.Surface.MajorRadius, tol.distance): - Vmid = (Vparams[0] + Vparams[1]) * 0.5 - pMid = face.valueAt(0, Vmid) - center - if pMid.cross(axis).Length < face.Surface.MajorRadius: - inSurf = True - else: - inSurf = False - else: - if d1 < face.Surface.MajorRadius: - inSurf = True - else: - inSurf = False - return (center, axis, radius, face.Surface.MinorRadius), surfType, inSurf - - else: - surfType = "Cone" - za = (z2 * d1 - z1 * d2) / (d1 - d2) - Apex = face.Surface.Center + za * axis - semiAngle = abs(math.atan(d1 / (z1 - za))) - - ConeAxis = axis if (z1 - za) > 0.0 else -axis - - Vmid = (Vparams[0] + Vparams[1]) * 0.5 - pMid = face.valueAt(0, Vmid) - face.Surface.Center - zMid = pMid.dot(axis) - dMid = pMid.cross(axis).Length - - dCone = d1 * (zMid - za) / (z1 - za) - inSurf = True if dMid < dCone else False - - return ( - ( - Apex, - ConeAxis, - semiAngle, - face.Surface.MinorRadius, - face.Surface.MajorRadius, - ), - surfType, - inSurf, - ) - - -def cellDef(metaObj, Surfaces, UniverseBox): - - solids = metaObj.Solids - delList = [] - - PieceDef = BoolSequence(operator="OR") - PieceObj = [] - cones = set() - for isol, solid in enumerate(solids): - SurfPiece = [] - SurfObj = [] - extraPlaneReverse = dict() - - flag_inv = isInverted(solid) - solid_GU = GU.solid_GU(solid) - lastTorus = -1 - for iface, face in enumerate(solid_GU.Faces): - surfaceType = str(face.Surface) - if abs(face.Area) < tol.min_area: - if opt.verbose: - print( - f"Warning: {surfaceType} surface removed from cell definition. Face area < Min area ({face.Area} < {tol.min_area}) " - ) - continue - if face.Area < 0: - if opt.verbose: - print("Warning : Negative surface Area") - if face.Orientation not in ("Forward", "Reversed"): - continue - if flag_inv: - orient_temp = face.Orientation - if orient_temp == "Forward": - orient = "Reversed" - elif orient_temp == "Reversed": - orient = "Forward" - else: - orient = face.Orientation - - if "Sphere" in surfaceType: - surfaceType = "Sphere" - - # cone additional plane is added afterward - if ( - surfaceType in ("", "", "Sphere") - and orient == "Reversed" - ): - # cone additional plane is added afterward - idFace = getId(face.Surface, Surfaces) - if surfaceType == "": - cones.add(idFace) - if str(idFace) not in SurfPiece: - SurfPiece.append(str(idFace)) - SurfObj.append(face) - - try: - plane = GenPlane(face, solid_GU) - if plane is not None: - plane = GU.Plane_GU(plane) - except: - plane = None - if opt.verbose: - print("Warning: generation of additional plane has failed") - - if plane is not None: - p = GEOUNED_Surface( - ("Plane", (plane.Position, plane.Axis, plane.dim1, plane.dim2)), - UniverseBox, - Face="Build", - ) - - id, exist = Surfaces.addPlane(p) - sign = signPlane(face.CenterOfMass, p) - if exist: - pp = Surfaces.getSurface(id) - if isOposite(p.Surf.Axis, pp.Surf.Axis, tol.angle): - id = -id - id *= sign - - if idFace not in extraPlaneReverse.keys(): - extraPlaneReverse[idFace] = [str(id)] - SurfObj.append(p.shape) - else: - if str(id) not in extraPlaneReverse[idFace]: - extraPlaneReverse[idFace].append(str(id)) - SurfObj.append(p.shape) - - elif surfaceType == "": - - if ( - isParallel(face.Surface.Axis, FreeCAD.Vector(1, 0, 0), tol.angle) - or isParallel(face.Surface.Axis, FreeCAD.Vector(0, 1, 0), tol.angle) - or isParallel(face.Surface.Axis, FreeCAD.Vector(0, 0, 1), tol.angle) - ): - - idT = getId(face.Surface, Surfaces) - - index, Uparams = solid_GU.TorusUParams[iface] - if index == lastTorus: - continue - lastTorus = index - - # add if necesary additional planes following U variable - UClosed, UminMax = Uparams - # UClosed = True - if not UClosed: - planes, ORop = GenTorusAnnexUPlanes(face, UminMax) - plane1, plane2 = planes - - plane = GEOUNED_Surface( - ("Plane", plane1), UniverseBox, Face="Build" - ) - id1, exist = Surfaces.addPlane(plane) - if exist: - p = Surfaces.getSurface(id1) - if isOposite(plane.Surf.Axis, p.Surf.Axis, tol.pln_angle): - id1 = -id1 - - if plane2 is None: - UVar = "%i" % id1 - else: - plane = GEOUNED_Surface( - ("Plane", plane2), UniverseBox, Face="Build" - ) - id2, exist = Surfaces.addPlane(plane) - if exist: - p = Surfaces.getSurface(id2) - if isOposite( - plane.Surf.Axis, p.Surf.Axis, tol.pln_angle - ): - id2 = -id2 - - UVar = ( - "(%i : %i)" % (id1, id2) - if ORop - else "%i %i" % (id1, id2) - ) - - else: - UVar = "" - - # add if necesary additional surface following V variable - if orient == "Forward": - VVar = "-%i" % idT - - else: - index, Vparams = solid_GU.TorusVParams[iface] - VClosed, VminMax = Vparams - if VClosed: - VVar = "%i" % idT - else: - surfParams, surfType, inSurf = GenTorusAnnexVSurface( - face, VminMax, opt.forceCylinder - ) - - if surfType == "Cone": - cone = GEOUNED_Surface( - ("Cone", surfParams), UniverseBox, Face="Build" - ) - id2, exist = Surfaces.addCone(cone) - - elif surfType == "Cylinder": - cyl = GEOUNED_Surface( - ("Cylinder", surfParams), UniverseBox, Face="Build" - ) - id2, exist = Surfaces.addCylinder(cyl) - - elif surfType == "Plane": - plane = GEOUNED_Surface( - ("Plane", surfParams), UniverseBox, Face="Build" - ) - id2, exist = Surfaces.addPlane(plane) - if exist: - p = Surfaces.getSurface(id2) - if isOposite( - plane.Surf.Axis, p.Surf.Axis, tol.pln_angle - ): - id2 = -id2 - - VVar = "%i %i" % (idT, -id2 if inSurf else id2) - - var = VVar if UClosed else " ".join((VVar, UVar)) - if var not in SurfPiece: - SurfPiece.append(var) - SurfObj.append(face) - else: - if opt.verbose: - print( - "Only Torus with axis along X, Y , Z axis can be reproduced" - ) - else: - id = getId(face.Surface, Surfaces) - if surfaceType == "": - cones.add(-id) - - surf = face - if id == 0: - if opt.verbose: - print("Warning: ", surfaceType, " not found in surface list") - if surfaceType == "": - dim1 = face.ParameterRange[1] - face.ParameterRange[0] - dim2 = face.ParameterRange[3] - face.ParameterRange[2] - plane = GEOUNED_Surface( - ( - "Plane", - (face.Surface.Position, face.Surface.Axis, dim1, dim2), - ), - UniverseBox, - Face="Build", - ) - id, exist = Surfaces.addPlane(plane) - surf = plane.shape - elif surfaceType == "": - dimL = face.ParameterRange[3] - face.ParameterRange[2] - cylinder = GEOUNED_Surface( - ( - "Cylinder", - ( - face.Surface.Center, - face.Surface.Axis, - face.Surface.Radius, - dimL, - ), - ), - UniverseBox, - Face="Build", - ) - id, exist = Surfaces.addCylinder(cylinder) - surf = cylinder.shape - - if orient == "Reversed": - var = id - elif orient == "Forward": - var = -id - - if surfaceType == "": - s = Surfaces.getSurface(id) - if isOposite(face.Surface.Axis, s.Surf.Axis, tol.pln_angle): - var = -var - - if str(var) in SurfPiece: - continue - - SurfPiece.append(str(var)) - SurfObj.append(surf) - - if extraPlaneReverse: - for extra in extraPlaneReverse.values(): - if len(extra) == 1: - if extra[0] not in SurfPiece: - SurfPiece.append(extra[0]) - else: - SurfPiece.append("({})".format(":".join(extra))) - - SurfPieceBool = BoolSequence(" ".join(SurfPiece)) - # possible expresion for e - # i1 - # i1 i2SurfPiece - # i1 i2 i3 - # (i1:i2) - # i1 (i2:i3) - - # semi = e.find(':') - # blk = e.find(' ') - # print (e) - # if semi != -1 : - # orTerm = expOR(int(e[1:semi]),int(e[semi+1:-1])) - # SurfPieceBool.add(orTerm) - # elif blk != -1 : - # SurfPieceBool.add(int(e[1:blk]),int(e[blk+1:-1])) - # else : - # SurfPieceBool.add(int(e)) - - if SurfPieceBool.elements: - PieceDef.append(SurfPieceBool) - PieceObj.append(SurfObj) - else: - delList.append(isol) - - for isol in reversed(delList): - del metaObj.Solids[isol] - metaObj.setDefinition(PieceDef) - metaObj.setFaces(PieceObj) - return tuple(cones) - - -def getSurfValue(Definition, reverse=False): - - if Definition.level == 0: - if reverse: - surf = {-i for i in Definition.elements} - else: - surf = set(Definition.elements) - else: - surf = set() - for e in Definition.elements: - if e.operator == "AND": - if reverse: - surf = {-i for i in e.elements} - else: - surf = set(e.elements) - break - return surf - - -def appendComp(newCell, cellDef, cellCAD, metaComplementary): - - surfCell = getSurfValue(cellDef, True) - if metaComplementary.Definition.operator == "AND": - if not cellCAD.BoundBox.intersect(metaComplementary.CADSolid.BoundBox): - return False - Seq = metaComplementary.Definition - surfComp = getSurfValue(Seq, False) - if len(surfComp & surfCell) > 0: - return False - newCell.append(Seq.getComplementary()) - return True - - else: - append = False - for i, compPart in enumerate(metaComplementary.Solids): - if not cellCAD.BoundBox.intersect(compPart.BoundBox): - continue - Seq = metaComplementary.Definition.elements[i] - surfComp = getSurfValue(Seq, False) - if len(surfComp & surfCell) > 0: - continue - append = True - newCell.append(Seq.getComplementary()) - return append - - -def noOverlappingCell(metaList, Surfaces): - - Surfs = {} - for lst in Surfaces.values(): - for s in lst: - Surfs[s.Index] = s - - newDefinitionList = [] - metaList[0].setCADSolid() - - for i, m in enumerate(metaList[1:]): - m.setCADSolid() - - if m.Definition.operator == "AND": - newDef = BoolSequence(operator="AND") - newDef.append(m.Definition.copy()) - simplify = False - for mm in metaList[: i + 1]: - simp = appendComp(newDef, m.Definition, m.CADSolid, mm) - if simp: - simplify = True - simpTerm = [simplify] - - else: - newDef = BoolSequence(operator="OR") - simpTerm = [] - for j, partSolid in enumerate(m.Solids): - subDef = BoolSequence(operator="AND") - subDef.append(m.Definition.elements[j].copy()) - simplify = False - for mm in metaList[: i + 1]: - simp = appendComp(subDef, m.Definition.elements[j], partSolid, mm) - if simp: - simplify = True - simpTerm.append(simplify) - newDef.append(subDef) - newDefinitionList.append((newDef, simpTerm)) - - for m, tDef in zip(metaList[1:], newDefinitionList): - Def, simplify = tDef - if True in simplify: - print(f"reduce cell {m.__id__}") - Box = UF.getBox(m) - - # evaluate only diagonal elements of the Constraint Table (fastest) and remove surface not - # crossing in the solid boundBox - CT = buildCTableFromSolids( - Box, (tuple(Def.getSurfacesNumbers()), Surfs), option="diag" - ) - - newDef = removeExtraSurfaces(Def, CT) - - # evaluate full constraint Table with less surfaces involved - CT = buildCTableFromSolids( - Box, (tuple(newDef.getSurfacesNumbers()), Surfs), option="full" - ) - - if newDef.operator == "AND": - newDef.simplify(CT) - newDef.clean() - else: - for i, s in enumerate(simplify): - if not s: - continue - comp = newDef.elements[i] - comp.simplify(CT) - comp.clean() - - m.setDefinition(newDef) - m.Definition.joinOperators() - m.Definition.levelUpdate() - - -def ExtraPlaneCylFace(face, Box, Surfaces): - wire = face.OuterWire - planesId = [] - for e in wire.OrderedEdges: - curve = str(e.Curve) - if curve[0:6] == "Circle" or curve == "": - dir = e.Curve.Axis - center = e.Curve.Center - if curve == "": - dim1 = e.Curve.MinorRadius - dim2 = e.Curve.MajorRadius - else: - dim1 = e.Curve.Radius - dim2 = e.Curve.Radius - plane = GEOUNED_Surface( - ("Plane", (center, dir, dim1, dim2)), Box, Face="Build" - ) - id, exist = Surfaces.addPlane(plane) - if exist: - pp = Surfaces.getSurface(id) - if isOposite(plane.Surf.Axis, pp.Surf.Axis, tol.pln_angle): - id = -id - planesId.append(id) - return planesId - - -def addConePlane(Definition, conesList, Surfaces, UniverseBox): - x_axis = FreeCAD.Vector(1, 0, 0) - y_axis = FreeCAD.Vector(0, 1, 0) - z_axis = FreeCAD.Vector(0, 0, 1) - - for cid in conesList: - cone = Surfaces.getSurface(abs(cid)) - if ( - isParallel(cone.Surf.Axis, x_axis, tol.angle) - or isParallel(cone.Surf.Axis, y_axis, tol.angle) - or isParallel(cone.Surf.Axis, z_axis, tol.angle) - ): - continue - - plane = GEOUNED_Surface( - ("Plane", (cone.Surf.Apex, cone.Surf.Axis, 1, 1)), UniverseBox, Face="Build" - ) - pid, exist = Surfaces.addPlane(plane) - - if exist: - p = Surfaces.getSurface(pid) - if isOposite(plane.Surf.Axis, p.Surf.Axis, tol.pln_angle): - pid = -pid - - if cid > 0: - insertInSequence(Definition, cid, -pid, "OR") - else: - insertInSequence(Definition, cid, pid, "AND") diff --git a/src/geouned/GEOUNED/Cuboid/translate.py b/src/geouned/GEOUNED/Cuboid/translate.py deleted file mode 100644 index b07a6d61..00000000 --- a/src/geouned/GEOUNED/Cuboid/translate.py +++ /dev/null @@ -1,163 +0,0 @@ -import FreeCAD -import Part - -from ..Decompose import Decom_one as Decom -from ..Utils import BasicFunctions_part2 as BF -from ..Utils import Geometry_GU as GU -from ..Utils.BasicFunctions_part1 import isOposite, isParallel -from ..Utils.booleanFunction import BoolSequence -from ..Utils.Options.Classes import Options as opt -from ..Utils.Options.Classes import Tolerances as tol - - -def commonEdge(face1, face2): - for e1 in face1.Edges: - for e2 in face2.Edges: - if e1.isSame(e2): - return e1 - return None - - -def isConvex(face1, face2, edge): - de = 0.1 - tol = 1.0e-5 - e = edge.Vertexes[1].Point - edge.Vertexes[0].Point - e.normalize() - V = e.cross(face2.Surface.Axis) - - P = edge.CenterOfMass + de * V - if not face2.__face__.isInside(P, tol, True): - V = -V - - convex = False - if face1.Surface.Axis.dot(V) < 0: - if face1.Orientation == "Forward": - convex = True - else: - if face1.Orientation == "Reversed": - convex = True - return convex - - -def removeElement(Faces, idf): - for i, f in enumerate(Faces): - if f[0] == idf: - del Faces[i] - break - - -def isInverted(solid): - face = solid.Faces[0] - Range = face.ParameterRange - u = (Range[1] + Range[0]) / 2.0 - v = (Range[3] + Range[2]) / 2.0 - - point2 = face.CenterOfMass.add(face.normalAt(u, v).multiply(1.0e-6)) - - if solid.isInside(point2, 1e-7, False): - return True - else: - return False - - -def getId(facein, Surfaces): - - if isParallel(facein.Axis, FreeCAD.Vector(1, 0, 0), tol.pln_angle): - P = "PX" - elif isParallel(facein.Axis, FreeCAD.Vector(0, 1, 0), tol.pln_angle): - P = "PY" - elif isParallel(facein.Axis, FreeCAD.Vector(0, 0, 1), tol.pln_angle): - P = "PZ" - else: - P = "P" - - for s in Surfaces[P]: - if BF.isSamePlane( - facein, - s.Surf, - dtol=tol.pln_distance, - atol=tol.pln_angle, - relTol=tol.relativeTol, - ): - return s.Index - - return 0 - - -def translate(MetaList, Surfaces, UniverseBox, setting): - totsolid = len(MetaList) - for i, m in enumerate(MetaList): - if m.IsEnclosure: - continue - print("Decomposing solid: {}/{} ".format(i, totsolid)) - if setting["debug"]: - print(m.Comments) - if m.IsEnclosure: - m.Solids[0].exportStep("origEnclosure_{}.stp".format(i)) - else: - m.Solids[0].exportStep("origSolid_{}.stp".format(i)) - - Surfaces.extend( - Decom.ExtractSurfaces( - Part.makeCompound(m.Solids), "Plane3Pts", UniverseBox, MakeObj=False - ) - ) - setDefinition(m, Surfaces) - - -def setDefinition(metaObj, Surfaces): - solids = metaObj.Solids - sDef = BoolSequence(operator="OR") - - for sol in solids: - subSol = BoolSequence(operator="AND") - flag_inv = isInverted(sol) - solid_GU = GU.solid_GU(sol, plane3Pts=True) - - Faces = [] - for face in solid_GU.Faces: - if abs(face.Area) < 1e-2: - continue - if face.Area < 0: - if opt.verbose: - print("Warning : Negative surface Area") - if str(face.Surface) != "": - print("Warning : All surfaces must be plane") - continue - if face.Orientation not in ("Forward", "Reversed"): - continue - - id = getId(face.Surface, Surfaces) - s = Surfaces.getSurface(id) - if isOposite(face.Surface.Axis, s.Surf.Axis, tol.pln_angle): - id = -id - if face.Orientation == "Forward": - id = -id - if flag_inv: - id = -id - Faces.append((id, face)) - - while len(Faces) > 0: - id1, face1 = Faces[0] - noConvex = [] - for id2, face2 in Faces[1:]: - edge = commonEdge(face1, face2) - if edge is None: - continue - if not isConvex(face1, face2, edge): - noConvex.append(id2) - - if noConvex != []: - noConvex.insert(0, id1) - for i in noConvex: - removeElement(Faces, i) - orPlanes = BoolSequence(operator="OR") - orPlanes.append(*noConvex) - subSol.append(orPlanes) - else: - removeElement(Faces, id1) - subSol.append(id1) - - sDef.append(subSol) - - metaObj.setDefinition(sDef) diff --git a/src/geouned/GEOUNED/LoadFile/LoadFunctions.py b/src/geouned/GEOUNED/LoadFile/LoadFunctions.py deleted file mode 100644 index cb56527d..00000000 --- a/src/geouned/GEOUNED/LoadFile/LoadFunctions.py +++ /dev/null @@ -1,375 +0,0 @@ -import re - -import FreeCAD - -from ..Utils.Functions import GEOUNED_Solid -from ..Utils.Options.Classes import Options as opt - - -def GetLabel(label): - """Deleting the last word of the string if this is a number - Only if the option delLastNumber is True""" - if not opt.delLastNumber: - return label - wrd = label.split() - try: - new_label = " ".join(wrd[:-1]) - return new_label - except: - return label - - -def getCommentTree(obj): - recursiveList = [] - cObj = obj - while cObj.InList: - label = GetLabel(cObj.InList[0].Label) - recursiveList.append(label) - cObj = cObj.InList[0] - - comment = "" - for label in reversed(recursiveList): - comment += "/" + label - return comment - - -def joinEnvelopes(MetaList): - joinList = [] - oldID = -1 - newlist = [] - for i, m in enumerate(MetaList): - if m.CellType != "envelope": - continue - if m.EnclosureID != oldID: - joinList.append(newlist) - newlist = [i] - oldID = m.EnclosureID - else: - newlist.append(i) - - joinList.append(newlist) - del joinList[0] - - for IDlist in joinList: - fuseMetaObj(MetaList, IDlist[0], IDlist[-1] + 1) - - -def fuseMetaObj(MetaList, init, end): - if end - init == 1: - MetaList[init].__id__ = init + 1 - return - solids = [] - for m in MetaList[init:end]: - solids.extend(m.Solids) - - newMeta = GEOUNED_Solid(init + 1, solids) - newMeta.EnclosureID = MetaList[init].EnclosureID - newMeta.ParentEnclosureID = MetaList[init].ParentEnclosureID - newMeta.IsEnclosure = MetaList[init].IsEnclosure - newMeta.CellType = MetaList[init].CellType - - del MetaList[init:end] - MetaList.insert(init, newMeta) - - -# Paco mod -def checkCADFileMaterial(Material, mdict): - """Function to check that if a component in the CAD file has a material, it exist in the material file""" - templist = [elem for elem in set(Material) if elem not in mdict] - if templist: - raise ValueError( - "At least one material in the CAD model is not present in the material file\n" - f"List of not present materials:{templist}\n" - "Code STOPS\n" - ) - return - - -# Paco mod -def setEnclosureSolidList(MetaList): - """Function to define DataList list.""" - return [elem for elem in MetaList if elem.IsEnclosure] - - -def RemoveEnclosure(MetaList): - """This function removes Enclosure solid and actualises __id__ attributes to take into account the removal from MetaList list of nested enclosures originally loaded from CAD model.""" - - # update id of solids - lenMetaList = len(MetaList) - icount = 0 - for i, m in enumerate(MetaList): - if m.IsEnclosure: - icount += 1 - else: - MetaList[i].__id__ -= icount - - # remove Enclosure solids from metaList - for i in range(lenMetaList - 1, -1, -1): - if MetaList[i].IsEnclosure: - MetaList.pop(i) - - -def setEnclosureLevels(EnclosureList): - nestedList = [[0], []] - nestedEnclosure = [[]] - parentLevel = 0 - tempList = EnclosureList[:] - - while len(tempList) > 0: - remove = [] - for i, encl in enumerate(tempList): - if encl.ParentEnclosureID in nestedList[parentLevel]: - nestedList[parentLevel + 1].append(encl.EnclosureID) - nestedEnclosure[parentLevel].append(encl) - remove.append(i) - - nestedList.append([]) - nestedEnclosure.append([]) - parentLevel += 1 - remove.reverse() - for i in remove: - del tempList[i] - - nestedEnclosureList = [] - for Level in nestedEnclosure: - temp = [] - for i, encl in enumerate(Level): - temp.append((encl.ParentEnclosureID, i)) - temp.sort() - - grouped = [] - for t in temp: - grouped.append(Level[t[1]]) - nestedEnclosureList.append(grouped) - - for i, Level in enumerate(nestedEnclosureList[0:-1]): - for encl in Level: - childList = [] - for child in nestedEnclosureList[i + 1]: - if child.ParentEnclosureID == encl.EnclosureID: - childList.append(child) - encl.SonEnclosures = childList[:] - - return nestedEnclosureList - - -def checkEnclosure(FreeCAD_doc, EnclosureList): - - stop = False - # check all enclosure labels have an associated solid - TempList = [] - for elem in FreeCAD_doc.Objects: - if elem.TypeId == "Part::Feature": - if elem.Shape.Solids: - if elem.InList: - templabel = re.search( - "enclosure(?P[0-9]+)_(?P[0-9]+)_", - elem.InList[0].Label, - ) - if templabel is not None: - if ( - elem.TypeId == "Part::Feature" - and len(elem.Shape.Solids) == 0 - ): - TempList.append(elem) - - if TempList: - stop = True - print( - "One or more nested enclosure labels in CAD solid tree view/structure tree do not have any CAD solid.\n", - "Each nested enclosure must have only one solid.\nCode STOPS.", - "\nList of problematic nested enclosure labels:", - ) - for elem in TempList: - print(elem.EnclosureID) - - # check enclosure Labels don't make loops - - # 1) check at leat one 0 is in Parent enclosure label - # 2) check 0 is not in child enclosure label - # 3) check child enclosure label is not repeated - - SIDList = [] - PIDSet = set() - repeatedID = set() - - for encl in EnclosureList: - PIDSet.add(encl.ParentEnclosureID) - if encl.EnclosureID in SIDList: - repeatedID.add(encl.EnclosureID) - else: - SIDList.append(encl.EnclosureID) - - if 0 in SIDList: - stop = True - print('"0" cannot be label on child Enclosure') - if 0 not in PIDSet: - stop = True - print(' "0" should parent label of most external enclosure(s)') - if repeatedID: - stop = True - print("Child label cannot be repeated.\nRepeated labels :") - for lab in repeatedID: - print(lab) - - # this stop should not be move to the end of routine - # if previous point not satisfied point 4) may lead to infinite loop - if stop: - raise ValueError("Exiting to avoid infinite loop") - - # 4) look for explicit loops - enclDict = dict() - for encl in EnclosureList: - if encl.ParentEnclosureID in enclDict.keys(): - enclDict[encl.ParentEnclosureID].append(encl.EnclosureID) - else: - enclDict[encl.ParentEnclosureID] = [encl.EnclosureID] - - parent = [0] - cont = True - while cont: - cont = False - nextParent = [] - for p in parent: - if p in enclDict.keys(): - cont = True - nextParent.extend(enclDict[p]) - del enclDict[p] - parent = nextParent - - if enclDict.keys(): - print("Following enclosure produce loop") - for p in enclDict.keys(): - for s in enclDict[p]: - print("{}_{}".format(s, p)) - raise ValueError("GEOUNED.LoadFunctions.checkEnclosure failed") - - # check enclosures solids are correctly nested and don't overlap each other - nestedLevels = setEnclosureLevels(EnclosureList) - - overlap = [] - enclTree = [[0]] - for level in nestedLevels: - enclTree = updateTree(enclTree, level) - for encl in level: - sameParent = dict() - if encl.ParentEnclosureID in sameParent.keys(): - sameParent[encl.ParentEnclosureID].append(encl.EnclosureID) - else: - sameParent[encl.ParentEnclosureID] = [encl.EnclosureID] - for encl in sameParent.values(): - overlap.extend(checkOverlap(encl)) - - notEmbedded = [] - for chain in enclTree: - up = chain[0] - for low in chain[1:]: - inter = up.checkIntersection(low.CADSolid) - if inter != -2: - notEmbedded.append((low.EnclosureID, up.EnclosureID)) - - if notEmbedded: - stop = True - print(" Following enclosures are not fully embedded in Parent enclosure") - for elemt in notEmbedded: - print("{}_{}").format(elemt[0], elemt[1]) - - if overlap: - stop = True - print(" Following enclosures overlapping ") - for elemt in overlap: - print("{}_{}").format(elemt[0], elemt[1]) - - if stop: - raise ValueError("GEOUNED.LoadFunctions.checkEnclosure failed") - - -def checkOverlap(enclosures): - overlap = [] - for i, enc1 in enumerate(enclosures): - for enc2 in enclosures[i + 1 :]: - inter = enc1.checkIntersection(enc2.CADSolid) - if inter != 1: - overlap.append((enc1.EnclosureID, enc2.EnclosureID)) - return overlap - - -def updateTree(Tree, level): - newTree = [] - for encl in level: - for lst in Tree: - if lst[-1] == encl.ParentEnclosureID: - newTree.append(lst + [encl.EnclosureID]) - continue - return newTree - - -def set_docOptions(): - # set import step document options for FreeCAD version >0.18 compatible with FreeCAD0.18 opening options - p0 = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Import") - p0.SetBool("UseLinkGroup", False) - p0.SetBool("ReduceObjects", False) - p0.SetBool("ExpandCompound", False) - - p1 = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Import/hSTEP") - p1.SetBool("UseLinkGroup", False) - p1.SetBool("ReadShapeCompoundMode", True) - - -# functions not used in the code -def checkIndex(docList, index, returnObject=False): - subList = docList.ElementList - for i in index[:-1]: - if len(subList) > i: - subList = subList[i] - if subList.TypeId != "App::LinkGroup": - return None - subList = subList.ElementList - else: - return None - - if len(subList) > index[-1]: - if subList[index[-1]].TypeId == "App::LinkGroup": - return "Link" - else: - if returnObject: - return subList[index[-1]] - else: - return "Feature" - else: - return None - - -def nextIndex(docList, lastIndex=None): - if lastIndex is None: - lastIndex = [0] - while checkIndex(docList, lastIndex) != "Feature": - lastIndex.append(0) - else: - return lastIndex - - tmp = lastIndex[:] - move = True - while True: - if move: - tmp[-1] = tmp[-1] + 1 - obj = checkIndex(docList, tmp) - if obj == "Feature": - return tmp - elif obj == "Link": - tmp.append(0) - move = False - else: - tmp = tmp[0:-1] - move = True - if len(tmp) == 0: - return None - - -def solidGenerator(doclist): - last = None - while True: - last = nextIndex(doclist, last) - if last is None: - break - yield checkIndex(doclist, last, True) diff --git a/src/geouned/GEOUNED/LoadFile/LoadSTEP.py b/src/geouned/GEOUNED/LoadFile/LoadSTEP.py deleted file mode 100644 index b54057b9..00000000 --- a/src/geouned/GEOUNED/LoadFile/LoadSTEP.py +++ /dev/null @@ -1,194 +0,0 @@ -# -# Module to load a STEP file -# -import os -import re - -import FreeCAD -import Part -from FreeCAD import Import - -from ..Utils import Functions as UF -from . import LoadFunctions as LF - - -# Paco mod -def extractMaterials(filename): - rhoreal = [] - mdict = {} # _ Material dictionary - with open(filename, "rt") as file: - for line in file: - vals = line.split() - if vals[0].startswith("#"): - continue - matlabel = int(vals[0]) - rhoreal = -float(vals[1]) - matname = " ".join(vals[2:]) - mdict[matlabel] = (rhoreal, matname) - return mdict - - -def LoadCAD(filename, matfilename, defaultMat=[], compSolids=True): - - # Set document solid tree options when opening CAD differing from version 0.18 - if int(FreeCAD.Version()[1]) > 18: - LF.set_docOptions() - - CAD_simplificado_doc = FreeCAD.newDocument("CAD_simplificado") - Import.insert(filename, "CAD_simplificado") - - if matfilename != "": - if os.path.exists(matfilename): - mdict = extractMaterials(matfilename) - else: - print("Material definition file {} does not exist.".format(matfilename)) - mdict = {} - else: - mdict = {} - - s = Part.Shape() - s.read(filename) - Solids = s.Solids - MetaList = [] - for i, s in enumerate(Solids): - MetaList.append(UF.GEOUNED_Solid(i + 1, s)) - - isolid = 0 - missingMat = set() - - docObjects = CAD_simplificado_doc.Objects - - for elem in docObjects: - if elem.TypeId == "Part::Feature": - comment = LF.getCommentTree(elem) - if not elem.Shape.Solids: - print( - "Warning: Element {:} has no associated solid".format( - comment + "/" + elem.Label - ) - ) - continue - else: - tempremat = None - tempredil = None - - # MIO: lightly modification of label if required - label = LF.GetLabel(elem.Label) - comment = comment + "/" + label - if elem.InList: - # MIO: lightly modification of label if required - label_inList = LF.GetLabel(elem.InList[0].Label) - enclLabel = re.search( - "enclosure(?P[0-9]+)_(?P[0-9]+)_", label_inList - ) - if not enclLabel: - enclLabel = re.search( - "enclosure(?P[0-9]+)_(?P[0-9]+)_", label - ) - - envelLabel = re.search( - "envelope(?P[0-9]+)_(?P[0-9]+)_", label_inList - ) - if not envelLabel: - envelLabel = re.search( - "envelope(?P[0-9]+)_(?P[0-9]+)_", label - ) - - # tempremat = re.search("(m(?P\d+)_)",elem.Label) - # if not tempremat : - # tempremat = re.search("(m(?P\d+)_)",elem.InList[0].Label) - - # tempredil = re.search("(_d(?P\d*\.\d*)_)",elem.Label) - # if not tempredil : - # tempredil = re.search("(_d(?P\d*\.\d*)_)",elem.InList[0].Label) - - # Paco modifications - # Search for material definition in tree - xelem = [elem] - while xelem and not tempremat: - # MIO: Modification of label if required - temp_label = LF.GetLabel(xelem[0].Label) - tempremat = re.search("_m(?P\d+)_", "_" + temp_label) - xelem = xelem[0].InList - - # Search for dilution definition in tree - xelem = [elem] - while xelem and not tempredil: - # MIO: Modification of label if required - temp_label = LF.GetLabel(xelem[0].Label) - tempredil = re.search("_d(?P\d*\.\d*)_", temp_label) - xelem = xelem[0].InList - # Paco end - else: - enclLabel = None - envelLabel = None - - # compSolid Diferent solid of the same cell are stored in the same metaObject (compSolid) - # enclosures and envelopes are always stored as compound - if compSolids or enclLabel or envelLabel: - - init = isolid - end = isolid + len(elem.Shape.Solids) - LF.fuseMetaObj(MetaList, init, end) - nsolids = 1 - else: - nsolids = len(elem.Shape.Solids) - - for i in range(nsolids): - MetaList[isolid].setComments("{}{}".format(comment, (i + 1))) - MetaList[isolid].setCADSolid() - - if tempremat: - matlabel = int(tempremat.group("mat")) - if matlabel in mdict.keys(): - MetaList[isolid].setMaterial( - matlabel, mdict[matlabel][0], mdict[matlabel][1] - ) - else: - if matlabel == 0: - MetaList[isolid].setMaterial(matlabel, 0, 0) - else: - MetaList[isolid].setMaterial( - matlabel, - -100, - "Missing material density information", - ) - missingMat.add(matlabel) - else: - # print('Warning : No material label associated to solid {}.\nDefault material used instead.'.format(comment)) - if defaultMat: - MetaList[isolid].setMaterial(*defaultMat) - if tempredil: - MetaList[isolid].setDilution(float(tempredil.group("dil"))) - - if enclLabel is not None: - MetaList[isolid].EnclosureID = int(enclLabel.group("encl")) - MetaList[isolid].ParentEnclosureID = int( - enclLabel.group("parent") - ) - MetaList[isolid].IsEnclosure = True - MetaList[isolid].CellType = "void" - - if envelLabel is not None: - MetaList[isolid].EnclosureID = int(envelLabel.group("env")) - MetaList[isolid].ParentEnclosureID = int( - envelLabel.group("parent") - ) - MetaList[isolid].IsEnclosure = True - MetaList[isolid].CellType = "envelope" - isolid += 1 - - LF.joinEnvelopes(MetaList) - if missingMat: - print( - "Warning!! At least one material in the CAD model is not present in the material file" - ) - print("List of not present materials:", missingMat) - - EnclosureList = LF.setEnclosureSolidList(MetaList) - if EnclosureList: - LF.checkEnclosure(CAD_simplificado_doc, EnclosureList) - # LF.RemoveEnclosure(MetaList) - return MetaList, EnclosureList - else: - return MetaList, [] diff --git a/src/geouned/GEOUNED/Utils/BasicFunctions_part1.py b/src/geouned/GEOUNED/Utils/BasicFunctions_part1.py deleted file mode 100644 index f5ccfc99..00000000 --- a/src/geouned/GEOUNED/Utils/BasicFunctions_part1.py +++ /dev/null @@ -1,233 +0,0 @@ -# -# Set of useful functions used in different parts of the code -# -import math - -import FreeCAD - - -def isSameValue(v1, v2, tolerance=1e-6): - return abs(v1 - v2) < tolerance - - -def isOposite(Vector1, Vector2, tolerance=1e-6): - return abs(Vector1.getAngle(-Vector2)) < tolerance - - -def isParallel(Vector1, Vector2, tolerance=1e-6): - angle = abs(Vector1.getAngle(Vector2)) - return angle < tolerance or isSameValue(angle, math.pi, tolerance) - - -def isInLine(Point, Dir, Pnt_line, tolerance=1e-6): - r12 = Point - Pnt_line - return isParallel(Dir, r12) or (r12.Length < tolerance) - - -def isInPoints(point, Points, tolerance=1e-5): - if len(Points) > 0: - for P in Points: - if point.isEqual(P, tolerance): - return True - return False - - -def isInEdge(Edge1, Edge2, tolerance=1e-8): - Ver1 = Edge1.Vertexes - Ver2 = Edge2.Vertexes - con1 = Ver1[0].Point.isEqual(Ver2[0].Point, tolerance) or Ver1[0].Point.isEqual( - Ver2[1].Point, tolerance - ) - con2 = Ver1[1].Point.isEqual(Ver2[0].Point, tolerance) or Ver1[1].Point.isEqual( - Ver2[1].Point, tolerance - ) - return con1 and con2 - - -def isInPlane(Point, plane, dtolerance=1e-7): - return abs(Point.distanceToPlane(plane.Surf.Position, plane.Surf.Axis)) < dtolerance - - -def isInTolerance(val, tol, fuzzyLow, fuzzyHigh): - if abs(val) < fuzzyLow: - return True, False # 1) isintolerance 2) fuzzy - elif abs(val) < tol: - return True, True - elif abs(val) > fuzzyHigh: - return False, False - else: - return False, True - - -def signPlane(Point, plane): - value = plane.Surf.Axis.dot(Point - plane.Surf.Position) - if value >= 0.0: - sign = 1 - else: - sign = -1 - return sign - - -def pointsToCoeffs(Points): - p1, p2, p3 = Points[0:3] - scf = (p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, p3.x, p3.y, p3.z) - - # mcnp implementation to convert 3 point plane to - # plane parameters - - tpp = [0] * 4 - for i in range(1, 4): - j = i % 3 + 1 - k = 6 - i - j - k -= 1 - j -= 1 - tpp[i - 1] = ( - scf[j] * (scf[k + 3] - scf[k + 6]) - + scf[j + 3] * (scf[k + 6] - scf[k]) - + scf[j + 6] * (scf[k] - scf[k + 3]) - ) - tpp[3] += scf[i - 1] * (scf[j + 3] * scf[k + 6] - scf[j + 6] * scf[k + 3]) - - xm = 0 - coeff = [0] * 4 - for i in range(1, 5): - if xm == 0 and tpp[4 - i] != 0: - xm = 1 / tpp[4 - i] - coeff[4 - i] = tpp[4 - i] * xm - - Axis = FreeCAD.Vector(coeff[0:3]) - Distance = coeff[3] / Axis.Length - Axis.normalize() - - return Axis, Distance - - -class Plane3PtsParams: - def __init__(self, params, real=True): - self.Position = params[0] - self.Axis = params[1] - self.dimL1 = params[2] - self.dimL2 = params[3] - self.Points = params[4] - self.real = real - self.pointDef = True - - def __str__(self): - # outstr = '''Plane : - # Point 1 : {P1[0]} {P1[1]} {P1[2]} - # Point 2 : {P2[0]} {P2[1]} {P2[2]} - # Point 3 : {P3[0]} {P3[1]} {P3[2]} '''.format(P1=self.Points[0], P2=self.Points[1], P3=self.Points[2]) - pos = self.Axis.dot(self.Position) - outstr = """Plane : - Axis : {} {} {} - Position : {} """.format( - self.Axis.x, self.Axis.y, self.Axis.z, pos - ) - return outstr - - -class PlaneParams: - def __init__(self, params, real=True): - self.Position = params[0] - self.Axis = params[1] - self.dimL1 = params[2] - self.dimL2 = params[3] - self.real = real - self.pointDef = False - - def __str__(self): - pos = self.Axis.dot(self.Position) - outstr = """Plane : - Axis : {} {} {} - Position : {} """.format( - self.Axis.x, self.Axis.y, self.Axis.z, pos - ) - return outstr - - -class CylinderParams: - def __init__(self, params, real=True): - self.Center = params[0] - self.Axis = params[1] - self.Radius = params[2] - self.dimL = params[3] - self.real = real - - def __str__(self): - outstr = """Cylinder : - Axis : {} {} {} - Center : {} {} {} - Radius : {} """.format( - self.Axis.x, - self.Axis.y, - self.Axis.z, - self.Center.x, - self.Center.y, - self.Center.z, - self.Radius, - ) - return outstr - - -class ConeParams: - def __init__(self, params, real=True): - self.Apex = params[0] - self.Axis = params[1] - self.SemiAngle = params[2] - self.dimL = params[3] - self.dimR = params[4] - self.real = real - - def __str__(self): - outstr = """Cone : - Axis : {} {} {} - Center : {} {} {} - SemiAngle: {} """.format( - self.Axis.x, - self.Axis.y, - self.Axis.z, - self.Apex.x, - self.Apex.y, - self.Apex.z, - self.SemiAngle, - ) - return outstr - - -class SphereParams: - def __init__(self, params): - self.Center = params[0] - self.Radius = params[1] - - def __str__(self): - outstr = """Sphere : - Center : {} {} {} - Radius : {} """.format( - self.Center.x, self.Center.y, self.Center.z, self.Radius - ) - return outstr - - -class TorusParams: - def __init__(self, params): - self.Center = params[0] - self.Axis = params[1] - self.MajorRadius = params[2] - self.MinorRadius = params[3] - - def __str__(self): - outstr = """Torus : - Axis : {} {} {} - Center : {} {} {} - MajorRadius: {} - MinorRadius: {} """.format( - self.Axis.x, - self.Axis.y, - self.Axis.z, - self.Center.x, - self.Center.y, - self.Center.z, - self.MajorRadius, - self.MinorRadius, - ) - return outstr diff --git a/src/geouned/GEOUNED/Utils/BasicFunctions_part2.py b/src/geouned/GEOUNED/Utils/BasicFunctions_part2.py deleted file mode 100644 index 430e1303..00000000 --- a/src/geouned/GEOUNED/Utils/BasicFunctions_part2.py +++ /dev/null @@ -1,331 +0,0 @@ -# -# Set of useful functions used in different parts of the code -# -import math - -import FreeCAD - -from ..Utils.BasicFunctions_part1 import ( - isInLine, - isInPlane, - isInTolerance, - isOposite, - isParallel, - isSameValue, -) -from ..Write.Functions import MCNPSurface - -sameSurfFic = open("fuzzySurfaces", "w") - - -def Fuzzy(index, dtype, surf1, surf2, val, tol): - - same = val <= tol - - if dtype == "plane": - p1str = MCNPSurface(index, "Plane", surf1) - p2str = MCNPSurface(0, "Plane", surf2) - line = "Same surface : {}\nPlane distance / Tolerance : {} {}\n {}\n {}\n\n".format( - same, val, tol, p1str, p2str - ) - sameSurfFic.write(line) - - elif dtype == "cylRad": - cyl1str = MCNPSurface(index, "Cylinder", surf1) - cyl2str = MCNPSurface(0, "Cylinder", surf2) - line = "Same surface : {}\nDiff Radius / Tolerance: {} {}\n {}\n {}\n\n".format( - same, val, tol, cyl1str, cyl2str - ) - sameSurfFic.write(line) - - elif dtype == "cylAxs": - cyl1str = MCNPSurface(index, "Cylinder", surf1) - cyl2str = MCNPSurface(0, "Cylinder", surf2) - line = "Same surface : {}\nDist Axis / Tolerance: {} {}\n {}\n {}\n\n".format( - same, val, tol, cyl1str, cyl2str - ) - sameSurfFic.write(line) - - -def isSamePlane(p1, p2, dtol=1e-6, atol=1e-6, relTol=True, fuzzy=(False, 0)): - if isParallel(p1.Axis, p2.Axis, atol): - d1 = p1.Axis.dot(p1.Position) - d2 = p2.Axis.dot(p2.Position) - if isOposite(p1.Axis, p2.Axis, atol): - d2 = -d2 - d = abs(d1 - d2) - if relTol: - tol = dtol * max(p2.dim1, p2.dim2) - else: - tol = dtol - - isSame, isFuzzy = isInTolerance(d, tol, 0.5 * tol, 2 * tol) - if isFuzzy and fuzzy[0]: - Fuzzy(fuzzy[1], "plane", p2, p1, d, tol) - return isSame - return False - - -def isSameCylinder(cyl1, cyl2, dtol=1e-6, atol=1e-6, relTol=True, fuzzy=(False, 0)): - if relTol: - rtol = dtol * max(cyl2.Radius, cyl1.Radius) - else: - rtol = dtol - - isSameRad, isFuzzy = isInTolerance( - cyl2.Radius - cyl1.Radius, rtol, 0.5 * rtol, 2 * rtol - ) - if isFuzzy and fuzzy[0]: - Fuzzy(fuzzy[1], "cylRad", cyl2, cyl1, abs(cyl2.Radius - cyl1.Radius), rtol) - - if isSameRad: - if isParallel(cyl1.Axis, cyl2.Axis, atol): - c12 = cyl1.Center - cyl2.Center - d = cyl1.Axis.cross(c12).Length - - if relTol: - tol = dtol * max(cyl1.Center.Length, cyl2.Center.Length) - else: - tol = dtol - - isSameCenter, isFuzzy = isInTolerance(d, tol, 0.5 * tol, 2 * tol) - if isFuzzy and fuzzy[0]: - Fuzzy(fuzzy[1], "cylAxs", cyl1, cyl2, d, tol) - - return isSameCenter - return False - - -def isSameCone(cone1, cone2, dtol=1e-6, atol=1e-6, relTol=True): - if isSameValue(cone1.SemiAngle, cone2.SemiAngle, atol): - if isParallel(cone1.Axis, cone2.Axis, atol): - if relTol: - tol = dtol * max(cone1.Apex.Length, cone2.Apex.Length) - else: - tol = dtol - return cone1.Apex.isEqual(cone2.Apex, tol) - return False - - -def isSameSphere(sph1, sph2, tolerance=1e-6, relTol=True): - if relTol: - rtol = tolerance * max(sph2.Radius, sph1.Radius) - else: - rtol = tolerance - if isSameValue(sph1.Radius, sph2.Radius, rtol): - if relTol: - ctol = tolerance * max(sph2.Center.Length, sph1.Center.Length) - else: - ctol = tolerance - return sph1.Center.isEqual(sph2.Center, ctol) - - return False - - -def isSameTorus(tor1, tor2, dtol=1e-6, atol=1e-6, relTol=True): - if isParallel(tor1.Axis, tor2.Axis, atol): - if tor1.Axis.dot(tor2.Axis) < 0: - return False # Assume same cone with oposite axis as different - if relTol: - Rtol = dtol * max(tor1.MajorRadius, tor2.MajorRadius) - rtol = dtol * max(tor1.MinorRadius, tor2.MinorRadius) - else: - Rtol = dtol - rtol = dtol - - if isSameValue(tor1.MajorRadius, tor2.MajorRadius, Rtol) and isSameValue( - tor1.MinorRadius, tor2.MinorRadius, rtol - ): - if relTol: - ctol = dtol * max(tor1.Center.Length, tor2.Center.Length) - else: - ctol = dtol - return tor1.Center.isEqual(tor2.Center, ctol) - return False - - -def isDuplicateInList(num_str1, i, lista): - for j, elem2 in enumerate(lista): - if i == j: - continue - num_str2 = "%11.4E" % (elem2) - num_str3 = "%11.4E" % (elem2 + 2.0 * math.pi) - num_str4 = "%11.4E" % (elem2 - 2.0 * math.pi) - - if abs(float(num_str2)) < 1.0e-5: - num_str2 = "%11.4E" % 0.0 - - if abs(float(num_str3)) < 1.0e-5: - num_str3 = "%11.4E" % 0.0 - - if abs(float(num_str4)) < 1.0e-5: - num_str4 = "%11.4E" % 0.0 - - if num_str1 == num_str2 or num_str1 == num_str3 or num_str1 == num_str4: - return True - - return False - - -def isInFaces(face, Faces): - - if Faces == []: - return False - vector_nulo = FreeCAD.Vector(0, 0, 0) - surface = face.Surface - kind_surf = str(face.Surface) - if kind_surf == "": - Axis = surface.Axis - Position = surface.Position - - elif kind_surf == "": - Axis = surface.Axis - Radius = surface.Radius - Center = surface.Center - - elif kind_surf == "": - Axis = surface.Axis - Apex = surface.Apex - SemiAngle = surface.SemiAngle - - elif kind_surf[0:6] == "Sphere": - Center = surface.Center - Radius = surface.Radius - - elif kind_surf == "": - Axis = surface.Axis - Center = surface.Center - MajorRadius = surface.MajorRadius - MinorRadius = surface.MinorRadius - - for elem in Faces: - surf = elem.Surface - ##print surf - if str(surf) == "" and kind_surf == "": - vector_cross = Axis.cross(surf.Axis) - if vector_cross == vector_nulo and isInPlane(Position, surf): - return True - elif str(surf) == "" and kind_surf == "": - dir = surf.Axis - rad = surf.Radius - pnt = surf.Center - vector_cross = Axis.cross(surf.Axis) - if ( - vector_cross == vector_nulo - and Radius == rad - and isInLine(Center, dir, pnt) - ): - return True - elif str(surf) == "" and kind_surf == "": - # corresponding logic for cone - dir = surf.Axis - punta = surf.Apex - semiangle = surf.SemiAngle - if ( - Axis.isEqual(dir, 1e-5) - and Apex.isEqual(punta, 1e-5) - and (SemiAngle - semiangle) < 1e-6 - ): - return True - elif str(surf)[0:6] == "Sphere" and kind_surf[0:6] == "Sphere": - # corresponding logic for sphere - rad = surf.Radius - pnt = surf.Center - if Center == pnt and Radius == rad: - return True - - elif str(surf) == "" and kind_surf == "": - # corresponding logic for Torus - radMaj = surf.MajorRadius - radMin = surf.MinorRadius - pnt = surf.Center - dir = surf.Axis - if ( - Axis.isEqual(dir, 1e-5) - and Center.isEqual(pnt, 1e-5) - and (MajorRadius - radMaj) < 1e-6 - ) and (MinorRadius - radMin) < 1e-6: - return True - - return False - - -def isInFaces2(face, Faces): - - if Faces == []: - return False - vector_nulo = FreeCAD.Vector(0, 0, 0) - surface = face - kind_surf = face.type - if kind_surf == "": - Axis = surface.Axis - Position = surface.Position - - elif kind_surf == "": - Axis = surface.Axis - Radius = surface.Radius - Center = surface.Center - - elif kind_surf == "": - Axis = surface.Axis - Apex = surface.Apex - SemiAngle = surface.SemiAngle - - elif kind_surf[0:6] == "Sphere": - Center = surface.Center - Radius = surface.Radius - - elif kind_surf == "": - Axis = surface.Axis - Center = surface.Center - MajorRadius = surface.MajorRadius - MinorRadius = surface.MinorRadius - - for elem in Faces: - ##print surf - if elem.type == "" and kind_surf == "": - vector_cross = Axis.cross(elem.Axis) - if vector_cross == vector_nulo and isInPlane(Position, elem.Surface): - # if (isParallel(elem.Axis,elem.Surface.Axis) and isInPlane(Position,elem.Surface)): - return True - elif elem.type == "" and kind_surf == "": - dir = elem.Axis - rad = elem.Radius - pnt = elem.Center - vector_cross = Axis.cross(elem.Axis) - if ( - vector_cross == vector_nulo - and Radius == rad - and isInLine(Center, dir, pnt) - ): - return True - elif elem.type == "" and kind_surf == "": - # corresponding logic for cone - dir = elem.Axis - punta = elem.Apex - semiangle = elem.SemiAngle - if ( - Axis.isEqual(dir, 1e-5) - and Apex.isEqual(punta, 1e-5) - and (SemiAngle - semiangle) < 1e-6 - ): - return True - elif elem.type == "Sphere" and kind_surf == "Sphere": - # corresponding logic for sphere - rad = elem.Radius - pnt = elem.Center - if Center == pnt and Radius == rad: - return True - elif elem.type == "" and kind_surf == "": - # corresponding logic for Torus - radMaj = elem.MajorRadius - radMin = elem.MinorRadius - pnt = elem.Center - dir = elem.Axis - if ( - Axis.isEqual(dir, 1e-5) - and Center.isEqual(pnt, 1e-5) - and (MajorRadius - radMaj) < 1e-6 - ) and (MinorRadius - radMin) < 1e-6: - return True - return False diff --git a/src/geouned/GEOUNED/Utils/Options/Classes.py b/src/geouned/GEOUNED/Utils/Options/Classes.py deleted file mode 100644 index fe672f25..00000000 --- a/src/geouned/GEOUNED/Utils/Options/Classes.py +++ /dev/null @@ -1,10 +0,0 @@ -class Options: - pass - - -class MCNP_numeric_format: - pass - - -class Tolerances: - pass diff --git a/src/geouned/GEOUNED/Utils/Options/__init__.py b/src/geouned/GEOUNED/Utils/Options/__init__.py deleted file mode 100644 index 9e84780c..00000000 --- a/src/geouned/GEOUNED/Utils/Options/__init__.py +++ /dev/null @@ -1,77 +0,0 @@ -from .Classes import MCNP_numeric_format, Options, Tolerances - -# default options values -forceCylinder = True # Force using cylinders instead cones for auxillary surfaces of torus surface definition -newSplitPlane = True # Use new module for planes splitting in decomposition module -delLastNumber = False # Deleting the last word in the comment if it is a number -verbose = False # Display information during conversion process -enlargeBox = 2 # Enlarge box Boundary when evaluating Ctable (dimension in mm) -nPlaneReverse = 0 # numbers of plane thresold whether cut of parallel planes are made fisrt with lowest or highest number -splitTolerance = 0 # First try for fuzzy tolerance used in BOPTOOL Split function. If BOPTSplit crash try lower value for tolerance -scaleUp = True # Scale up Fuzzy tolerance once get below 1e-12 -quadricPY = False # use quadric form of cones and cylinder not aligned with X,Y,Z axis when write openMC script file -Facets = False # use alternative conversion module when geometry is defined by cells compound by only triangular plane faces -prnt3PPlane = ( - False # print 3 point plane definition in MCNP output as 3 points coordinates -) -forceNoOverlap = False # force no overlaping cell definition. Adjacent cell definition are rested from current cell definition - -tolValueDict = { - "relativeTol": False, - "relativePrecision": 1.0e-6, # relative precision - "value": 1.0e-6, # Tolerance in single value comparison - "distance": 1.0e-4, # General Distance Tolerance - "angle": 1.0e-4, # General Angle Tolerance - "pln_distance": 1.0e-4, # distance between planes equal planes if distance between parallel planes < 1e-4 cm - "pln_angle": 1.0e-4, # angle between axis. 1e-4 : planes separate each other 0.1mm each 1m - "cyl_distance": 1.0e-4, # distance between radius/center - "cyl_angle": 1.0e-4, # angle between axis - "sph_distance": 1.0e-4, # distance between radius/center - "kne_distance": 1.0e-4, # distance between apex - "kne_angle": 1.0e-4, # angle between semiangles/axis - "tor_distance": 1.0e-4, # distance between Major/Minor radii/center - "tor_angle": 1.0e-4, # angle between axis - "min_area": 1.0e-2, -} # minimun face area to consider in cell definition - - -numValueDict = { - "P_abc": "14.7e", # Plane general a,b,c params - "P_d": "14.7e", # Plane general d params - "P_xyz": "14.7e", # PX/PY/PZ params - "S_r": "14.7e", # SO/SX/SY/SZ/S radius - "S_xyz": "14.7e", # SO/SX/SY/SZ/S center - "C_r": "12f", # Cylinder radius - "C_xyz": "12f", # Cylinder center - "K_xyz": "13.6e", # Cone apex - "K_tan2": "12f", # Cone tan^2 value - "T_r": "14.7e", # Torus radii - "T_xyz": "14.7e", # Torus center - "GQ_1to6": "18.15f", # GQ 1 to 6 coefficients (order 2 x2,y2,z2,xy,...) - "GQ_7to9": "18.15f", # GQ 7 to 9 coefficients (order 1 x,y,z) - "GQ_10": "18.15f", -} # GQ 10 coefficient (order 0) - - -# Set default attributes to Options class -setattr(Options, "forceCylinder", forceCylinder) -setattr(Options, "newSplitPlane", newSplitPlane) -setattr(Options, "enlargeBox", enlargeBox) -setattr(Options, "delLastNumber", delLastNumber) -setattr(Options, "verbose", verbose) -setattr(Options, "nPlaneReverse", nPlaneReverse) -setattr(Options, "splitTolerance", splitTolerance) -setattr(Options, "scaleUp", scaleUp) -setattr(Options, "quadricPY", quadricPY) -setattr(Options, "Facets", Facets) -setattr(Options, "prnt3PPlane", prnt3PPlane) -setattr(Options, "forceNoOverlap", forceNoOverlap) - -# Set default attributes to Tolerances class -for key in tolValueDict.keys(): - setattr(Tolerances, key, tolValueDict[key]) - - -# Set default attributes to MCNP number format class -for key in numValueDict.keys(): - setattr(MCNP_numeric_format, key, numValueDict[key]) diff --git a/src/geouned/GEOUNED/Void/Void.py b/src/geouned/GEOUNED/Void/Void.py deleted file mode 100644 index 4eae1802..00000000 --- a/src/geouned/GEOUNED/Void/Void.py +++ /dev/null @@ -1,198 +0,0 @@ -import FreeCAD -import Part - -from ..LoadFile import LoadFunctions as LF -from ..Utils.BasicFunctions_part1 import isOposite -from ..Utils.booleanFunction import BoolSequence -from ..Utils.Functions import GEOUNED_Solid, GEOUNED_Surface -from ..Utils.Options.Classes import Options as opt -from ..Void import voidFunctions as VF -from .VoidBoxClass import VoidBox - - -def voidGeneration(MetaList, EnclosureList, Surfaces, UniverseBox, setting, init): - voidList = [] - - if EnclosureList: - NestedEnclosure = LF.setEnclosureLevels(EnclosureList) - VF.assignEnclosure(MetaList, NestedEnclosure) - - # add to Metalist Level 1 enclosures, remove from list material cells totally embedded in Level 1 enclosures - newMetaList = VF.selectSolids(MetaList, NestedEnclosure[0], UniverseBox) - else: - newMetaList = MetaList[:] - NestedEnclosure = [] - - Box = Part.makeBox( - UniverseBox.XLength, - UniverseBox.YLength, - UniverseBox.ZLength, - FreeCAD.Vector(UniverseBox.XMin, UniverseBox.YMin, UniverseBox.ZMin), - FreeCAD.Vector(0, 0, 1), - ) - - EnclosureBox = GEOUNED_Solid(None, Box) - if setting["voidMat"]: - voidMat = setting["voidMat"] - EnclosureBox.setMaterial(voidMat[0], voidMat[1], voidMat[2]) - - # get voids in 0 Level Enclosure (original Universe) - # if exist Level 1 enclosures are considered as material cells - print("Build Void highest enclosure") - - voids = GetVoidDef(newMetaList, Surfaces, EnclosureBox, setting, Lev0=True) - voidList.append(voids) - - # Perform enclosure void - # Loop until the lowest enclosure level - - for i, Level in enumerate(NestedEnclosure): - - print("Build Void highest enclosure") - for j, encl in enumerate(Level): - if encl.CellType == "envelope": - continue - newMetaList = VF.selectSolids(MetaList, encl.SonEnclosures, encl) - print("Build Void enclosure {} in enclosure level {}".format(j, i + 1)) - # select solids overlapping current enclosure "encl", and lower level enclosures - voids = GetVoidDef(newMetaList, Surfaces, encl, setting) - voidList.append(voids) - - voidList.append(setGraveyardCell(Surfaces, UniverseBox)) - - return VF.updateVoidList(init, voidList, NestedEnclosure, setting["sortEnclosure"]) - - -def GetVoidDef(MetaList, Surfaces, Enclosure, setting, Lev0=False): - - maxsurf = setting["maxSurf"] - maxbracket = setting["maxBracket"] - minSize = setting["minVoidSize"] - - if "full" in setting["simplify"].lower(): - simplifyVoid = "full" - elif "void" in setting["simplify"].lower(): - simplifyVoid = "diag" - else: - simplifyVoid = "no" - - if Lev0: - Universe = VoidBox(MetaList, Enclosure.BoundBox) - else: - Universe = VoidBox(MetaList, Enclosure.CADSolid) - - Initial = [Universe] - VoidDef = [] - iloop = 0 - while iloop < 50: - Temp = [] - iloop += 1 - nvoid = len(Initial) - print("Loop, Box to Split :", iloop, nvoid) - - for iz, z in enumerate(Initial): - nsurfaces, nbrackets = z.getNumbers() - if opt.verbose: - print( - "{} {}/{} {} {}".format(iloop, iz + 1, nvoid, nsurfaces, nbrackets) - ) - - if nsurfaces > maxsurf and nbrackets > maxbracket: - newspace = z.Split(minSize) - else: - newspace = None - - if type(newspace) is tuple: - Temp.extend(newspace) - else: - # if len(z.Objects) >= 50 : z.refine() - boxDim = ( - z.BoundBox.XMin * 0.1, - z.BoundBox.XMax * 0.1, - z.BoundBox.YMin * 0.1, - z.BoundBox.YMax * 0.1, - z.BoundBox.ZMin * 0.1, - z.BoundBox.ZMax * 0.1, - ) - - print("build complementary {} {}".format(iloop, iz)) - - cell, CellIn = z.getVoidComplementary(Surfaces, simplify=simplifyVoid) - if cell is not None: - VoidCell = (cell, (boxDim, CellIn)) - VoidDef.append(VoidCell) - - Initial = Temp - if len(Temp) == 0: - break - - voidList = [] - for i, vcell in enumerate(VoidDef): - mVoid = GEOUNED_Solid(i) - mVoid.Void = True - mVoid.CellType = "void" - mVoid.setDefinition(vcell[0], simplify=True) - mVoid.setMaterial(Enclosure.Material, Enclosure.Rho, Enclosure.MatInfo) - mVoid.setDilution(Enclosure.Dilution) - - mVoid.__commentInfo__ = vcell[1] - - voidList.append(mVoid) - - return voidList - - -def setGraveyardCell(Surfaces, UniverseBox): - Universe = VoidBox([], UniverseBox) - - externalBox = getUniverseComplementary(Universe, Surfaces) - center = UniverseBox.Center - radius = 0.51 * UniverseBox.DiagonalLength - sphere = GEOUNED_Surface(("Sphere", (center, radius)), UniverseBox) - id, exist = Surfaces.addSphere(sphere) - - sphdef = BoolSequence(str(-id)) - sphdef.operator = "AND" - sphdef.append(externalBox) - - notsph = BoolSequence(str(id)) - - mVoidSphIn = GEOUNED_Solid(0) - mVoidSphIn.Void = True - mVoidSphIn.CellType = "void" - mVoidSphIn.setDefinition(sphdef) - mVoidSphIn.setMaterial(0, 0, "Graveyard_in") - mVoidSphIn.__commentInfo__ = None - - mVoidSphOut = GEOUNED_Solid(1) - mVoidSphOut.Void = True - mVoidSphOut.CellType = "void" - mVoidSphOut.setDefinition(notsph) - mVoidSphOut.setMaterial(0, 0, "Graveyard") - mVoidSphOut.__commentInfo__ = None - - return (mVoidSphIn, mVoidSphOut) - - -def getUniverseComplementary(Universe, Surfaces): - Def = BoolSequence(operator="OR") - for p in Universe.getBoundPlanes(): - id, exist = Surfaces.addPlane(p) - if not exist: - Def.elements.append(-id) - else: - s = Surfaces.getSurface(id) - if isOposite(p.Surf.Axis, s.Surf.Axis): - Def.elements.append(id) - else: - Def.elements.append(-id) - return Def - - -def voidCommentLine(CellInfo): - boxDef, cellIn = CellInfo - cells = ", ".join(map(str, cellIn)) - box = ", ".join(f"{num:.3f}" for num in boxDef) - line = f"Automatic Generated Void Cell. Enclosure({box})\n" - line += f"Enclosed cells : ({cells})\n" - return line diff --git a/src/geouned/GEOUNED/Write/AdditionalFiles.py b/src/geouned/GEOUNED/Write/AdditionalFiles.py deleted file mode 100644 index 968113cb..00000000 --- a/src/geouned/GEOUNED/Write/AdditionalFiles.py +++ /dev/null @@ -1,39 +0,0 @@ -def commentsWrite(name, MetaList): - """Function to write in an independent file the comment strings""" - - outfile = open(name + "_comments.txt", "w", encoding="utf-8") - for m in MetaList: - outfile.write(m.Comments + "\n") - - outfile.close() - - -def summaryWrite(name, MetaList): - - outfile = open(name + "_Summary.txt", "w", encoding="utf-8") - header = " Cell Id{:5s}Mat Id{:6s}Density{:7s}Volume{:5s}Comments\n".format( - "", "", "", "" - ) - outfile.write(header) - - for m in MetaList: - if m.Void or m.__id__ is None: - continue - index = m.__id__ - Vol = m.Volume * 1e-3 - if m.Material == 0: - line = " {:>8d}{:3s}{:>8d}{:3s}{:11s}{:3s}{:11.4e}{:3s}{}\n".format( - index, "", 0, "", "", "", Vol, "", m.Comments - ) - else: - if abs(m.Density) < 1e-2: - line = " {:>8d}{:3s}{:>8d}{:3s}{:11.4e}{:3s}{:11.4e}{:3s}{}\n".format( - index, "", m.Material, "", m.Density, "", Vol, "", m.Comments - ) - else: - line = " {:>8d}{:3s}{:>8d}{:3s}{:11.7f}{:3s}{:11.4e}{:3s}{}\n".format( - index, "", m.Material, "", m.Density, "", Vol, "", m.Comments - ) - - outfile.write(line) - outfile.close() diff --git a/src/geouned/GEOUNED/Write/Functions.py b/src/geouned/GEOUNED/Write/Functions.py deleted file mode 100644 index 4095eb6d..00000000 --- a/src/geouned/GEOUNED/Write/Functions.py +++ /dev/null @@ -1,1000 +0,0 @@ -import math -import re - -import FreeCAD - -from ..Utils import Qform as Qform -from ..Utils.BasicFunctions_part1 import isOposite, isParallel -from ..Utils.Options.Classes import MCNP_numeric_format as nf -from ..Utils.Options.Classes import Options as opt -from ..Utils.Options.Classes import Tolerances as tol -from .StringFunctions import remove_redundant - - -class CardLine: - def __init__(self, card, linesize=80, tabspace=6, fmt=""): - self.str = card + " " - self.lineSize = linesize - self.__leftspace__ = linesize - len(self.str) - self.tabspace = tabspace - self.tabstr = " " * tabspace - self.fmt = fmt - - def extend(self, dataList): - line = "" - for data in dataList: - data_str = "{:{fmt}} ".format(data, fmt=self.fmt) - if self.__leftspace__ - len(data_str) > 0: - line += data_str - self.__leftspace__ -= len(data_str) - else: - self.str += line + "\n" - line = "{}{}".format(self.tabstr, data_str) - self.__leftspace__ = self.lineSize - len(line) - - self.str += line - - -class CellString: - def __init__(self, linesize=80, tabspace=6): - self.str = "" - self.lineSize = linesize - self.__leftspace__ = linesize - self.tabspace = tabspace - self.tabstr = " " * tabspace - - def add(self, string): - self.str += string - - def delLastChar(self): - self.str = self.str[0:-1] - self.__leftspace__ += 1 - - def wrapLine(self, offset=0): - - self.str = self.str.strip() - self.str = re.sub(" +", " ", self.str) - self.str = re.sub(" *\( *", "(", self.str) - self.str = re.sub(" *\) *", ")", self.str) - self.str = re.sub(" *: *", ":", self.str) - self.str = re.sub("\)\(", ") (", self.str) - self.str = re.sub("(?P\d)\(", "\g (", self.str) - self.str = re.sub("\)(?P\d)", ") \g", self.str) - self.str = re.sub("\)-", ") -", self.str) - - if len(self.str) + offset <= self.lineSize: - return - - newline = "" - init = 0 - icount = self.lineSize - 1 - offset - lenleft = len(self.str) - - while True: - while self.str[icount] not in (")", ":", " "): - icount -= 1 - - newline += self.str[init : icount + 1] - lenleft -= icount - init + 1 - init = icount + 1 - icount += self.lineSize - self.tabspace - if lenleft == 0: - break - elif icount < len(self.str): - newline += "\n{}".format(self.tabstr) - else: - newline += "\n{}{}".format(self.tabstr, self.str[init:]) - break - - self.str = newline - - -def changeSurfSign(surf, Seq): - if Seq.level == 0: - for i, e in enumerate(Seq.elements): - if surf == abs(e): - Seq.elements[i] = -Seq.elements[i] - else: - for i, e in enumerate(Seq.elements): - if type(e) is int: - if surf == abs(e): - Seq.elements[i] = -Seq.elements[i] - else: - changeSurfSign(surf, e) - - -def writeMCNPCellDef(Definition, tabspace=0, offset=0): - sdef = CellString(tabspace=tabspace) - strDef = remove_redundant(writeSequenceMCNP(Definition)) - sdef.add(strDef) - sdef.wrapLine(offset) - return sdef.str - - -def writeSerpentCellDef(Definition, tabspace=0, offset=0): - sdef = CellString(tabspace=tabspace) - strDef = remove_redundant(writeSequenceSerpent(Definition)) - sdef.add(strDef) - sdef.wrapLine(offset) - return sdef.str - - -def writePHITSCellDef(Definition, tabspace=0, offset=0): - sdef = CellString(tabspace=tabspace) - strDef = remove_redundant(writeSequencePHITS(Definition)) - sdef.add(strDef) - sdef.wrapLine(offset) - return sdef.str - - -def writeOpenMCregion(Definition, Wtype="XML"): - if Wtype == "XML": - return writeSequenceOMCXML(Definition) - if Wtype == "PY": - return writeSequenceOMCPY(Definition) - - -def writeSequenceMCNP(Seq): - if Seq.level == 0: - if Seq.operator == "AND": - line = "({})".format(" ".join(map(str, Seq.elements))) - else: - line = "({})".format(":".join(map(str, Seq.elements))) - else: - terms = [] - for e in Seq.elements: - if type(e) is int: - terms.append(str(e)) - else: - terms.append(writeSequenceMCNP(e)) - - if Seq.operator == "AND": - line = "({})".format(" ".join(terms)) - else: - line = "({})".format(":".join(terms)) - - return line - - -def writeSequenceSerpent(Seq): - if Seq.level == 0: - if Seq.operator == "AND": - line = "({})".format(" ".join(map(str, Seq.elements))) - else: - line = "({})".format(":".join(map(str, Seq.elements))) - else: - terms = [] - for e in Seq.elements: - if type(e) is int: - terms.append(str(e)) - else: - terms.append(writeSequenceMCNP(e)) - - if Seq.operator == "AND": - line = "({})".format(" ".join(terms)) - else: - line = "({})".format(":".join(terms)) - - return line - - -def writeSequencePHITS(Seq): - if Seq.level == 0: - if Seq.operator == "AND": - line = "({})".format(" ".join(map(str, Seq.elements))) - else: - line = "({})".format(":".join(map(str, Seq.elements))) - else: - terms = [] - for e in Seq.elements: - if type(e) is int: - terms.append(str(e)) - else: - terms.append(writeSequencePHITS(e)) - - if Seq.operator == "AND": - line = "({})".format(" ".join(terms)) - else: - line = "({})".format(":".join(terms)) - - return line - - -def writeSequenceOMCXML(Seq): - if Seq.level == 0: - if Seq.operator == "AND": - line = "({})".format(" ".join(map(str, Seq.elements))) - else: - line = "({})".format(" | ".join(map(str, Seq.elements))) - else: - terms = [] - for e in Seq.elements: - if type(e) is int: - terms.append(str(e)) - else: - terms.append(writeSequenceOMCXML(e)) - - if Seq.operator == "AND": - line = "({})".format(" ".join(terms)) - else: - line = "({})".format(" | ".join(terms)) - return line - - -def writeSequenceOMCPY(Seq, prefix="S"): - - strSurf = lambda surf: ( - "-{}{}".format(prefix, -surf) if surf < 0 else "+{}{}".format(prefix, surf) - ) - - if Seq.level == 0: - if Seq.operator == "AND": - line = "({})".format(" & ".join(map(strSurf, Seq.elements))) - else: - line = "({})".format(" | ".join(map(strSurf, Seq.elements))) - else: - terms = [] - for e in Seq.elements: - if type(e) is int: - terms.append(strSurf(e)) - else: - terms.append(writeSequenceOMCPY(e)) - - if Seq.operator == "AND": - line = "({})".format(" & ".join(terms)) - else: - line = "({})".format(" | ".join(terms)) - return line - - -def MCNPSurface(id, Type, surf): - MCNP_def = "" - - if Type == "Plane": - if surf.pointDef and opt.prnt3PPlane: - P1 = surf.Points[0] - P2 = surf.Points[1] - P3 = surf.Points[2] - MCNP_def = """{:<6d} P {P1[0]:{d}} {P1[1]:{d}} {P1[2]:{d}} - {P2[0]:{d}} {P2[1]:{d}} {P2[2]:{d}} - {P3[0]:{d}} {P3[1]:{d}} {P3[2]:{d}}""".format( - id, P1=P1 / 10, P2=P2 / 10, P3=P3 / 10, d=nf.P_d - ) - else: - A = surf.Axis.x - B = surf.Axis.y - C = surf.Axis.z - D = surf.Axis.dot(surf.Position) - if surf.Axis.isEqual(FreeCAD.Vector(1, 0, 0), tol.pln_angle): - MCNP_def = "{:<6d} PX {:{x}}".format(id, D / 10.0, x=nf.P_xyz) - elif surf.Axis.isEqual(FreeCAD.Vector(0, 1, 0), tol.pln_angle): - MCNP_def = "{:<6d} PY {:{y}}".format(id, D / 10.0, y=nf.P_xyz) - elif surf.Axis.isEqual(FreeCAD.Vector(0, 0, 1), tol.pln_angle): - MCNP_def = "{:<6d} PZ {:{z}}".format(id, D / 10.0, z=nf.P_xyz) - else: - MCNP_def = "{:<6d} P {:{abc}} {:{abc}} {:{abc}} {:{d}}".format( - id, A, B, C, D / 10.0, abc=nf.P_abc, d=nf.P_d - ) - - elif Type == "Cylinder": - Dir = FreeCAD.Vector(surf.Axis) - Dir.normalize() - Pos = surf.Center * 0.1 - rad = surf.Radius * 0.1 - if isParallel(Dir, FreeCAD.Vector(1, 0, 0), tol.angle): - if Pos.y == 0.0 and Pos.z == 0.0: - MCNP_def = "{:<6d} CX {:{r}}".format(id, rad, r=nf.C_r) - else: - MCNP_def = "{:<6d} C/X {:{yz}} {:{yz}} {:{r}}".format( - id, Pos.y, Pos.z, rad, yz=nf.C_xyz, r=nf.C_r - ) - elif isParallel(Dir, FreeCAD.Vector(0, 1, 0), tol.angle): - if Pos.x == 0.0 and Pos.z == 0.0: - MCNP_def = "{:<6d} CY {:{r}}".format(id, rad, r=nf.C_r) - else: - MCNP_def = "{:<6d} C/Y {:{xz}} {:{xz}} {:{r}}".format( - id, Pos.x, Pos.z, rad, xz=nf.C_xyz, r=nf.C_r - ) - elif isParallel(Dir, FreeCAD.Vector(0, 0, 1), tol.angle): - if Pos.y == 0.0 and Pos.x == 0.0: - MCNP_def = "{:<6d} CZ {:{r}}".format(id, rad, r=nf.C_r) - else: - MCNP_def = "{:<6d} C/Z {:{xy}} {:{xy}} {:{r}}".format( - id, Pos.x, Pos.y, rad, xy=nf.C_xyz, r=nf.C_r - ) - else: - # Is not still working fine - Q = Qform.QFormCyl(Dir, Pos, rad) - MCNP_def = """\ -{:<6d} GQ {v[0]:{aTof}} {v[1]:{aTof}} {v[2]:{aTof}} - {v[3]:{aTof}} {v[4]:{aTof}} {v[5]:{aTof}} - {v[6]:{gToi}} {v[7]:{gToi}} {v[8]:{gToi}} - {v[9]:{j}} """.format( - id, v=Q, aTof=nf.GQ_1to6, gToi=nf.GQ_7to9, j=nf.GQ_10 - ) - - # Si se quiere rcc en vez de Q form - # pnt = surf.Center.sub(surf.Axis.multiply(1.0e7)) # mas alla de 100 m - # dir = surf.Axis.multiply(1.0e8) - # Vx= pnt.x/10.0 - # Vy= pnt.y/10.0 - # Vz= pnt.z/10.0 - # Hx= dir.x/10.0 - # Hy= dir.y/10.0 - # Hz= dir.z/10.0 - # rad=surf.Radius/10.0 - # MCNP_def='%i RCC %13.7E %13.7E %13.7E %13.7E\n %13.7E %13.7E %13.7E' %(id,Vx,Vy,Vz,Hx,Hy,Hz,rad) - - elif Type == "Cone": - Apex = surf.Apex * 0.1 - Dir = surf.Axis * 0.1 - tan = math.tan(surf.SemiAngle) - X_dir = FreeCAD.Vector(1, 0, 0) - Y_dir = FreeCAD.Vector(0, 1, 0) - Z_dir = FreeCAD.Vector(0, 0, 1) - if isParallel(Dir, X_dir, tol.angle): - sheet = 1 - if isOposite(Dir, X_dir, tol.angle): - sheet = -1 - if Apex.y == 0.0 and Apex.z == 0.0: - MCNP_def = "{:<6d} KX {:{x}} {:{t2}} {}".format( - id, Apex.x, tan**2, sheet, x=nf.K_xyz, t2=nf.K_tan2 - ) - else: - MCNP_def = "{:<6d} K/X {:{xyz}} {:{xyz}} {:{xyz}} {:{t2}} {}".format( - id, - Apex.x, - Apex.y, - Apex.z, - tan**2, - sheet, - xyz=nf.K_xyz, - t2=nf.K_tan2, - ) - elif isParallel(Dir, Y_dir, tol.angle): - sheet = 1 - if isOposite(Dir, Y_dir, tol.angle): - sheet = -1 - if Apex.x == 0.0 and Apex.z == 0.0: - MCNP_def = "{:<6d} KY {:{y}} {:{t2}} {}".format( - id, Apex.y, tan**2, sheet, y=nf.K_xyz, t2=nf.K_tan2 - ) - else: - MCNP_def = "{:<6d} K/Y {:{xyz}} {:{xyz}} {:{xyz}} {:{t2}} {}".format( - id, - Apex.x, - Apex.y, - Apex.z, - tan**2, - sheet, - xyz=nf.K_xyz, - t2=nf.K_tan2, - ) - elif isParallel(Dir, Z_dir, tol.angle): - sheet = 1 - if isOposite(Dir, Z_dir, tol.angle): - sheet = -1 - if Apex.x == 0.0 and Apex.y == 0.0: - MCNP_def = "{:<6d} KZ {:{z}} {:{t2}} {}".format( - id, Apex.z, tan**2, sheet, z=nf.K_xyz, t2=nf.K_tan2 - ) - else: - MCNP_def = "{:<6d} K/Z {:{xyz}} {:{xyz}} {:{xyz}} {:{t2}} {}".format( - id, - Apex.x, - Apex.y, - Apex.z, - tan**2, - sheet, - xyz=nf.K_xyz, - t2=nf.K_tan2, - ) - else: - Q = Qform.QFormCone(Dir, Apex, tan) - MCNP_def = """\ -{:<6d} GQ {v[0]:{aTof}} {v[1]:{aTof}} {v[2]:{aTof}} - {v[3]:{aTof}} {v[4]:{aTof}} {v[5]:{aTof}} - {v[6]:{gToi}} {v[7]:{gToi}} {v[8]:{gToi}} - {v[9]:{j}} """.format( - id, v=Q, aTof=nf.GQ_1to6, gToi=nf.GQ_7to9, j=nf.GQ_10 - ) - - elif Type == "Sphere": - # corresponding logic - rad = surf.Radius * 0.1 - pnt = surf.Center * 0.1 - if pnt.isEqual(FreeCAD.Vector(0, 0, 0), tol.sph_distance): - MCNP_def = "{:<6d} SO {:{r}}".format(id, rad, r=nf.S_r) - else: - MCNP_def = "{:<6d} S {:{xyz}} {:{xyz}} {:{xyz}} {:{r}}".format( - id, pnt.x, pnt.y, pnt.z, rad, xyz=nf.S_xyz, r=nf.S_r - ) - - elif Type == "Torus": - Dir = FreeCAD.Vector(surf.Axis) - Dir.normalize() - Pos = surf.Center * 0.1 - radMaj = surf.MajorRadius * 0.1 - radMin = surf.MinorRadius * 0.1 - if isParallel(Dir, FreeCAD.Vector(1, 0, 0), tol.angle): - MCNP_def = """\ -{:<6d} TX {:{xyz}} {:{xyz}} {:{xyz}} - {:{r}} {:{r}} {:{r}}""".format( - id, Pos.x, Pos.y, Pos.z, radMaj, radMin, radMin, xyz=nf.T_xyz, r=nf.T_r - ) - elif isParallel(Dir, FreeCAD.Vector(0, 1, 0), tol.angle): - MCNP_def = """\ -{:<6d} TY {:{xyz}} {:{xyz}} {:{xyz}} - {:{r}} {:{r}} {:{r}}""".format( - id, Pos.x, Pos.y, Pos.z, radMaj, radMin, radMin, xyz=nf.T_xyz, r=nf.T_r - ) - elif isParallel(Dir, FreeCAD.Vector(0, 0, 1), tol.angle): - MCNP_def = """\ -{:<6d} TZ {:{xyz}} {:{xyz}} {:{xyz}} - {:{r}} {:{r}} {:{r}}""".format( - id, Pos.x, Pos.y, Pos.z, radMaj, radMin, radMin, xyz=nf.T_xyz, r=nf.T_r - ) - - return trim(MCNP_def, 80) - - -def OpenMCSurface(Type, surf, outXML=True, quadricForm=False): - if Type == "Plane": - A = surf.Axis.x - B = surf.Axis.y - C = surf.Axis.z - D = surf.Axis.dot(surf.Position) * 0.1 - if surf.Axis.isEqual(FreeCAD.Vector(1, 0, 0), tol.pln_angle): - if outXML: - OMCsurf = "x-plane" - coeffs = "{:{x}}".format(D, x=nf.P_xyz) - else: - OMCsurf = "XPlane" - coeffs = "x0={}".format(D) - - elif surf.Axis.isEqual(FreeCAD.Vector(0, 1, 0), tol.pln_angle): - if outXML: - OMCsurf = "y-plane" - coeffs = "{:{x}}".format(D, x=nf.P_xyz) - else: - OMCsurf = "YPlane" - coeffs = "y0={}".format(D) - - elif surf.Axis.isEqual(FreeCAD.Vector(0, 0, 1), tol.pln_angle): - if outXML: - OMCsurf = "z-plane" - coeffs = "{:{x}}".format(D, x=nf.P_xyz) - else: - OMCsurf = "ZPlane" - coeffs = "z0={}".format(D) - - else: - if outXML: - OMCsurf = "plane" - coeffs = "{:{abc}} {:{abc}} {:{abc}} {:{d}}".format( - A, B, C, D, abc=nf.P_abc, d=nf.P_d - ) - else: - OMCsurf = "Plane" - coeffs = "a={},b={},c={},d={}".format(A, B, C, D) - - elif Type == "Cylinder": - Pos = surf.Center * 0.1 - Rad = surf.Radius * 0.1 - Dir = FreeCAD.Vector(surf.Axis) - Dir.normalize() - - if isParallel(Dir, FreeCAD.Vector(1, 0, 0), tol.angle): - if outXML: - OMCsurf = "x-cylinder" - coeffs = "{:{xy}} {:{xy}} {:{r}}".format( - Pos.y, Pos.z, Rad, xy=nf.C_xyz, r=nf.C_r - ) - else: - OMCsurf = "XCylinder" - coeffs = "y0={},z0={},r={}".format(Pos.y, Pos.z, Rad) - - elif isParallel(Dir, FreeCAD.Vector(0, 1, 0), tol.angle): - if outXML: - OMCsurf = "y-cylinder" - coeffs = "{:{xy}} {:{xy}} {:{r}}".format( - Pos.x, Pos.z, Rad, xy=nf.C_xyz, r=nf.C_r - ) - else: - OMCsurf = "YCylinder" - coeffs = "x0={},z0={},r={}".format(Pos.x, Pos.z, Rad) - - elif isParallel(Dir, FreeCAD.Vector(0, 0, 1), tol.angle): - if outXML: - OMCsurf = "z-cylinder" - coeffs = "{:{xy}} {:{xy}} {:{r}}".format( - Pos.x, Pos.y, Rad, xy=nf.C_xyz, r=nf.C_r - ) - else: - OMCsurf = "ZCylinder" - coeffs = "x0={},y0={},r={}".format(Pos.x, Pos.y, Rad) - - else: - if outXML: - OMCsurf = "quadric" - Q = Qform.QFormCyl(Dir, Pos, Rad) - coeffs = "{v[0]:{aTof}} {v[1]:{aTof}} {v[2]:{aTof}} {v[3]:{aTof}} {v[4]:{aTof}} {v[5]:{aTof}} {v[6]:{gToi}} {v[7]:{gToi}} {v[8]:{gToi}} {v[9]:{j}}".format( - v=Q, aTof=nf.GQ_1to6, gToi=nf.GQ_7to9, j=nf.GQ_10 - ) - else: - if quadricForm: - OMCsurf = "Quadric" - Q = Qform.QFormCyl(Dir, Pos, Rad) - coeffs = "a={v[0]},b={v[1]},c={v[2]},d={v[3]},e={v[4]},f={v[5]},g={v[6]},h={v[7]},j={v[8]},k={v[9]}".format( - v=Q - ) - else: - OMCsurf = "Cylinder" - coeffs = "x0={},y0={},z0={},r={},dx={},dy={},dz={}".format( - Pos.x, Pos.y, Pos.z, Rad, Dir.x, Dir.y, Dir.z - ) - - elif Type == "Cone": - Apex = surf.Apex * 0.1 - Dir = FreeCAD.Vector(surf.Axis) - Dir.normalize() - tan = math.tan(surf.SemiAngle) - tan2 = tan * tan - - X_dir = FreeCAD.Vector(1, 0, 0) - Y_dir = FreeCAD.Vector(0, 1, 0) - Z_dir = FreeCAD.Vector(0, 0, 1) - - if isParallel(Dir, X_dir, tol.angle): - if outXML: - OMCsurf = "x-cone" - coeffs = "{:{xyz}} {:{xyz}} {:{xyz}} {:{t2}}".format( - Apex.x, Apex.y, Apex.z, tan2, xyz=nf.K_xyz, t2=nf.K_tan2 - ) - else: - OMCsurf = "XCone" - coeffs = "x0={},y0={},z0={},r2={}".format(Apex.x, Apex.y, Apex.z, tan2) - - elif isParallel(Dir, Y_dir, tol.angle): - if outXML: - OMCsurf = "y-cone" - coeffs = "{:{xyz}} {:{xyz}} {:{xyz}} {:{t2}}".format( - Apex.x, Apex.y, Apex.z, tan2, xyz=nf.K_xyz, t2=nf.K_tan2 - ) - else: - OMCsurf = "YCone" - coeffs = "x0={},y0={},z0={},r2={}".format(Apex.x, Apex.y, Apex.z, tan2) - - elif isParallel(Dir, Z_dir, tol.angle): - if outXML: - OMCsurf = "z-cone" - coeffs = "{:{xyz}} {:{xyz}} {:{xyz}} {:{t2}}".format( - Apex.x, Apex.y, Apex.z, tan2, xyz=nf.K_xyz, t2=nf.K_tan2 - ) - else: - OMCsurf = "ZCone" - coeffs = "x0={},y0={},z0={},r2={}".format(Apex.x, Apex.y, Apex.z, tan2) - - else: - if outXML: - OMCsurf = "quadric" - Q = Qform.QFormCone(Dir, Apex, tan) - coeffs = "{v[0]:{aTof}} {v[1]:{aTof}} {v[2]:{aTof}} {v[3]:{aTof}} {v[4]:{aTof}} {v[5]:{aTof}} {v[6]:{gToi}} {v[7]:{gToi}} {v[8]:{gToi}} {v[9]:{j}}".format( - v=Q, aTof=nf.GQ_1to6, gToi=nf.GQ_7to9, j=nf.GQ_10 - ) - else: - if quadricForm: - OMCsurf = "Quadric" - Q = Qform.QFormCone(Dir, Apex, tan) - coeffs = "a={v[0]},b={v[1]},c={v[2]},d={v[3]},e={v[4]},f={v[5]},g={v[6]},h={v[7]},j={v[8]},k={v[9]}".format( - v=Q - ) - else: - OMCsurf = "Cone" - coeffs = "x0={},y0={},z0={},r2={},dx={},dy={},dz={}".format( - Apex.x, Apex.y, Apex.z, tan2, Dir.x, Dir.y, Dir.z - ) - - elif Type == "Sphere": - Center = surf.Center * 0.1 - Rad = surf.Radius * 0.1 - if outXML: - OMCsurf = "sphere" - coeffs = "{:{xyz}} {:{xyz}} {:{xyz}} {:{r}}".format( - Center.x, Center.y, Center.z, Rad, xyz=nf.S_xyz, r=nf.S_r - ) - else: - OMCsurf = "Sphere" - coeffs = "x0={},y0={},z0={},r={}".format(Center.x, Center.y, Center.z, Rad) - - elif Type == "Torus": - Center = surf.Center * 0.1 - minRad = surf.MinorRadius * 0.1 - majRad = surf.MajorRadius * 0.1 - Dir = FreeCAD.Vector(surf.Axis) - Dir.normalize() - if outXML: - coeffs = "{:{xyz}} {:{xyz}} {:{xyz}} {:{r}} {:{r}} {:{r}}".format( - Center.x, - Center.y, - Center.z, - majRad, - minRad, - minRad, - xyz=nf.T_xyz, - r=nf.T_r, - ) - else: - coeffs = "x0={},y0={},z0={},r{},r1={},r2={}".format( - Center.x, Center.y, Center.z, majRad, minRad, minRad - ) - - if isParallel(Dir, FreeCAD.Vector(1, 0, 0), tol.angle): - OMCsurf = "x-torus" if outXML else "XTorus" - elif isParallel(Dir, FreeCAD.Vector(0, 1, 0), tol.angle): - OMCsurf = "y-torus" if outXML else "YTorus" - elif isParallel(Dir, FreeCAD.Vector(0, 0, 1), tol.angle): - OMCsurf = "z-torus" if outXML else "ZTorus" - else: - OMCsurf = None - - if outXML: - coeffs = " ".join(coeffs.split()) - return OMCsurf, coeffs - - -def SerpentSurface(id, Type, surf): - Serpent_def = "" - - if Type == "Plane": - if surf.pointDef and opt.prnt3PPlane: - P1 = surf.Points[0] - P2 = surf.Points[1] - P3 = surf.Points[2] - Serpent_def = f"surf {id} plane {P1.x/10:{nf.P_d}} {P1.y/10:{nf.P_d}} {P1.z/10:{nf.P_d}}\n" - Serpent_def += ( - f" {P2.x/10:{nf.P_d}} {P2.y/10:{nf.P_d}} {P2.z/10:{nf.P_d}}\n" - ) - Serpent_def += ( - f" {P3.x/10:{nf.P_d}} {P3.y/10:{nf.P_d}} {P3.z/10:{nf.P_d}}" - ) - - else: - A = surf.Axis.x - B = surf.Axis.y - C = surf.Axis.z - D = surf.Axis.dot(surf.Position) - if surf.Axis.isEqual(FreeCAD.Vector(1, 0, 0), tol.pln_angle): - Serpent_def = f"surf {id} px {D/10:{nf.P_xyz}}" - elif surf.Axis.isEqual(FreeCAD.Vector(0, 1, 0), tol.pln_angle): - Serpent_def = f"surf {id} py {D/10:{nf.P_xyz}}" - elif surf.Axis.isEqual(FreeCAD.Vector(0, 0, 1), tol.pln_angle): - Serpent_def = f"surf {id} pz {D/10:{nf.P_xyz}}" - else: - Serpent_def = f"surf {id} plane {A:{nf.P_d}} {B:{nf.P_d}} {C:{nf.P_d}} {D/10:{nf.P_d}}" - - elif Type == "Cylinder": - Dir = surf.Axis - Dir.normalize() - Pos = surf.Center * 0.1 - rad = surf.Radius * 0.1 - if isParallel(Dir, FreeCAD.Vector(1, 0, 0), tol.angle): - Serpent_def = ( - f"surf {id} cylx {Pos.y:{nf.C_xyz}} {Pos.z:{nf.C_xyz}} {rad:{nf.C_r}}" - ) - elif isParallel(Dir, FreeCAD.Vector(0, 1, 0), tol.angle): - Serpent_def = ( - f"surf {id} cyly {Pos.x:{nf.C_xyz}} {Pos.z:{nf.C_xyz}} {rad:{nf.C_r}}" - ) - elif isParallel(Dir, FreeCAD.Vector(0, 0, 1), tol.angle): - Serpent_def = f"surf {id} cylz {rad:{nf.C_r}}" - else: - # Is not still working fine - Q = Qform.QFormCyl(Dir, Pos, rad) - Serpent_def = """\ -surf quadratic {v[0]:{aTof}} {v[1]:{aTof}} {v[2]:{aTof}} - {v[3]:{aTof}} {v[4]:{aTof}} {v[5]:{aTof}} - {v[6]:{gToi}} {v[7]:{gToi}} {v[8]:{gToi}} - {v[9]:{j}} """.format( - id, v=Q, aTof=nf.GQ_1to6, gToi=nf.GQ_7to9, j=nf.GQ_10 - ) - - elif Type == "Cone": - Apex = surf.Apex * 0.1 - Dir = surf.Axis * 0.1 - tan = math.tan(surf.SemiAngle) - X_dir = FreeCAD.Vector(1, 0, 0) - Y_dir = FreeCAD.Vector(0, 1, 0) - Z_dir = FreeCAD.Vector(0, 0, 1) - - # Need to check this - # Serpent has no specific card for cone at origin, explicit origin only - - if isParallel(Dir, X_dir, tol.angle): - sheet = 1 - if isOposite(Dir, X_dir, tol.angle): - sheet = -1 - Serpent_def = "surf ckx {:{xyz}} {:{xyz}} {:{xyz}} {:{t2}} {}".format( - id, Apex.x, Apex.y, Apex.z, tan**2, sheet, xyz=nf.K_xyz, t2=nf.K_tan2 - ) - elif isParallel(Dir, Y_dir, tol.angle): - sheet = 1 - if isOposite(Dir, Y_dir, tol.angle): - sheet = -1 - Serpent_def = "surf cky {:{xyz}} {:{xyz}} {:{xyz}} {:{t2}} {}".format( - id, Apex.x, Apex.y, Apex.z, tan**2, sheet, xyz=nf.K_xyz, t2=nf.K_tan2 - ) - elif isParallel(Dir, Z_dir, tol.angle): - sheet = 1 - if isOposite(Dir, Z_dir, tol.angle): - sheet = -1 - Serpent_def = "surf ckz {:{xyz}} {:{xyz}} {:{xyz}} {:{t2}} {}".format( - id, Apex.x, Apex.y, Apex.z, tan**2, sheet, xyz=nf.K_xyz, t2=nf.K_tan2 - ) - else: - Q = Qform.QFormCone(Dir, Apex, tan) - - elif Type == "Sphere": - rad = surf.Radius * 0.1 - pnt = surf.Center * 0.1 - # Serpent has only explicit spheres at the origin - Serpent_def = f"surf {id} sph {pnt.x:{nf.S_xyz}} {pnt.y:{nf.S_xyz}} {pnt.z:{nf.S_xyz}} {rad:{nf.S_r}}" - - elif Type == "Torus": - Dir = surf.Axis - Dir.normalize() - Pos = surf.Center * 0.1 - radMaj = surf.MajorRadius * 0.1 - radMin = surf.MinorRadius * 0.1 - if isParallel(Dir, FreeCAD.Vector(1, 0, 0), tol.angle): - Serpent_def = f"surf {id} torx {Pos.x:{nf.T_xyz}} {Pos.y:{nf.T_xyz}} {Pos.z:{nf.T_xyz}}\n" - Serpent_def += ( - f" {radMaj:{nf.T_r}} {radMin:{nf.T_r}} {radMin:{nf.T_r}}" - ) - elif isParallel(Dir, FreeCAD.Vector(0, 1, 0), tol.angle): - Serpent_def = f"surf {id} tory {Pos.x:{nf.T_xyz}} {Pos.y:{nf.T_xyz}} {Pos.z:{nf.T_xyz}}\n" - Serpent_def += ( - f" {radMaj:{nf.T_r}} {radMin:{nf.T_r}} {radMin:{nf.T_r}}" - ) - elif isParallel(Dir, FreeCAD.Vector(0, 0, 1), tol.angle): - Serpent_def = f"surf {id} torz {Pos.x:{nf.T_xyz}} {Pos.y:{nf.T_xyz}} {Pos.z:{nf.T_xyz}}\n" - Serpent_def += ( - f" {radMaj:{nf.T_r}} {radMin:{nf.T_r}} {radMin:{nf.T_r}}" - ) - - return Serpent_def - - -def PHITSSurface(id, Type, surf): - PHITS_def = "" - - if Type == "Plane": - if surf.pointDef and opt.prnt3PPlane: - P1 = surf.Points[0] - P2 = surf.Points[1] - P3 = surf.Points[2] - PHITS_def = """{:<6d} P {P1[0]:{d}} {P1[1]:{d}} {P1[2]:{d}} - {P2[0]:{d}} {P2[1]:{d}} {P2[2]:{d}} - {P3[0]:{d}} {P3[1]:{d}} {P3[2]:{d}}""".format( - id, P1=P1 / 10, P2=P2 / 10, P3=P3 / 10, d=nf.P_d - ) - else: - A = surf.Axis.x - B = surf.Axis.y - C = surf.Axis.z - D = surf.Axis.dot(surf.Position) - if surf.Axis.isEqual(FreeCAD.Vector(1, 0, 0), tol.pln_angle): - PHITS_def = "{:<6d} PX {:{x}}".format(id, D / 10.0, x=nf.P_xyz) - elif surf.Axis.isEqual(FreeCAD.Vector(0, 1, 0), tol.pln_angle): - PHITS_def = "{:<6d} PY {:{y}}".format(id, D / 10.0, y=nf.P_xyz) - elif surf.Axis.isEqual(FreeCAD.Vector(0, 0, 1), tol.pln_angle): - PHITS_def = "{:<6d} PZ {:{z}}".format(id, D / 10.0, z=nf.P_xyz) - else: - PHITS_def = "{:<6d} P {:{abc}} {:{abc}} {:{abc}} {:{d}}".format( - id, A, B, C, D / 10.0, abc=nf.P_abc, d=nf.P_d - ) - - elif Type == "Cylinder": - Dir = FreeCAD.Vector(surf.Axis) - Dir.normalize() - Pos = surf.Center * 0.1 - rad = surf.Radius * 0.1 - if isParallel(Dir, FreeCAD.Vector(1, 0, 0), tol.angle): - if Pos.y == 0.0 and Pos.z == 0.0: - PHITS_def = "{:<6d} CX {:{r}}".format(id, rad, r=nf.C_r) - else: - PHITS_def = "{:<6d} C/X {:{yz}} {:{yz}} {:{r}}".format( - id, Pos.y, Pos.z, rad, yz=nf.C_xyz, r=nf.C_r - ) - elif isParallel(Dir, FreeCAD.Vector(0, 1, 0), tol.angle): - if Pos.x == 0.0 and Pos.z == 0.0: - PHITS_def = "{:<6d} CY {:{r}}".format(id, rad, r=nf.C_r) - else: - PHITS_def = "{:<6d} C/Y {:{xz}} {:{xz}} {:{r}}".format( - id, Pos.x, Pos.z, rad, xz=nf.C_xyz, r=nf.C_r - ) - elif isParallel(Dir, FreeCAD.Vector(0, 0, 1), tol.angle): - if Pos.y == 0.0 and Pos.x == 0.0: - PHITS_def = "{:<6d} CZ {:{r}}".format(id, rad, r=nf.C_r) - else: - PHITS_def = "{:<6d} C/Z {:{xy}} {:{xy}} {:{r}}".format( - id, Pos.x, Pos.y, rad, xy=nf.C_xyz, r=nf.C_r - ) - else: - # Is not still working fine - Q = Qform.QFormCyl(Dir, Pos, rad) - PHITS_def = """\ -{:<6d} GQ {v[0]:{aTof}} {v[1]:{aTof}} {v[2]:{aTof}} - {v[3]:{aTof}} {v[4]:{aTof}} {v[5]:{aTof}} - {v[6]:{gToi}} {v[7]:{gToi}} {v[8]:{gToi}} - {v[9]:{j}} """.format( - id, v=Q, aTof=nf.GQ_1to6, gToi=nf.GQ_7to9, j=nf.GQ_10 - ) - - # Si se quiere rcc en vez de Q form - # pnt = surf.Center.sub(surf.Axis.multiply(1.0e7)) # mas alla de 100 m - # dir = surf.Axis.multiply(1.0e8) - # Vx= pnt.x/10.0 - # Vy= pnt.y/10.0 - # Vz= pnt.z/10.0 - # Hx= dir.x/10.0 - # Hy= dir.y/10.0 - # Hz= dir.z/10.0 - # rad=surf.Radius/10.0 - # MCNP_def='%i RCC %13.7E %13.7E %13.7E %13.7E\n %13.7E %13.7E %13.7E' %(id,Vx,Vy,Vz,Hx,Hy,Hz,rad) - - elif Type == "Cone": - Apex = surf.Apex * 0.1 - Dir = surf.Axis * 0.1 - tan = math.tan(surf.SemiAngle) - X_dir = FreeCAD.Vector(1, 0, 0) - Y_dir = FreeCAD.Vector(0, 1, 0) - Z_dir = FreeCAD.Vector(0, 0, 1) - if isParallel(Dir, X_dir, tol.angle): - sheet = 1 - if isOposite(Dir, X_dir, tol.angle): - sheet = -1 - if Apex.y == 0.0 and Apex.z == 0.0: - PHITS_def = "{:<6d} KX {:{x}} {:{t2}} {}".format( - id, Apex.x, tan**2, sheet, x=nf.K_xyz, t2=nf.K_tan2 - ) - else: - PHITS_def = "{:<6d} K/X {:{xyz}} {:{xyz}} {:{xyz}} {:{t2}} {}".format( - id, - Apex.x, - Apex.y, - Apex.z, - tan**2, - sheet, - xyz=nf.K_xyz, - t2=nf.K_tan2, - ) - elif isParallel(Dir, Y_dir, tol.angle): - sheet = 1 - if isOposite(Dir, Y_dir, tol.angle): - sheet = -1 - if Apex.x == 0.0 and Apex.z == 0.0: - PHITS_def = "{:<6d} KY {:{y}} {:{t2}} {}".format( - id, Apex.y, tan**2, sheet, y=nf.K_xyz, t2=nf.K_tan2 - ) - else: - PHITS_def = "{:<6d} K/Y {:{xyz}} {:{xyz}} {:{xyz}} {:{t2}} {}".format( - id, - Apex.x, - Apex.y, - Apex.z, - tan**2, - sheet, - xyz=nf.K_xyz, - t2=nf.K_tan2, - ) - elif isParallel(Dir, Z_dir, tol.angle): - sheet = 1 - if isOposite(Dir, Z_dir, tol.angle): - sheet = -1 - if Apex.x == 0.0 and Apex.y == 0.0: - PHITS_def = "{:<6d} KZ {:{z}} {:{t2}} {}".format( - id, Apex.z, tan**2, sheet, z=nf.K_xyz, t2=nf.K_tan2 - ) - else: - PHITS_def = "{:<6d} K/Z {:{xyz}} {:{xyz}} {:{xyz}} {:{t2}} {}".format( - id, - Apex.x, - Apex.y, - Apex.z, - tan**2, - sheet, - xyz=nf.K_xyz, - t2=nf.K_tan2, - ) - else: - Q = Qform.QFormCone(Dir, Apex, tan) - PHITS_def = """\ -{:<6d} GQ {v[0]:{aTof}} {v[1]:{aTof}} {v[2]:{aTof}} - {v[3]:{aTof}} {v[4]:{aTof}} {v[5]:{aTof}} - {v[6]:{gToi}} {v[7]:{gToi}} {v[8]:{gToi}} - {v[9]:{j}} """.format( - id, v=Q, aTof=nf.GQ_1to6, gToi=nf.GQ_7to9, j=nf.GQ_10 - ) - - elif Type == "Sphere": - # corresponding logic - rad = surf.Radius * 0.1 - pnt = surf.Center * 0.1 - if pnt.isEqual(FreeCAD.Vector(0, 0, 0), tol.sph_distance): - PHITS_def = "{:<6d} SO {:{r}}".format(id, rad, r=nf.S_r) - else: - PHITS_def = "{:<6d} S {:{xyz}} {:{xyz}} {:{xyz}} {:{r}}".format( - id, pnt.x, pnt.y, pnt.z, rad, xyz=nf.S_xyz, r=nf.S_r - ) - - elif Type == "Torus": - Dir = FreeCAD.Vector(surf.Axis) - Dir.normalize() - Pos = surf.Center * 0.1 - radMaj = surf.MajorRadius * 0.1 - radMin = surf.MinorRadius * 0.1 - if isParallel(Dir, FreeCAD.Vector(1, 0, 0), tol.angle): - PHITS_def = """\ -{:<6d} TX {:{xyz}} {:{xyz}} {:{xyz}} - {:{r}} {:{r}} {:{r}}""".format( - id, Pos.x, Pos.y, Pos.z, radMaj, radMin, radMin, xyz=nf.T_xyz, r=nf.T_r - ) - elif isParallel(Dir, FreeCAD.Vector(0, 1, 0), tol.angle): - PHITS_def = """\ -{:<6d} TY {:{xyz}} {:{xyz}} {:{xyz}} - {:{r}} {:{r}} {:{r}}""".format( - id, Pos.x, Pos.y, Pos.z, radMaj, radMin, radMin, xyz=nf.T_xyz, r=nf.T_r - ) - elif isParallel(Dir, FreeCAD.Vector(0, 0, 1), tol.angle): - PHITS_def = """\ -{:<6d} TZ {:{xyz}} {:{xyz}} {:{xyz}} - {:{r}} {:{r}} {:{r}}""".format( - id, Pos.x, Pos.y, Pos.z, radMaj, radMin, radMin, xyz=nf.T_xyz, r=nf.T_r - ) - - return trim(PHITS_def, 80) - - -def trim(surfDef, lineLength=80): - - lines = surfDef.split("\n") - if len(lines) == 1 and len(lines[0]) <= lineLength: - return surfDef - - longLine = [] - for i, line in enumerate(lines): - if len(line) > lineLength: - longLine.append(i) - - if len(longLine) == 0: - return surfDef - - newDef = "" - for i, line in enumerate(lines): - if i in longLine: - newLine = cutLine(line, lineLength) - else: - newLine = line - newDef += newLine + "\n" - - return newDef[:-1] - - -def cutLine(line, lineLength): - tabNumber = 10 - while True: - pos = line.rfind(" ") - if pos <= lineLength: - break - - line1 = line[0:pos] - line2 = line[pos + 1 :] - - # second line is only spaces - if len(line2.strip()) == 0: - newLine = line1 - else: - newLine = "{}\n{: <{n}}{}".format(line1, "", line2, n=tabNumber) - - return newLine diff --git a/src/geouned/GEOUNED/Write/WriteFiles.py b/src/geouned/GEOUNED/Write/WriteFiles.py deleted file mode 100644 index d7b7c3db..00000000 --- a/src/geouned/GEOUNED/Write/WriteFiles.py +++ /dev/null @@ -1,90 +0,0 @@ -from . import AdditionalFiles as OutFiles -from .MCNPFormat import MCNP_input -from .OpenMCFormat import OpenMC_input -from .PHITSFormat import PHITS_input -from .SerpentFormat import Serpent_input - - -def writeGeometry(UniverseBox, MetaList, Surfaces, code_setting): - - baseName = code_setting["geometryName"] - - # write cells comments in file - if code_setting["cellCommentFile"]: - OutFiles.commentsWrite(baseName, MetaList) - if code_setting["cellSummaryFile"]: - OutFiles.summaryWrite(baseName, MetaList) - - if "mcnp" in code_setting["outFormat"]: - mcnpFilename = baseName + ".mcnp" - outBox = ( - UniverseBox.XMin, - UniverseBox.XMax, - UniverseBox.YMin, - UniverseBox.YMax, - UniverseBox.ZMin, - UniverseBox.ZMax, - ) - if code_setting["voidGen"]: - outSphere = (Surfaces["Sph"][-1].Index, Surfaces["Sph"][-1].Surf.Radius) - else: - outSphere = None - - MCNPfile = MCNP_input(MetaList, Surfaces, code_setting) - MCNPfile.setSDEF((outSphere, outBox)) - MCNPfile.writeInput(mcnpFilename) - - if ( - "openMC_XML" in code_setting["outFormat"] - or "openMC_PY" in code_setting["outFormat"] - ): - OMCFile = OpenMC_input(MetaList, Surfaces, code_setting) - - if "openMC_XML" in code_setting["outFormat"]: - omcFilename = baseName + ".xml" - OMCFile.writeXML(omcFilename) - - if "openMC_PY" in code_setting["outFormat"]: - omcFilename = baseName + ".py" - OMCFile.writePY(omcFilename) - - if "serpent" in code_setting["outFormat"]: - serpentFilename = baseName + ".serp" - outBox = ( - UniverseBox.XMin, - UniverseBox.XMax, - UniverseBox.YMin, - UniverseBox.YMax, - UniverseBox.ZMin, - UniverseBox.ZMax, - ) - if code_setting["voidGen"]: - outSphere = (Surfaces["Sph"][-1].Index, Surfaces["Sph"][-1].Surf.Radius) - else: - outSphere = None - - Serpentfile = Serpent_input(MetaList, Surfaces, code_setting) - # Serpentfile.setSDEF((outSphere,outBox)) - Serpentfile.writeInput(serpentFilename) - - if "phits" in code_setting["outFormat"]: - phitsFilename = baseName + ".inp" - PHITS_outBox = ( - UniverseBox.XMin, - UniverseBox.XMax, - UniverseBox.YMin, - UniverseBox.YMax, - UniverseBox.ZMin, - UniverseBox.ZMax, - ) - if code_setting["voidGen"]: - PHITS_outSphere = ( - Surfaces["Sph"][-1].Index, - Surfaces["Sph"][-1].Surf.Radius, - ) - else: - PHITS_outSphere = None - - PHITSfile = PHITS_input(MetaList, Surfaces, code_setting) - # PHITSfile.setSDEF_PHITS((PHITS_outSphere,PHITS_outBox)) - PHITSfile.writePHITS(phitsFilename) diff --git a/src/geouned/GEOUNED/__init__.py b/src/geouned/GEOUNED/__init__.py index 1a9b05d0..35c0fcd8 100644 --- a/src/geouned/GEOUNED/__init__.py +++ b/src/geouned/GEOUNED/__init__.py @@ -1,874 +1,2 @@ -# Init file of GEOUNED module -# - -# We load the STEP and the materials - -# this try except attempts to import freecad (lowercase) which is the conda -# package name for FreeCAD (mixed case) upon import the conda package appends -# the sys path for Conda installed FreeCAD, consequently FreeCAD can then be -# found by subsequent import statements through out the code base -try: - import freecad -except: - pass -import configparser -import typing -from datetime import datetime -from os import mkdir, path - -import FreeCAD -import Part - -from .CodeVersion import * -from .Conversion import CellDefinition as Conv -from .Cuboid.translate import translate -from .Decompose import Decom_one as Decom -from .LoadFile import LoadSTEP as Load -from .Utils import Functions as UF -from .Utils.BooleanSolids import buildCTableFromSolids -from .Utils.Options.Classes import MCNP_numeric_format, Options, Tolerances -from .Void import Void as Void -from .Write.Functions import writeMCNPCellDef -from .Write.WriteFiles import writeGeometry - - -class GEOUNED: - - def __init__( - self, - title: str = "Geouned conversion", - stepFile: str = "", - geometryName: str = "", - matFile: str = "", - outFormat: typing.Tuple[str] = ("mcnp",), - voidGen: bool = True, - debug: bool = False, - compSolids: bool = True, - volSDEF: bool = False, - dummyMat: bool = False, - volCARD: bool = True, - UCARD=None, - simplify: str = "No", - cellRange=[], - exportSolids: str = "", - minVoidSize: float = 200.0, # units mm - maxSurf: int = 50, - maxBracket: int = 30, - voidMat=[], - voidExclude=[], - startCell: int = 1, - startSurf: int = 1, - cellCommentFile: bool = False, - cellSummaryFile: bool = True, - sortEnclosure: bool = False, - ): - """Base class for the conversion of CAD to CSG models - - Args: - title (str, optional): Title of the model. Defaults to "Geouned - conversion". - stepFile (str, optional): Name of the CAD file (in STEP format) to - be converted. Defaults to "". - geometryName (str, optional): Base name of the output file(s). - Defaults to "". - matFile (str, optional): _description_. Defaults to "". - outFormat (typing.Tuple[str], optional): Format for the output - geometry. Available format are: mcnp, openMC_XML, openMC_PY, - phits and serpent. Several output format can be written in the - same geouned run. Defaults to ("mcnp",). - voidGen (bool, optional): Generate voids of the geometry. Defaults - to True. - debug (bool, optional): Write step files of original and decomposed - solids, for each solid in the STEP file. Defaults to False. - compSolids (bool, optional): Join subsolids of STEP file as a single - compound solid. Step files generated with SpaceClaim have not - exactly the same level of solids as FreeCAD. It may a happened - that solids defined has separated solids are read by FreeCAD - as a single compound solid (and will produce only one MCNP - cell). In this case compSolids should be set to False. Defaults - to True. - volSDEF (bool, optional): Write SDEF definition and tally of solid - cell for stochastic volume checking. Defaults to False. - dummyMat (bool, optional): Write dummy material definition card in - the MCNP output file for all material labels present in the - model. Dummy material definition is "MX 1001 1". Defaults to - False. - volCARD (bool, optional): Write the CAD calculated volume in the - cell definition using the VOL card. Defaults to True. - UCARD (_type_, optional): Write universe card in the cell definition - with the specified universe number (if value = 0 Universe card - is not written). Defaults to None. - simplify (str, optional): Simplify the cell definition considering - relative surfaces position and using Boolean logics. Available - options are: "no" no optimization, "void" only void cells are - simplified. Algorithm is faster but the simplification is not - optimal. "voidfull" : only void cells are simplified with the - most optimal algorithm. The time of the conversion can be - multiplied by 5 or more. "full" : all the cells (solids and - voids) are simplified. Defaults to "No". - cellRange (list, optional): Range of cell to be converted (only one - range is allowed, e.g [100,220]). Default all solids are - converted. Defaults to []. - exportSolids (str, optional): Export CAD solid after reading. - The execution is stopped after export, the translation is not - carried out. Defaults to "". - minVoidSize (float, optional): Minimum size of the edges of the - void cell. Units are in mm. Defaults to 200.0. - maxBracket (int, optional): Maximum number of brackets (solid - complementary) allowed in void cell definition. Defaults to 30. - voidMat (list, optional): Assign a material defined by the user - instead of void for cells without material definition and the - cells generated in the automatic void generation. The format - is a 3 valued tuple (mat_label, mat_density, mat_description). - Example (100,1e-3,'Air assigned to Void'). Defaults to []. - voidExclude (list, optional): #TODO see issue 87. Defaults to []. - startCell (int, optional): Starting cell numbering label. Defaults to 1. - startSurf (int, optional): Starting surface numbering label. Defaults to 1. - cellCommentFile (bool, optional): Write an additional file with - comment associated to each CAD cell in the MCNP output file. - Defaults to False. - cellSummaryFile (bool, optional): Write an additional file with - information on the CAD cell translated. Defaults to True. - sortEnclosure (bool, optional): If enclosures are defined in the - CAD models, the voids cells of the enclosure will be located in - the output file in the same location where the enclosure solid - is located in the CAD solid tree.. Defaults to False. - """ - self.title = title - self.stepFile = stepFile - self.geometryName = geometryName - self.matFile = matFile - self.outFormat = outFormat - self.voidGen = voidGen - self.debug = debug - self.compSolids = compSolids - self.volSDEF = volSDEF - self.dummyMat = dummyMat - self.volCARD = volCARD - self.UCARD = UCARD - self.simplify = simplify - self.cellRange = cellRange - self.exportSolids = exportSolids - self.minVoidSize = minVoidSize - self.maxSurf = maxSurf - self.maxBracket = maxBracket - self.voidMat = voidMat - self.voidExclude = voidExclude - self.startCell = startCell - self.startSurf = startSurf - self.cellCommentFile = cellCommentFile - self.cellSummaryFile = cellSummaryFile - self.sortEnclosure = sortEnclosure - - def SetOptions(self): - toleranceKwrd = ( - "relativeTolerance", - "relativePrecision", - "singleValue", - "generalDistance", - "generalAngle", - "planeDistance", - "planeAngle", - "cylinderDistance", - "cylinderAngle", - "sphereDistance", - "coneDistance", - "coneAngle", - "torusDistance", - "torusAngle", - "minArea", - ) - numericKwrd = ( - "P_abc", - "P_d", - "P_xyz", - "S_r", - "S_xyz", - "C_r", - "C_xyz", - "K_tan2", - "K_xyz", - "T_r", - "T_xyz", - "GQ_1to6", - "GQ_7to9", - "GQ_10", - ) - tolKwrdEquiv = { - "relativeTolerance": "relativeTol", - "relativePrecision": "relativePrecision", - "singleValue": "value", - "generalDistance": "distance", - "generalAngle": "angle", - "planeDistance": "pln_distance", - "planeAngle": "pln_angle", - "cylinderDistance": "cyl_distance", - "cylinderAngle": "cyl_angle", - "sphereDistance": "sph_distance", - "coneDistance": "kne_distance", - "coneAngle": "kne_angle", - "torusDistance": "tor_distance", - "torusAngle": "tor_angle", - "minArea": "min_area", - } - - config = configparser.ConfigParser() - config.optionxform = str - config.read(self.__dict__["title"]) - for section in config.sections(): - if section == "Files": - for key in config["Files"].keys(): - if key in ("geometryName", "matFile", "title"): - self.set(key, config.get("Files", key)) - - elif key == "stepFile": - value = config.get("Files", key).strip() - lst = value.split() - if value[0] in ("(", "[") and value[-1] in ("]", ")"): - data = value[1:-1].split(",") - data = [x.strip() for x in data] - self.set(key, data) - elif len(lst) > 1: - self.set(key, lst) - else: - self.set(key, value) - - elif key == "outFormat": - raw = config.get("Files", key).strip() - values = tuple(x.strip() for x in raw.split(",")) - outFormat = [] - for v in values: - if v.lower() == "mcnp": - outFormat.append("mcnp") - elif v.lower() == "openmc_xml": - outFormat.append("openMC_XML") - elif v.lower() == "openmc_py": - outFormat.append("openMC_PY") - elif v.lower() == "serpent": - outFormat.append("serpent") - elif v.lower() == "phits": - outFormat.append("phits") - self.set(key, tuple(outFormat)) - - elif section == "Parameters": - for key in config["Parameters"].keys(): - if key in ( - "voidGen", - "debug", - "compSolids", - "volSDEF", - "volCARD", - "dummyMat", - "cellSummaryFile", - "cellCommentFile", - "sortEnclosure", - ): - self.set(key, config.getboolean("Parameters", key)) - elif key in ( - "minVoidSize", - "maxSurf", - "maxBracket", - "startCell", - "startSurf", - ): - self.set(key, config.getint("Parameters", key)) - elif key in ("exportSolids", "UCARD", "simplify"): - self.set(key, config.get("Parameters", key)) - elif key == "voidMat": - value = config.get("Parameters", key).strip() - data = value[1:-1].split(",") - self.set(key, (int(data[0]), float(data[1]), data[2])) - else: - value = config.get("Parameters", key).strip() - data = value[1:-1].split(",") - self.set(key, tuple(map(int, data))) - - elif section == "Options": - for key in config["Options"].keys(): - if key in ( - "forceCylinder", - "newSplitPlane", - "delLastNumber", - "verbose", - "scaleUP", - "quadricPY", - "Facets", - "prnt3PPlane", - "forceNoOverlap", - ): - setattr(Options, key, config.getboolean("Options", key)) - elif key in ("enlargeBox", "nPlaneReverse", "splitTolerance"): - setattr(Options, key, config.getfloat("Options", key)) - - elif section == "Tolerances": - for key in config["Tolerances"].keys(): - if key == "relativeTolerance": - setattr(Tolerances, key, config.getboolean("Tolerances", key)) - elif key in toleranceKwrd: - setattr( - Tolerances, - tolKwrdEquiv[key], - config.getfloat("Tolerances", key), - ) - - elif section == "MCNP_Numeric_Format": - PdEntry = False - for key in config["MCNP_Numeric_Format"].keys(): - if key in numericKwrd: - setattr( - MCNP_numeric_format, - key, - config.get("MCNP_Numeric_Format", key), - ) - if key == "P_d": - PdEntry = True - - else: - print("bad section name : {}".format(section)) - - if self.__dict__["geometryName"] == "": - self.__dict__["geometryName"] = self.__dict__["stepFile"][:-4] - - if Options.prnt3PPlane and not PdEntry: - MCNP_numeric_format.P_d = "22.15e" - - print(self.__dict__) - - def set(self, kwrd, value): - - if kwrd not in self.__dict__.keys(): - print("Bad entry : {}".format(kwrd)) - return - - if kwrd == "stepFile": - if isinstance(value, (list, tuple)): - for v in value: - if not isinstance(v, str): - print("elemt in {} list should be string".format(kwrd)) - return - elif not isinstance(value, str): - print("{} should be string or tuple of strings".format(kwrd)) - return - - elif kwrd == "UCARD": - if value == "None": - value = None - elif value.isdigit(): - value = int(value) - else: - print("{} value should be None or integer".format(kwrd)) - return - elif kwrd == "outFormat": - if len(value) == 0: - return - elif kwrd in ("geometryName", "matFile", "exportSolids"): - if not isinstance(value, str): - print("{} value should be str instance".format(kwrd)) - return - elif kwrd in ("cellRange", "voidMat", "voidExclude"): - if not isinstance(value, (list, tuple)): - print("{} value should be list or tuple".format(kwrd)) - return - elif kwrd in ("minVoidSize", "maxSurf", "maxBracket", "startCell", "startSurf"): - if not isinstance(value, int): - print("{} value should be integer".format(kwrd)) - return - elif kwrd in ( - "voidGen", - "debug", - "compSolids", - "simplifyCTable", - "volSDEF", - "volCARD", - "dummyMat", - "cellSummaryFile", - "cellCommentFile", - "sortEnclosure", - ): - if not isinstance(value, bool): - print("{} value should be boolean".format(kwrd)) - return - - self.__dict__[kwrd] = value - if kwrd == "stepFile" and self.__dict__["geometryName"] == "": - if isinstance(value, (tuple, list)): - self.__dict__["geometryName"] == "joined_step_files" - else: - self.__dict__["geometryName"] == value[:-4] - - def Start(self): - - print("start") - FreeCAD_Version = "{V[0]:}.{V[1]:}.{V[2]:}".format(V=FreeCAD.Version()) - print( - "GEOUNED version {} {} \nFreeCAD version {}".format( - GEOUNED_Version, GEOUNED_ReleaseDate, FreeCAD_Version - ) - ) - - code_setting = self.__dict__ - if code_setting is None: - raise ValueError("Cannot run the code. Input are missing") - if self.stepFile == "": - raise ValueError("Cannot run the code. Step file name is missing") - - if isinstance(self.stepFile, (tuple, list)): - for stp in self.stepFile: - if not path.isfile(stp): - raise FileNotFoundError(f"Step file {stp} not found.\nStop.") - else: - if not path.isfile(self.stepFile): - raise FileNotFoundError(f"Step file {self.stepFile} not found.\nStop.") - - startTime = datetime.now() - - if isinstance(self.stepFile, (list, tuple)): - MetaChunk = [] - EnclosureChunk = [] - for stp in self.stepFile: - print("read step file : {}".format(stp)) - Meta, Enclosure = Load.LoadCAD(stp, self.matFile) - MetaChunk.append(Meta) - EnclosureChunk.append(Enclosure) - MetaList = joinMetaLists(MetaChunk) - EnclosureList = joinMetaLists(EnclosureChunk) - else: - print("read step file : {}".format(self.stepFile)) - MetaList, EnclosureList = Load.LoadCAD( - self.stepFile, self.matFile, self.voidMat, self.compSolids - ) - - print("End of loading phase") - tempstr1 = str(datetime.now() - startTime) - print(tempstr1) - tempTime = datetime.now() - - # Select a specific solid range from original STEP solids - if self.cellRange: - MetaList = MetaList[ - self.cellRange[0] : self.cellRange[1] - ] - - # export in STEP format solids read from input file - # terminate excution - if self.exportSolids != "": - solids = [] - for m in MetaList: - if m.IsEnclosure: - continue - solids.extend(m.Solids) - Part.makeCompound(solids).exportStep(self.exportSolids) - msg = ( - f'Solids exported in file {self.exportSolids}\n' - "GEOUNED Finish. No solid translation performed." - ) - raise ValueError(msg) - - # set up Universe - if EnclosureList: - UniverseBox = getUniverse(MetaList + EnclosureList) - else: - UniverseBox = getUniverse(MetaList) - - Surfaces = UF.Surfaces_dict(offset=self.startSurf - 1) - - warnSolids = [] - warnEnclosures = [] - coneInfo = dict() - tempTime0 = datetime.now() - if not Options.Facets: - - # decompose all solids in elementary solids (convex ones) - warningSolidList = DecomposeSolids( - MetaList, Surfaces, UniverseBox, code_setting, True - ) - - # decompose Enclosure solids - if self.voidGen and EnclosureList: - warningEnclosureList = DecomposeSolids( - EnclosureList, Surfaces, UniverseBox, code_setting, False - ) - - print("End of decomposition phase") - - # start Building CGS cells phase - - for j, m in enumerate(MetaList): - if m.IsEnclosure: - continue - print("Building cell: ", j + 1) - cones = Conv.cellDef(m, Surfaces, UniverseBox) - if cones: - coneInfo[m.__id__] = cones - if j in warningSolidList: - warnSolids.append(m) - if not m.Solids: - print("none", j, m.__id__) - print(m.Definition) - - if Options.forceNoOverlap: - Conv.noOverlappingCell(MetaList, Surfaces) - - else: - translate(MetaList, Surfaces, UniverseBox, code_setting) - # decompose Enclosure solids - if self.voidGen and EnclosureList: - warningEnclosureList = DecomposeSolids( - EnclosureList, Surfaces, UniverseBox, code_setting, False - ) - - tempstr2 = str(datetime.now() - tempTime) - print(tempstr2) - - # building enclosure solids - - if self.voidGen and EnclosureList: - for j, m in enumerate(EnclosureList): - print("Building Enclosure Cell: ", j + 1) - cones = Conv.cellDef(m, Surfaces, UniverseBox) - if cones: - coneInfo[m.__id__] = cones - if j in warningEnclosureList: - warnEnclosures.append(m) - - tempTime1 = datetime.now() - - # void generation phase - MetaVoid = [] - if self.voidGen: - print("Build Void") - print(self.voidExclude) - if not self.voidExclude: - MetaReduced = MetaList - else: - MetaReduced = excludeCells(MetaList, self.voidExclude) - - if MetaList: - init = MetaList[-1].__id__ - len(EnclosureList) - else: - init = 0 - MetaVoid = Void.voidGeneration( - MetaReduced, EnclosureList, Surfaces, UniverseBox, code_setting, init - ) - - # if code_setting['simplify'] == 'full' and not Options.forceNoOverlap: - if self.simplify == "full": - Surfs = {} - for lst in Surfaces.values(): - for s in lst: - Surfs[s.Index] = s - - for c in MetaList: - if c.Definition.level == 0 or c.IsEnclosure: - continue - print("simplify cell", c.__id__) - Box = UF.getBox(c) - CT = buildCTableFromSolids(Box, (c.Surfaces, Surfs), option="full") - c.Definition.simplify(CT) - c.Definition.clean() - if type(c.Definition.elements) is bool: - print( - "unexpected constant cell {} :{}".format( - c.__id__, c.Definition.elements - ) - ) - - tempTime2 = datetime.now() - print("build Time:", tempTime2 - tempTime1) - - print(datetime.now() - startTime) - - cellOffSet = self.startCell - 1 - if EnclosureList and self.sortEnclosure: - # sort group solid cell / void cell sequence in each for each enclosure - # if a solid belong to several enclosure, its definition will be written - # for the highest enclosure level or if same enclosure level in the first - # enclosure found - MetaList = sortEnclosure(MetaList, MetaVoid, cellOffSet) - else: - # remove Null Cell and apply cell numbering offset - deleted = [] - idLabel = {0: 0} - icount = cellOffSet - for i, m in enumerate(MetaList): - if m.NullCell or m.IsEnclosure: - deleted.append(i) - continue - - icount += 1 - m.label = icount - idLabel[m.__id__] = m.label - - for i in reversed(deleted): - del MetaList[i] - - lineComment = """\ -########################################################## - VOID CELLS -##########################################################""" - mc = UF.GEOUNED_Solid(None) - mc.Comments = lineComment - MetaList.append(mc) - - deleted = [] - for i, m in enumerate(MetaVoid): - if m.NullCell: - deleted.append(i) - continue - icount += 1 - m.label = icount - updateComment(m, idLabel) - for i in reversed(deleted): - del MetaVoid[i] - - MetaList.extend(MetaVoid) - - printWarningSolids(warnSolids, warnEnclosures) - - # add plane definition to cone - processCones(MetaList, coneInfo, Surfaces, UniverseBox) - - # write outputformat input - writeGeometry(UniverseBox, MetaList, Surfaces, code_setting) - - print("End of MCNP, OpenMC, Serpent and PHITS translation phase") - - print("Process finished") - print(datetime.now() - startTime) - - print("Translation time of solid cells", tempTime1 - tempTime0) - print("Translation time of void cells", tempTime2 - tempTime1) - - -def DecomposeSolids(MetaList, Surfaces, UniverseBox, setting, meta): - totsolid = len(MetaList) - warningSolids = [] - for i, m in enumerate(MetaList): - if meta and m.IsEnclosure: - continue - print("Decomposing solid: {}/{} ".format(i + 1, totsolid)) - if setting["debug"]: - print(m.Comments) - if not path.exists("debug"): - mkdir("debug") - if m.IsEnclosure: - m.Solids[0].exportStep("debug/origEnclosure_{}.stp".format(i)) - else: - m.Solids[0].exportStep("debug/origSolid_{}.stp".format(i)) - - comsolid, err = Decom.SplitSolid(Part.makeCompound(m.Solids), UniverseBox) - - if err != 0: - if not path.exists("Suspicious_solids"): - mkdir("Suspicious_solids") - if m.IsEnclosure: - Part.CompSolid(m.Solids).exportStep( - "Suspicious_solids/Enclosure_original_{}.stp".format(i) - ) - comsolid.exportStep( - "Suspicious_solids/Enclosure_split_{}.stp".format(i) - ) - else: - Part.CompSolid(m.Solids).exportStep( - "Suspicious_solids/Solid_original_{}.stp".format(i) - ) - comsolid.exportStep("Suspicious_solids/Solid_split_{}.stp".format(i)) - - warningSolids.append(i) - - if setting["debug"]: - if m.IsEnclosure: - comsolid.exportStep("debug/compEnclosure_{}.stp".format(i)) - else: - comsolid.exportStep("debug/compSolid_{}.stp".format(i)) - Surfaces.extend( - Decom.ExtractSurfaces(comsolid, "All", UniverseBox, MakeObj=True) - ) - m.setCADSolid() - m.updateSolids(comsolid.Solids) - - return warningSolids - - -def updateComment(meta, idLabel): - if meta.__commentInfo__ is None: - return - if meta.__commentInfo__[1] is None: - return - newLabel = (idLabel[i] for i in meta.__commentInfo__[1]) - meta.setComments(Void.voidCommentLine((meta.__commentInfo__[0], newLabel))) - - -def processCones(MetaList, coneInfo, Surfaces, UniverseBox): - cellId = tuple(coneInfo.keys()) - for m in MetaList: - if m.__id__ not in cellId and not m.Void: - continue - - if m.Void and m.__commentInfo__ is not None: - if m.__commentInfo__[1] is None: - continue - cones = set() - for Id in m.__commentInfo__[1]: - if Id in cellId: - cones.update(-x for x in coneInfo[Id]) - Conv.addConePlane(m.Definition, cones, Surfaces, UniverseBox) - elif not m.Void: - Conv.addConePlane(m.Definition, coneInfo[m.__id__], Surfaces, UniverseBox) - - -def getUniverse(MetaList): - d = 10 - Box = MetaList[0].BoundBox - xmin = Box.XMin - xmax = Box.XMax - ymin = Box.YMin - ymax = Box.YMax - zmin = Box.ZMin - zmax = Box.ZMax - for m in MetaList[1:]: - # MIO. This was removed since in HELIAS the enclosure cell is the biggest one - # if m.IsEnclosure: continue - xmin = min(m.BoundBox.XMin, xmin) - xmax = max(m.BoundBox.XMax, xmax) - ymin = min(m.BoundBox.YMin, ymin) - ymax = max(m.BoundBox.YMax, ymax) - zmin = min(m.BoundBox.ZMin, zmin) - zmax = max(m.BoundBox.ZMax, zmax) - - return FreeCAD.BoundBox( - FreeCAD.Vector(xmin - d, ymin - d, zmin - d), - FreeCAD.Vector(xmax + d, ymax + d, zmax + d), - ) - - -def printWarningSolids(warnSolids, warnEnclosures): - - if warnSolids or warnEnclosures: - fic = open("Warning_Solids_definition.txt", "w") - else: - return - - if warnSolids: - lines = "Solids :\n" - for sol in warnSolids: - lines += "\n" - lines += "{}\n".format(sol.label) - lines += "{}\n".format(sol.Comments) - lines += "{}\n".format(writeMCNPCellDef(sol.Definition)) - fic.write(lines) - - if warnEnclosures: - lines = "Enclosures :\n" - for sol in warnEnclosures: - lines += "\n" - lines += "{}\n".format(sol.label) - lines += "{}\n".format(sol.Comments) - lines += "{}\n".format(writeMCNPCellDef(sol.Definition)) - - fic.write(lines) - - fic.close() - - -def joinMetaLists(MList): - - newMetaList = MList[0] - if MList[0]: - for M in MList[1:]: - lastID = newMetaList[-1].__id__ + 1 - for i, meta in enumerate(M): - meta.__id__ = lastID + i - newMetaList.append(meta) - - return newMetaList - - -def excludeCells(MetaList, labelList): - voidMeta = [] - for m in MetaList: - if m.IsEnclosure: - continue - found = False - for label in labelList: - if label in m.Comments: - found = True - break - if not found: - voidMeta.append(m) - - return voidMeta - - -def sortEnclosure(MetaList, MetaVoid, offSet=0): - - newList = {} - for m in MetaVoid: - if m.EnclosureID in newList.keys(): - newList[m.EnclosureID].append(m) - else: - newList[m.EnclosureID] = [m] - - icount = offSet - idLabel = {0: 0} - newMeta = [] - for m in MetaList: - if m.NullCell: - continue - if m.IsEnclosure: - lineComment = """\ -########################################################## - ENCLOSURE {} -##########################################################""".format( - m.EnclosureID - ) - mc = UF.GEOUNED_Solid(None) - mc.Comments = lineComment - newMeta.append(mc) - for e in newList[m.EnclosureID]: - if e.NullCell: - continue - icount += 1 - e.label = icount - idLabel[e.__id__] = e.label - newMeta.append(e) - lineComment = """\ -########################################################## - END ENCLOSURE {} -##########################################################""".format( - m.EnclosureID - ) - mc = UF.GEOUNED_Solid(None) - mc.Comments = lineComment - newMeta.append(mc) - - else: - icount += 1 - m.label = icount - idLabel[m.__id__] = m.label - newMeta.append(m) - - lineComment = """\ -########################################################## - VOID CELLS -##########################################################""" - mc = UF.GEOUNED_Solid(None) - mc.Comments = lineComment - newMeta.append(mc) - - for v in newList[0]: - if v.NullCell: - continue - icount += 1 - v.label = icount - idLabel[v.__id__] = v.label - newMeta.append(v) - - for m in newMeta: - if not m.Void: - continue - if m.IsEnclosure: - continue - updateComment(m, idLabel) - - return newMeta +from .core import CadToCsg +from .utils.data_classes import NumericFormat, Options, Settings, Tolerances diff --git a/src/geouned/GEOUNED/CodeVersion.py b/src/geouned/GEOUNED/code_version.py similarity index 92% rename from src/geouned/GEOUNED/CodeVersion.py rename to src/geouned/GEOUNED/code_version.py index 9af84e16..d02c2595 100644 --- a/src/geouned/GEOUNED/CodeVersion.py +++ b/src/geouned/GEOUNED/code_version.py @@ -7,7 +7,7 @@ Modification in 0.9.6 Release Date 24/11/2022 - Small change in the reading of material file. - - In "CellDefinition.py" file. "isOposite" function is call with rhe correct tolerance value instead of default value of the isOposite function. + - In "CellDefinition.py" file. "is_opposite" function is call with rhe correct tolerance value instead of default value of the is_opposite function. Modification in 0.9.7 Release Date 06/12/2022 - Compatibility with FreeCAD version higher than 0.18. @@ -21,7 +21,7 @@ - new module for plane splitting in decomposition modules. All planes are considerer equivalent (PX,PY,PZ,P), parallel planes are grouped together. Decomposition is performed by splitting first with group of parallel planes with lowest number of elements. Old version module is still available by switching the option keyword "newSplitPlane" to False. - - New option added when enclosure are used. The option "sortEnclosure" True (Default False) will produce output file where solids and void of an enclosure + - New option added when enclosure are used. The option "sort_enclosure" True (Default False) will produce output file where solids and void of an enclosure are grouped together (first solid cell of the enclosure, then voids) - Options has been added to enlarge Box dimension when Constraint table is evaluated diff --git a/src/geouned/GEOUNED/Cuboid/__init__.py b/src/geouned/GEOUNED/conversion/__init__.py similarity index 100% rename from src/geouned/GEOUNED/Cuboid/__init__.py rename to src/geouned/GEOUNED/conversion/__init__.py diff --git a/src/geouned/GEOUNED/conversion/cell_definition.py b/src/geouned/GEOUNED/conversion/cell_definition.py new file mode 100644 index 00000000..f5d109cb --- /dev/null +++ b/src/geouned/GEOUNED/conversion/cell_definition.py @@ -0,0 +1,1159 @@ +############################ +# Module for Cell definiton # +############################# +import logging +import math + +import FreeCAD +import Part + +from ..utils import basic_functions_part2 as BF +from ..utils import functions as UF +from ..utils import geometry_gu as GU +from ..utils.basic_functions_part1 import ( + is_in_line, + is_opposite, + is_parallel, + is_same_value, + sign_plane, +) +from ..utils.boolean_function import BoolSequence, insert_in_sequence +from ..utils.boolean_solids import build_c_table_from_solids, remove_extra_surfaces +from ..utils.functions import GeounedSurface + +logger = logging.getLogger("general_logger") + + +# TODO rename this function as there are two with the same name +def get_id(facein, surfaces, options, tolerances, numeric_format): + + surfin = str(facein) + if surfin == "": + if is_parallel(facein.Axis, FreeCAD.Vector(1, 0, 0), tolerances.pln_angle): + p = "PX" + elif is_parallel(facein.Axis, FreeCAD.Vector(0, 1, 0), tolerances.pln_angle): + p = "PY" + elif is_parallel(facein.Axis, FreeCAD.Vector(0, 0, 1), tolerances.pln_angle): + p = "PZ" + else: + p = "P" + + for s in surfaces[p]: + if BF.is_same_plane( + facein, + s.Surf, + options=options, + tolerances=tolerances, + numeric_format=numeric_format, + ): + return s.Index + + elif surfin == "": + for s in surfaces["Cyl"]: + if BF.is_same_cylinder( + facein, + s.Surf, + options=options, + tolerances=tolerances, + numeric_format=numeric_format, + ): + return s.Index + + elif surfin == "": + for s in surfaces["Cone"]: + if BF.is_same_cone( + facein, + s.Surf, + dtol=tolerances.kne_distance, + atol=tolerances.kne_angle, + rel_tol=tolerances.relativeTol, + ): + return s.Index + + elif surfin[0:6] == "Sphere": + for s in surfaces["Sph"]: + if BF.is_same_sphere(facein, s.Surf, tolerances.sph_distance, rel_tol=tolerances.relativeTol): + return s.Index + + elif surfin == "": + for s in surfaces["Tor"]: + if BF.is_same_torus( + facein, + s.Surf, + dtol=tolerances.tor_distance, + atol=tolerances.tor_angle, + rel_tol=tolerances.relativeTol, + ): + return s.Index + + return 0 + + +def is_inverted(solid): + + face = solid.Faces[0] + + # u=(face.Surface.bounds()[0]+face.Surface.bounds()[1])/2.0 # entre 0 y 2pi si es completo + # v=face.Surface.bounds()[0]+(face.Surface.bounds()[3]-face.Surface.bounds()[2])/3.0 # a lo largo del eje + parameter_range = face.ParameterRange + u = (parameter_range[1] + parameter_range[0]) / 2.0 + v = (parameter_range[3] + parameter_range[2]) / 2.0 + + if str(face.Surface) == "": + dist1 = face.Surface.value(u, v).distanceToLine(face.Surface.Center, face.Surface.Axis) + dist2 = ( + face.Surface.value(u, v) + .add(face.Surface.normal(u, v).multiply(1.0e-6)) + .distanceToLine(face.Surface.Center, face.Surface.Axis) + ) + if (dist2 - dist1) < 0.0: + # The normal of the cylinder is going inside + return True + elif str(face.Surface) == "": + dist1 = face.Surface.value(u, v).distanceToLine(face.Surface.Apex, face.Surface.Axis) + dist2 = ( + face.Surface.value(u, v) + .add(face.Surface.normal(u, v).multiply(1.0e-6)) + .distanceToLine(face.Surface.Apex, face.Surface.Axis) + ) + if (dist2 - dist1) < 0.0: + # The normal of the cylinder is going inside + return True + # MIO + elif str(face.Surface)[0:6] == "Sphere": + # radii = point - center + radii = face.Surface.value(u, v).add(face.Surface.Center.multiply(-1)) + radii_b = face.Surface.value(u, v).add(face.Surface.normal(u, v).multiply(1.0e-6)).add(face.Surface.Center.multiply(-1)) + # radii_b = radii.add( face.Surface.normal(u,v).multiply(1.0e-6) ) + if (radii_b.Length - radii.Length) < 0.0: + # An increasing of the radii vector in the normal direction decreases the radii: oposite normal direction + return True + + elif str(face.Surface) == "": + dist1 = face.CenterOfMass.distanceToPoint(solid.BoundBox.Center) + dist2 = face.CenterOfMass.add(face.normalAt(u, v).multiply(1.0e-6)).distanceToPoint(solid.BoundBox.Center) + point2 = face.CenterOfMass.add(face.normalAt(u, v).multiply(1.0e-6)) + if solid.isInside(point2, 1e-7, False): + return True + + return False + + +def gen_plane(face, solid, tolerances): + """Generate an additional plane when convex surfaces of second order are presented as a face of the solid""" + + surf = face.Surface + if str(surf) == "": + return gen_plane_cylinder(face, solid, tolerances) + if str(surf) == "": + return gen_plane_cone(face, solid, tolerances) + if str(surf) == "Sphere": + return gen_plane_sphere(face, solid) + + +def get_closed_ranges(solid, face_index): + + u_nodes = [] + for index in face_index: + URange = solid.Faces[index].ParameterRange + u_nodes.append((URange[0], index)) + u_nodes.append((URange[1], index)) + u_nodes.sort() + + closed_range = get_intervals(u_nodes) + + a_min = closed_range[0][0][0] + a_max = closed_range[-1][1][0] + + if abs(a_max - a_min - 2.0 * math.pi) < 1e-2: + if len(closed_range) == 1: + closed_face = True + else: + endPoint = (closed_range[-1][0][0] - 2 * math.pi, closed_range[-1][0][1]) + closed_range[0][0] = endPoint + closed_range[0][2].update(closed_range[-1][2]) + del closed_range[-1] + + if len(closed_range) == 1: + if abs(closed_range[0][1][0] - closed_range[0][0][0] - 2.0 * math.pi) < 1e-2: + closed_face = True + else: + closed_face = False + else: + closed_face = False + else: + closed_face = False + return closed_range, closed_face + + +def get_intervals(u_nodes): + closed_ranges = [] + pos_min = dict() + pos_max = dict() + for i, node in enumerate(u_nodes): + if node[1] not in pos_min.keys(): + pos_min[node[1]] = i + else: + pos_max[node[1]] = i + + u_min = u_nodes[0] + i_pos = pos_max[u_min[1]] + + while True: + x = u_nodes[i_pos] + end = True + for i in range(i_pos + 1, len(u_nodes)): + mxt_int = u_nodes[i][1] + if ( + u_nodes[pos_min[mxt_int]][0] - x[0] + ) < 1e-5: # x pos is > min boundary of the next inteval inside precision 1e-5 + i_pos = pos_max[mxt_int] + end = False + break + + if end: + u_max = x + closed_ranges.append([u_min, u_max]) + i_pos += 1 + if i_pos < len(u_nodes): + u_min = u_nodes[i_pos] + i_pos = pos_max[u_min[1]] + else: + break + + for closed_range in closed_ranges: + index = set() + xmin = closed_range[0][0] + xmax = closed_range[1][0] + for interval in u_nodes: + x = interval[0] + if (xmin - x) < 1.0e-5 and (x - xmax) < 1.0e-5: + index.add(interval[1]) + closed_range.append(index) + + return closed_ranges + + +def get_u_value_boundary(solid, face_index, my_index): + + face_u_ranges, closed_face = get_closed_ranges(solid, face_index) + if closed_face: + return None, None + + for face_u_range in face_u_ranges: + if my_index in face_u_range[2]: + u_min, u_max = face_u_range[0:2] + return u_min, u_max + + +def gen_plane_sphere(face, solid): + same_faces = [] + same_faces.append(face) + + for f in solid.Faces: + if f.isEqual(face) or str(f.Surface) != "Sphere": + continue + if f.Surface.Center == face.Surface.Center and f.Surface.Radius == face.Surface.Radius: + # print 'Warning: coincident sphere faces are the same' + for f2 in same_faces: + if f.distToShape(f2)[0] < 1e-6: + same_faces.append(f) + break + + # print same_faces + normal = FreeCAD.Vector(0, 0, 0) + for f in same_faces: + normal += f.Area * (f.CenterOfMass - face.Surface.Center) + + return Part.Plane(face.Surface.Center, normal).toShape() + + +def gen_plane_cylinder(face, solid, tolerances): + + surf = face.Surface + rad = surf.Radius + + if str(surf) != "": + return None + + my_index = solid.Faces.index(face) + face_index = [my_index] + + for i, face2 in enumerate(solid.Faces): + if face2.Area < tolerances.min_area: + logger.warning( + f"surface {str(surf)} removed from cell definition. Face area < Min area ({face2.Area} < {tolerances.min_area})" + ) + continue + if str(face2.Surface) == "" and not (face2.isEqual(face)): + if ( + face2.Surface.Axis.isEqual(face.Surface.Axis, 1e-5) + and face2.Surface.Radius == rad + and is_in_line(face2.Surface.Center, face.Surface.Axis, face.Surface.Center) + ): + # print 'Warning: coincident cylinder faces are the same' + face_index.append(i) + + u_min, u_max = get_u_value_boundary(solid, face_index, my_index) + if u_min is None: + return None + + u_1, i1 = u_min + u_2, i2 = u_max + + v_1 = solid.Faces[i1].ParameterRange[2] + v_2 = solid.Faces[i2].ParameterRange[2] + + p1 = solid.Faces[i1].valueAt(u_1, v_1) + p2 = solid.Faces[i2].valueAt(u_2, v_2) + + if p1.isEqual(p2, 1e-5): + logger.error("Error in the additional place definition") + return None + + normal = p2.sub(p1).cross(face.Surface.Axis) + plane = Part.Plane(p1, normal).toShape() + + return plane + + +def gen_plane_cylinder_old(face, solid, tolerances): + + surf = face.Surface + rad = surf.Radius + + if str(surf) != "": + return None + + face_index = [solid.Faces.index(face)] + + for i, face2 in enumerate(solid.Faces): + if face2.Area < tolerances.min_area: + logger.warning( + f"surface {str(surf)} removed from cell definition. Face area < Min area ({face2.Area} < {tolerances.min_area})" + ) + continue + if str(face2.Surface) == "" and not (face2.isEqual(face)): + if ( + face2.Surface.Axis.isEqual(face.Surface.Axis, 1e-5) + and face2.Surface.Radius == rad + and is_in_line(face2.Surface.Center, face.Surface.Axis, face.Surface.Center) + ): + # print 'Warning: coincident cylinder faces are the same' + face_index.append(i) + + angle_range = 0.0 + U_val = [] + for index in face_index: + Range = solid.Faces[index].ParameterRange + angle_range = angle_range + abs(Range[1] - Range[0]) + if not (Range[0] in U_val) and not (Range[1] in U_val): + U_val.append(Range[0]) + U_val.append(Range[1]) + if 2.0 * math.pi - angle_range < 1e-2: + return None + + UVNodes = [] + for index in face_index: + face2 = solid.Faces[index] + try: + UVNodes.append(face2.getUVNodes()) + except RuntimeError: + UVNodes.append(face2.getUVNodes()) + + U_val_str_cl = [] + for i, elem1 in enumerate(U_val): + num_str1 = f"{elem1:11.4E}" + if abs(elem1) < 1.0e-5: + num_str1 = "%11.4E" % 0.0 + if not (BF.is_duplicate_in_list(num_str1, i, U_val)): + U_val_str_cl.append(num_str1) + + face_index_2 = [face_index[0], face_index[0]] + + node_min = UVNodes[0][0] + node_max = UVNodes[0][1] + + dif1_0 = abs(float(U_val_str_cl[0]) - node_min[0]) + dif2_0 = abs(float(U_val_str_cl[1]) - node_max[0]) + + # searching for minimum and maximum angle points + for j, Nodes in enumerate(UVNodes): + for elem in Nodes: + dif1 = abs(float(U_val_str_cl[0]) - elem[0]) + dif2 = abs(float(U_val_str_cl[1]) - elem[0]) + + if dif1 < dif1_0: + node_min = elem + face_index_2[0] = face_index[j] + dif1_0 = dif1 + if dif2 < dif2_0: + node_max = elem + face_index_2[1] = face_index[j] + dif2_0 = dif2 + + v_1 = solid.Faces[face_index_2[0]].valueAt(node_min[0], node_min[1]) + v_2 = solid.Faces[face_index_2[1]].valueAt(node_max[0], node_max[1]) + + if v_1.isEqual(v_2, 1e-5): + logger.error("in the additional place definition") + return None + + normal = v_2.sub(v_1).cross(face.Surface.Axis) + plane = Part.Plane(v_1, normal).toShape() + + return plane + + +def gen_plane_cone(face, solid, tolerances): + + Surf = face.Surface + if str(Surf) != "": + return None + + myIndex = solid.Faces.index(face) + face_index = [myIndex] + + for i, face2 in enumerate(solid.Faces): + if face2.Area < tolerances.min_area: + logger.warning( + f"{str(Surf)} surface removed from cell definition. Face area < Min area ({face2.Area} < {tolerances.min_area})" + ) + continue + if str(face2.Surface) == "" and not (face2.isEqual(face)): + if ( + face2.Surface.Axis.isEqual(face.Surface.Axis, 1e-5) + and face2.Surface.Apex.isEqual(face.Surface.Apex, 1e-5) + and (face2.Surface.SemiAngle - face.Surface.SemiAngle) < 1e-6 + ): + face_index.append(i) + + u_min, u_max = get_u_value_boundary(solid, face_index, myIndex) + if u_min is None: + return None + + u_1, i1 = u_min + u_2, i2 = u_max + + v_1 = solid.Faces[i1].ParameterRange[2] + v_2 = solid.Faces[i2].ParameterRange[2] + + p1 = solid.Faces[i1].valueAt(u_1, v_1) + p2 = solid.Faces[i2].valueAt(u_2, v_2) + + if p1.isEqual(p2, 1e-5): + logger.error("in the additional place definition") + return None + + plane = Part.Plane(p1, p2, face.Surface.Apex).toShape() + + return plane + + +def gen_plane_cone_old(face, solid, tolerances): + + surf = face.Surface + if str(surf) != "": + return None + + face_index = [solid.Faces.index(face)] + + for i, face2 in enumerate(solid.Faces): + if face2.Area < tolerances.min_area: + logger.warning( + f"{str(surf)} surface removed from cell definition. Face area < Min area ({face2.Area} < {tolerances.min_area})" + ) + continue + if str(face2.Surface) == "" and not (face2.isEqual(face)): + if ( + face2.Surface.Axis.isEqual(face.Surface.Axis, 1e-5) + and face2.Surface.Apex.isEqual(face.Surface.Apex, 1e-5) + and (face2.Surface.SemiAngle - face.Surface.SemiAngle) < 1e-6 + ): + face_index.append(i) + + angle_range = 0.0 + u_val = [] + for index in face_index: + parameter_range = solid.Faces[index].ParameterRange + angle_range = angle_range + abs(parameter_range[1] - parameter_range[0]) + u_val.append(parameter_range[0]) + u_val.append(parameter_range[1]) + + if 2.0 * math.pi - angle_range < 1e-2: + return None + + uv_nodes = [] + for index in face_index: + face2 = solid.Faces[index] + try: + uv_nodes.append(face2.getUVNodes()) + except RuntimeError: + face.tessellate(1.0, True) + uv_nodes.append(face2.getUVNodes()) + + u_val_str_cl = [] + + for i, elem1 in enumerate(u_val): + num_str1 = f"{elem1:11.4E}" + if abs(elem1) < 1.0e-5: + num_str1 = "%11.4E" % 0.0 + if not (BF.is_duplicate_in_list(num_str1, i, u_val)): + u_val_str_cl.append(num_str1) + + face_index_2 = [face_index[0], face_index[0]] + + node_min = uv_nodes[0][0] + node_max = uv_nodes[0][1] + dif1_0 = abs(float(u_val_str_cl[0]) - node_min[0]) + dif2_0 = abs(float(u_val_str_cl[1]) - node_max[0]) + + # searching for minimum and maximum angle points + for j, Nodes in enumerate(uv_nodes): + for elem in Nodes: + dif1 = abs(float(u_val_str_cl[0]) - elem[0]) + dif2 = abs(float(u_val_str_cl[1]) - elem[0]) + if dif1 < dif1_0: + node_min = elem + face_index_2[0] = face_index[j] + dif1_0 = dif1 + if dif2 < dif2_0: + node_max = elem + face_index_2[1] = face_index[j] + dif2_0 = dif2 + + v_1 = solid.Faces[face_index_2[0]].valueAt(node_min[0], node_min[1]) + v_2 = solid.Faces[face_index_2[1]].valueAt(node_max[0], node_max[1]) + + if v_1.isEqual(v_2, 1e-5): + logger.error("in the additional place definition") + return None + + plane = Part.Plane(v_1, v_2, face.Surface.Apex).toShape() + + return plane + + +def gen_torus_annex_u_planes(face, u_params, tolerances): + + if is_parallel(face.Surface.Axis, FreeCAD.Vector(1, 0, 0), tolerances.tor_angle): + axis = FreeCAD.Vector(1, 0, 0) + elif is_parallel(face.Surface.Axis, FreeCAD.Vector(0, 1, 0), tolerances.tor_angle): + axis = FreeCAD.Vector(0, 1, 0) + elif is_parallel(face.Surface.Axis, FreeCAD.Vector(0, 0, 1), tolerances.tor_angle): + axis = FreeCAD.Vector(0, 0, 1) + + center = face.Surface.Center + p1 = face.valueAt(u_params[0], 0.0) + p2 = face.valueAt(u_params[1], 0.0) + pmid = face.valueAt(0.5 * (u_params[0] + u_params[1]), 0.0) + + if is_same_value(abs(u_params[1] - u_params[0]), math.pi, tolerances.value): + d = axis.cross(p2 - p1) + d.normalize() + if d.dot(pmid - center) < 0: + d = -d + return ( + (center, d, face.Surface.MajorRadius, face.Surface.MajorRadius), + None, + ), False + + elif u_params[1] - u_params[0] < math.pi: + d = axis.cross(p2 - p1) + d.normalize() + if d.dot(pmid - center) < 0: + d = -d + return ( + (center, d, face.Surface.MajorRadius, face.Surface.MajorRadius), + None, + ), False + + else: + d1 = axis.cross(p1) + d1.normalize() + if d1.dot(pmid - center) < 0: + d1 = -d1 + + d2 = axis.cross(p2) + d2.normalize() + if d2.dot(pmid - center) < 0: + d2 = -d2 + + return ( + (center, d1, face.Surface.MajorRadius, face.Surface.MajorRadius), + (center, d2, face.Surface.MajorRadius, face.Surface.MajorRadius), + ), True # (d1 : d2) + + +def gen_torus_annex_u_planes_org(face, u_params, tolerances): + + if is_parallel(face.Surface.Axis, FreeCAD.Vector(1, 0, 0), tolerances.tor_angle): + axis = FreeCAD.Vector(1, 0, 0) + elif is_parallel(face.Surface.Axis, FreeCAD.Vector(0, 1, 0), tolerances.tor_angle): + axis = FreeCAD.Vector(0, 1, 0) + elif is_parallel(face.Surface.Axis, FreeCAD.Vector(0, 0, 1), tolerances.tor_angle): + axis = FreeCAD.Vector(0, 0, 1) + + center = face.Surface.Center + p1 = face.valueAt(u_params[0], 0.0) + p2 = face.valueAt(u_params[1], 0.0) + pmid = face.valueAt(0.5 * (u_params[0] + u_params[1]), 0.0) + + if is_same_value(abs(u_params[1] - u_params[0]), math.pi, tolerances.value): + d = axis.cross(p2 - p1) + d.normalize() + if pmid.dot(d) < 0: + d = -d + return ((center, d, face.Surface.MajorRadius), None), False + + else: + d1 = axis.cross(p1) + d1.normalize() + if pmid.dot(d1) < 0: + d1 = -d1 + + d2 = axis.cross(p2) + d2.normalize() + if pmid.dot(d2) < 0: + d2 = -d2 + + if u_params[1] - u_params[0] < math.pi: + return ( + (center, d1, face.Surface.MajorRadius, face.Surface.MajorRadius), + (center, d2, face.Surface.MajorRadius, face.Surface.MajorRadius), + ), False # ( d1 d2 ) + else: + return ( + (center, d1, face.Surface.MajorRadius, face.Surface.MajorRadius), + (center, d2, face.Surface.MajorRadius, face.Surface.MajorRadius), + ), True # (d1 : d2) + + +def gen_torus_annex_v_surface(face, v_params, tolerances, force_cylinder=False): + if is_parallel(face.Surface.Axis, FreeCAD.Vector(1, 0, 0), tolerances.tor_angle): + axis = FreeCAD.Vector(1, 0, 0) + elif is_parallel(face.Surface.Axis, FreeCAD.Vector(0, 1, 0), tolerances.tor_angle): + axis = FreeCAD.Vector(0, 1, 0) + elif is_parallel(face.Surface.Axis, FreeCAD.Vector(0, 0, 1), tolerances.tor_angle): + axis = FreeCAD.Vector(0, 0, 1) + + p1 = face.valueAt(0.0, v_params[0]) - face.Surface.Center + z1 = p1.dot(axis) + d1 = p1.cross(axis).Length + + p2 = face.valueAt(0.0, v_params[1]) - face.Surface.Center + z2 = p2.dot(axis) + d2 = p2.cross(axis).Length + + if is_same_value(z1, z2, tolerances.distance): + surf_type = "Plane" + center = face.Surface.Center + z1 * axis + v_mid = (v_params[0] + v_params[1]) * 0.5 + p_mid = face.valueAt(0, v_mid) - face.Surface.Center + if p_mid.dot(axis) < z1: + in_surf = True + else: + in_surf = False + return ( + (center, axis, face.Surface.MajorRadius, face.Surface.MajorRadius), + surf_type, + in_surf, + ) + + elif is_same_value(d1, d2, tolerances.distance) or force_cylinder: + surf_type = "Cylinder" + radius = min(d1, d2) + center = face.Surface.Center + if is_same_value(d1, face.Surface.MajorRadius, tolerances.distance): + v_mid = (v_params[0] + v_params[1]) * 0.5 + p_mid = face.valueAt(0, v_mid) - center + if p_mid.cross(axis).Length < face.Surface.MajorRadius: + in_surf = True + v_mid = (v_params[0] + v_params[1]) * 0.5 + p_mid = face.valueAt(0, v_mid) - center + if p_mid.cross(axis).Length < face.Surface.MajorRadius: + in_surf = True + radius = max(d1, d2) + else: + in_surf = False + else: + if d1 < face.Surface.MajorRadius: + in_surf = True + radius = max(d1, d2) + else: + in_surf = False + return (center, axis, radius, face.Surface.MinorRadius), surf_type, in_surf + + else: + surf_type = "Cone" + za = (z2 * d1 - z1 * d2) / (d1 - d2) + apex = face.Surface.Center + za * axis + semi_angle = abs(math.atan(d1 / (z1 - za))) + + cone_axis = axis if (z1 - za) > 0.0 else -axis + + v_mid = (v_params[0] + v_params[1]) * 0.5 + p_mid = face.valueAt(0, v_mid) - face.Surface.Center + z_mid = p_mid.dot(axis) + d_mid = p_mid.cross(axis).Length + + d_cone = d1 * (z_mid - za) / (z1 - za) + in_surf = True if d_mid < d_cone else False + + return ( + ( + apex, + cone_axis, + semi_angle, + face.Surface.MinorRadius, + face.Surface.MajorRadius, + ), + surf_type, + in_surf, + ) + + +def cellDef(meta_obj, surfaces, universe_box, options, tolerances, numeric_format): + + solids = meta_obj.Solids + del_list = [] + + piece_def = BoolSequence(operator="OR") + iece_obj = [] + cones = set() + for isol, solid in enumerate(solids): + surf_piece = [] + surf_obj = [] + extra_plane_reverse = dict() + + flag_inv = is_inverted(solid) + solid_gu = GU.SolidGu(solid, tolerances=tolerances) + last_torus = -1 + for iface, face in enumerate(solid_gu.Faces): + surface_type = str(face.Surface) + if abs(face.Area) < tolerances.min_area: + logger.warning( + f"{surface_type} surface removed from cell definition. Face area < Min area ({face.Area} < {tolerances.min_area})" + ) + continue + if face.Area < 0: + logger.warning("Negative surface Area") + if face.Orientation not in ("Forward", "Reversed"): + continue + if flag_inv: + orient_temp = face.Orientation + if orient_temp == "Forward": + orient = "Reversed" + elif orient_temp == "Reversed": + orient = "Forward" + else: + orient = face.Orientation + + if "Sphere" in surface_type: + surface_type = "Sphere" + + # cone additional plane is added afterward + if surface_type in ("", "", "Sphere") and orient == "Reversed": + # cone additional plane is added afterward + id_face = get_id(face.Surface, surfaces, options, tolerances, numeric_format) + if surface_type == "": + cones.add(id_face) + if str(id_face) not in surf_piece: + surf_piece.append(str(id_face)) + surf_obj.append(face) + + try: + plane = gen_plane(face, solid_gu, tolerances) + if plane is not None: + plane = GU.PlaneGu(plane) + except: + plane = None + logger.warning("generation of additional plane has failed") + + if plane is not None: + p = GeounedSurface( + ("Plane", (plane.Position, plane.Axis, plane.dim1, plane.dim2)), + universe_box, + Face="Build", + ) + + id, exist = surfaces.add_plane(p, options, tolerances, numeric_format, False) + sign = sign_plane(face.CenterOfMass, p) + if exist: + pp = surfaces.get_surface(id) + if is_opposite(p.Surf.Axis, pp.Surf.Axis, tolerances.angle): + id = -id + id *= sign + + if id_face not in extra_plane_reverse.keys(): + extra_plane_reverse[id_face] = [str(id)] + surf_obj.append(p.shape) + else: + if str(id) not in extra_plane_reverse[id_face]: + extra_plane_reverse[id_face].append(str(id)) + surf_obj.append(p.shape) + + elif surface_type == "": + + if ( + is_parallel(face.Surface.Axis, FreeCAD.Vector(1, 0, 0), tolerances.angle) + or is_parallel(face.Surface.Axis, FreeCAD.Vector(0, 1, 0), tolerances.angle) + or is_parallel(face.Surface.Axis, FreeCAD.Vector(0, 0, 1), tolerances.angle) + ): + + idT = get_id(face.Surface, surfaces, options, tolerances, numeric_format) + + index, u_params = solid_gu.TorusUParams[iface] + if index == last_torus: + continue + last_torus = index + + # add if necesary additional planes following U variable + u_closed, u_minMax = u_params + # u_closed = True + if not u_closed: + planes, ORop = gen_torus_annex_u_planes(face, u_minMax, tolerances) + plane1, plane2 = planes + plane = GeounedSurface(("Plane", plane1), universe_box, Face="Build") + id1, exist = surfaces.add_plane(plane, options, tolerances, numeric_format, False) + if exist: + p = surfaces.get_surface(id1) + if is_opposite(plane.Surf.Axis, p.Surf.Axis, tolerances.pln_angle): + id1 = -id1 + + if plane2 is None: + u_var = "%i" % id1 + else: + plane = GeounedSurface(("Plane", plane2), universe_box, Face="Build") + id2, exist = surfaces.add_plane(plane, options, tolerances, numeric_format, False) + if exist: + p = surfaces.get_surface(id2) + if is_opposite(plane.Surf.Axis, p.Surf.Axis, tolerances.pln_angle): + id2 = -id2 + + u_var = "(%i : %i)" % (id1, id2) if ORop else "%i %i" % (id1, id2) + + else: + u_var = "" + + # add if necesary additional surface following V variable + if orient == "Forward": + v_var = "-%i" % idT + + else: + index, Vparams = solid_gu.TorusVParams[iface] + VClosed, VminMax = Vparams + if VClosed: + v_var = "%i" % idT + else: + surf_params, surf_type, in_surf = gen_torus_annex_v_surface( + face, VminMax, tolerances, options.forceCylinder + ) + + if surf_type == "Cone": + cone = GeounedSurface(("Cone", surf_params), universe_box, Face="Build") + id2, exist = surfaces.add_cone(cone, tolerances) + + elif surf_type == "Cylinder": + cyl = GeounedSurface( + ("Cylinder", surf_params), + universe_box, + Face="Build", + ) + id2, exist = surfaces.add_cylinder(cyl, options, tolerances, numeric_format) + + elif surf_type == "Plane": + plane = GeounedSurface(("Plane", surf_params), universe_box, Face="Build") + id2, exist = surfaces.add_plane(plane, options, tolerances, numeric_format, False) + if exist: + p = surfaces.get_surface(id2) + if is_opposite( + plane.Surf.Axis, + p.Surf.Axis, + tolerances.pln_angle, + ): + id2 = -id2 + + v_var = "%i %i" % (idT, -id2 if in_surf else id2) + + var = v_var if u_closed else " ".join((v_var, u_var)) + if var not in surf_piece: + surf_piece.append(var) + surf_obj.append(face) + else: + logger.info("Only Torus with axis along X, Y, Z axis can be reproduced") + else: + id = get_id(face.Surface, surfaces, options, tolerances, numeric_format) + if surface_type == "": + cones.add(-id) + + surf = face + if id == 0: + logger.warning(f"{surface_type} not found in surface list") + if surface_type == "": + dim1 = face.ParameterRange[1] - face.ParameterRange[0] + dim2 = face.ParameterRange[3] - face.ParameterRange[2] + plane = GeounedSurface( + ( + "Plane", + (face.Surface.Position, face.Surface.Axis, dim1, dim2), + ), + universe_box, + Face="Build", + ) + id, exist = surfaces.add_plane(plane, options, tolerances, numeric_format, False) + surf = plane.shape + elif surface_type == "": + dim_l = face.ParameterRange[3] - face.ParameterRange[2] + cylinder = GeounedSurface( + ( + "Cylinder", + ( + face.Surface.Center, + face.Surface.Axis, + face.Surface.Radius, + dim_l, + ), + ), + universe_box, + Face="Build", + ) + id, exist = surfaces.add_cylinder(cylinder, options, tolerances, numeric_format) + surf = cylinder.shape + + if orient == "Reversed": + var = id + elif orient == "Forward": + var = -id + + if surface_type == "": + s = surfaces.get_surface(id) + if is_opposite(face.Surface.Axis, s.Surf.Axis, tolerances.pln_angle): + var = -var + + if str(var) in surf_piece: + continue + + surf_piece.append(str(var)) + surf_obj.append(surf) + + if extra_plane_reverse: + for extra in extra_plane_reverse.values(): + if len(extra) == 1: + if extra[0] not in surf_piece: + surf_piece.append(extra[0]) + else: + surf_piece.append(f"({':'.join(extra)})") + + surf_piece_bool = BoolSequence(" ".join(surf_piece)) + # possible expresion for e + # i1 + # i1 i2surf_piece + # i1 i2 i3 + # (i1:i2) + # i1 (i2:i3) + + # semi = e.find(':') + # blk = e.find(' ') + # print (e) + # if semi != -1 : + # orTerm = expOR(int(e[1:semi]),int(e[semi+1:-1])) + # surf_piece_bool.add(orTerm) + # elif blk != -1 : + # surf_piece_bool.add(int(e[1:blk]),int(e[blk+1:-1])) + # else : + # surf_piece_bool.add(int(e)) + + if surf_piece_bool.elements: + piece_def.append(surf_piece_bool) + iece_obj.append(surf_obj) + else: + del_list.append(isol) + + for isol in reversed(del_list): + del meta_obj.Solids[isol] + meta_obj.set_definition(piece_def) + meta_obj.set_faces(iece_obj) + return tuple(cones) + + +def get_surf_value(definition, reverse=False): + + if definition.level == 0: + if reverse: + surf = {-i for i in definition.elements} + else: + surf = set(definition.elements) + else: + surf = set() + for e in definition.elements: + if e.operator == "AND": + if reverse: + surf = {-i for i in e.elements} + else: + surf = set(e.elements) + break + return surf + + +def append_comp(new_cell, cell_def, cell_cad, meta_complementary): + + surf_cell = get_surf_value(cell_def, True) + if meta_complementary.Definition.operator == "AND": + if not cell_cad.BoundBox.intersect(meta_complementary.CADSolid.BoundBox): + return False + Seq = meta_complementary.Definition + surfComp = get_surf_value(Seq, False) + if len(surfComp & surf_cell) > 0: + return False + new_cell.append(Seq.get_complementary()) + return True + + else: + append = False + for i, compPart in enumerate(meta_complementary.Solids): + if not cell_cad.BoundBox.intersect(compPart.BoundBox): + continue + Seq = meta_complementary.Definition.elements[i] + surfComp = get_surf_value(Seq, False) + if len(surfComp & surf_cell) > 0: + continue + append = True + new_cell.append(Seq.get_complementary()) + return append + + +def no_overlapping_cell(metaList, surfaces, options): + + Surfs = {} + for lst in surfaces.values(): + for s in lst: + Surfs[s.Index] = s + + new_definition_list = [] + metaList[0].set_cad_solid() + + for i, m in enumerate(metaList[1:]): + m.set_cad_solid() + + if m.Definition.operator == "AND": + new_def = BoolSequence(operator="AND") + new_def.append(m.Definition.copy()) + simplify = False + for mm in metaList[: i + 1]: + simp = append_comp(new_def, m.Definition, m.CADSolid, mm) + if simp: + simplify = True + simpTerm = [simplify] + + else: + new_def = BoolSequence(operator="OR") + simpTerm = [] + for j, partSolid in enumerate(m.Solids): + subDef = BoolSequence(operator="AND") + subDef.append(m.Definition.elements[j].copy()) + simplify = False + for mm in metaList[: i + 1]: + simp = append_comp(subDef, m.Definition.elements[j], partSolid, mm) + if simp: + simplify = True + simpTerm.append(simplify) + new_def.append(subDef) + new_definition_list.append((new_def, simpTerm)) + + for m, t_def_and_simplify in zip(metaList[1:], new_definition_list): + t_def, simplify = t_def_and_simplify + if True in simplify: + logger.info(f"reduce cell {m.__id__}") + box = UF.get_box(m) + + # evaluate only diagonal elements of the Constraint Table (fastest) and remove surface not + # crossing in the solid boundBox + CT = build_c_table_from_solids( + box, + (tuple(t_def.get_surfaces_numbers()), Surfs), + option="diag", + options=options, + ) + + new_def = remove_extra_surfaces(t_def, CT) + + # evaluate full constraint Table with less surfaces involved + CT = build_c_table_from_solids( + box, + (tuple(new_def.get_surfaces_numbers()), Surfs), + option="full", + options=options, + ) + + if new_def.operator == "AND": + new_def.simplify(CT) + new_def.clean() + else: + for i, s in enumerate(simplify): + if not s: + continue + comp = new_def.elements[i] + comp.simplify(CT) + comp.clean() + + m.set_definition(new_def) + m.Definition.join_operators() + m.Definition.level_update() + + +# TODO this function looks like it is not used in the code. +def extra_plane_cyl_face(face, box, surfaces, options, tolerances, numeric_format): + wire = face.OuterWire + planes_id = [] + for e in wire.OrderedEdges: + curve = str(e.Curve) + if curve[0:6] == "Circle" or curve == "": + dir = e.Curve.Axis + center = e.Curve.Center + if curve == "": + dim1 = e.Curve.MinorRadius + dim2 = e.Curve.MajorRadius + else: + dim1 = e.Curve.Radius + dim2 = e.Curve.Radius + plane = GeounedSurface(("Plane", (center, dir, dim1, dim2)), box, Face="Build") + id, exist = surfaces.add_plane(plane, options, tolerances, numeric_format, False) + if exist: + pp = surfaces.get_surface(id) + if is_opposite(plane.Surf.Axis, pp.Surf.Axis, tolerances.pln_angle): + id = -id + planes_id.append(id) + return planes_id + + +def add_cone_plane(definition, cones_list, surfaces, universe_box, options, tolerances, numeric_format): + x_axis = FreeCAD.Vector(1, 0, 0) + y_axis = FreeCAD.Vector(0, 1, 0) + z_axis = FreeCAD.Vector(0, 0, 1) + + for cid in cones_list: + cone = surfaces.get_surface(abs(cid)) + if ( + is_parallel(cone.Surf.Axis, x_axis, tolerances.angle) + or is_parallel(cone.Surf.Axis, y_axis, tolerances.angle) + or is_parallel(cone.Surf.Axis, z_axis, tolerances.angle) + ): + continue + + plane = GeounedSurface( + ("Plane", (cone.Surf.Apex, cone.Surf.Axis, 1, 1)), + universe_box, + Face="Build", + ) + pid, exist = surfaces.add_plane(plane, options, tolerances, numeric_format, False) + + if exist: + p = surfaces.get_surface(pid) + if is_opposite(plane.Surf.Axis, p.Surf.Axis, tolerances.pln_angle): + pid = -pid + + if cid > 0: + insert_in_sequence(definition, cid, -pid, "OR") + else: + insert_in_sequence(definition, cid, pid, "AND") diff --git a/src/geouned/GEOUNED/core.py b/src/geouned/GEOUNED/core.py new file mode 100644 index 00000000..f1e01320 --- /dev/null +++ b/src/geouned/GEOUNED/core.py @@ -0,0 +1,928 @@ +import configparser +import json +import logging +import typing +from datetime import datetime +from os import mkdir, path +from pathlib import Path +from typing import get_type_hints + +import FreeCAD +import Part +from tqdm import tqdm + +from .code_version import * +from .conversion import cell_definition as Conv +from .cuboid.translate import translate +from .decompose import decom_one as Decom +from .loadfile import load_step as Load +from .utils import functions as UF +from .utils.boolean_solids import build_c_table_from_solids +from .utils.data_classes import NumericFormat, Options, Settings, Tolerances +from .void import void as void +from .write.functions import write_mcnp_cell_def +from .write.write_files import write_geometry + +logger = logging.getLogger("general_logger") + + +class CadToCsg: + """Base class for the conversion of CAD to CSG models + + Args: + stepFile (str, optional): Name of the CAD file (in STEP format) to + be converted. Defaults to "". + options (geouned.Options, optional): An instance of a geouned.Options + class with the attributes set for the desired conversion. Defaults + to a geouned.Options with default attributes values. + tolerances (geouned.Tolerances, optional): An instance of a + geouned.Tolerances class with the attributes set for the desired + conversion. Defaults to a geouned.Tolerances with default + attributes values. + numeric_format (geouned.NumericFormat, optional): An instance of a + geouned.NumericFormat class with the attributes set for the desired + conversion. Defaults to a geouned.NumericFormat with default + attributes values. + settings (geouned.Settings, optional): An instance of a + geouned.Settings class with the attributes set for the desired + conversion. Defaults to a geouned.Settings with default + attributes values. + """ + + def __init__( + self, + stepFile: str = "", + options: Options = Options(), + tolerances: Tolerances = Tolerances(), + numeric_format: NumericFormat = NumericFormat(), + settings: Settings = Settings(), + ): + + self.stepFile = stepFile + self.options = options + self.tolerances = tolerances + self.numeric_format = numeric_format + self.settings = settings + + def export_csg( + self, + title: str = "Converted with GEOUNED", + geometryName: str = "csg", + outFormat: typing.Tuple[str] = ( + "openmc_xml", + "openmc_py", + "serpent", + "phits", + "mcnp", + ), + volSDEF: bool = False, + volCARD: bool = True, + UCARD: typing.Union[int, type(None)] = 101, + dummyMat: bool = False, + cellCommentFile: bool = False, + cellSummaryFile: bool = True, + ): + """Writes out a CSG file in the requested Monte Carlo code format. + + Args: + title (str, optional): Title of the model written at the top of the + output file. Defaults to "Geouned conversion". + geometryName (str, optional): the file stem of the output file(s). + Defaults to "converted_with_geouned". + outFormat (typing.Tuple[str], optional): Format for the output + geometry. Available format are: "mcnp", "openmc_xml", + "openmc_py", "phits" and "serpent". Several output format can + be written in the same method call. Defaults to output all codes. + volSDEF (bool, optional): Write SDEF definition and tally of solid + cell for stochastic volume checking. Defaults to False. + volCARD (bool, optional): Write the CAD calculated volume in the + cell definition using the VOL card. Defaults to True. + UCARD (int, optional): Write universe card in the cell definition + with the specified universe number (if value = 0 Universe card + is not written). Defaults to None. + dummyMat (bool, optional): Write dummy material definition card in + the MCNP output file for all material labels present in the + model. Dummy material definition is "MX 1001 1". Defaults to False. + cellCommentFile (bool, optional): Write an additional file with + comment associated to each CAD cell in the MCNP output file. + Defaults to False. + cellSummaryFile (bool, optional): Write an additional file with + information on the CAD cell translated. Defaults to True. + """ + + if not isinstance(UCARD, int) and not isinstance(UCARD, type(None)): + raise TypeError(f"UCARD should be of type int or None not {type(UCARD)}") + + for arg, arg_str in ( + (volSDEF, "volSDEF"), + (volCARD, "volCARD"), + (dummyMat, "dummyMat"), + (cellCommentFile, "cellCommentFile"), + (cellSummaryFile, "cellSummaryFile"), + ): + if not isinstance(arg, bool): + raise TypeError(f"{arg} should be of type bool not {type(arg_str)}") + + for arg, arg_str in ((title, "title"), (geometryName, "geometryName")): + if not isinstance(arg, str): + raise TypeError(f"{arg} should be of type str not {type(arg_str)}") + + write_geometry( + UniverseBox=self.UniverseBox, + MetaList=self.MetaList, + Surfaces=self.Surfaces, + settings=self.settings, + options=self.options, + tolerances=self.tolerances, + numeric_format=self.numeric_format, + geometryName=geometryName, + outFormat=outFormat, + cellCommentFile=cellCommentFile, + cellSummaryFile=cellSummaryFile, + title=title, + volSDEF=volSDEF, + volCARD=volCARD, + UCARD=UCARD, + dummyMat=dummyMat, + stepFile=self.stepFile, + ) + + logger.info("End of Monte Carlo code translation phase") + + @classmethod + def from_json(cls, filename: str): + """Creates a CadToCsg instance and returns the instance. Populating the + Options, Tolerance, Settings and NumericFormat attributes from matching + key names in the JSON. If export_to_csg key is present then this method + also runs .start() and .export_to_csg() on the instance. + + Args: + filename str: The filename of the config file. Defaults to "config.json". + + Raises: + FileNotFoundError: If the config file is not found + ValueError: If the config JSON file is found to contain an invalid key + + Returns: + geouned.CadToCsg: returns a geouned CadToCsg class instance. + """ + + if not Path(filename).exists(): + raise FileNotFoundError(f"config file {filename} not found") + + with open(filename) as f: + config = json.load(f) + + cad_to_csg = cls(stepFile=config["stepFile"]) + for key in config.keys(): + + if key in ["stepFile", "export_csg"]: + pass # these two keys are used before or after this loop + + elif key == "Tolerances": + cad_to_csg.tolerances = Tolerances(**config["Tolerances"]) + + elif key == "Options": + cad_to_csg.options = Options(**config["Options"]) + + elif key == "NumericFormat": + cad_to_csg.numeric_format = NumericFormat(**config["NumericFormat"]) + + elif key == "Settings": + cad_to_csg.settings = Settings(**config["Settings"]) + + else: + raise ValueError( + f"Invalid key '{key}' found in config file {filename}. Acceptable key names are 'stepFile', 'export_csg', 'Settings', 'Parameters', 'Tolerances' and 'NumericFormat'" + ) + + cad_to_csg.start() + if "export_csg" in config.keys(): + cad_to_csg.export_csg(**config["export_csg"]) + else: + cad_to_csg.export_csg() + return cad_to_csg + + # TODO add tests as set_configuration is not currently tested + def set_configuration(self, configFile=None): + + if configFile is None: + return + + config = configparser.ConfigParser() + config.optionxform = str + config.read(configFile) + for section in config.sections(): + if section == "Files": + for key in config["Files"].keys(): + if key in ("geometryName", "matFile", "title"): + self.set(key, config.get("Files", key)) + + elif key == "stepFile": + value = config.get("Files", key).strip() + lst = value.split() + if value[0] in ("(", "[") and value[-1] in ("]", ")"): + data = value[1:-1].split(",") + data = [x.strip() for x in data] + self.set(key, data) + elif len(lst) > 1: + self.set(key, lst) + else: + self.set(key, value) + + elif key == "outFormat": + raw = config.get("Files", key).strip() + values = tuple(x.strip() for x in raw.split(",")) + outFormat = [] + for v in values: + if v.lower() == "mcnp": + outFormat.append("mcnp") + elif v.lower() == "openmc_xml": + outFormat.append("openmc_xml") + elif v.lower() == "openmc_py": + outFormat.append("openmc_py") + elif v.lower() == "serpent": + outFormat.append("serpent") + elif v.lower() == "phits": + outFormat.append("phits") + self.set(key, tuple(outFormat)) + + elif section == "Parameters": + for key in config["Parameters"].keys(): + if key in ( + "voidGen", + "debug", + "compSolids", + "volSDEF", + "volCARD", + "dummyMat", + "cellSummaryFile", + "cellCommentFile", + "sort_enclosure", + ): + self.set(key, config.getboolean("Parameters", key)) + elif key in ( + "minVoidSize", + "maxSurf", + "maxBracket", + "startCell", + "startSurf", + ): + self.set(key, config.getint("Parameters", key)) + elif key in ("exportSolids", "UCARD", "simplify"): + self.set(key, config.get("Parameters", key)) + elif key == "voidMat": + value = config.get("Parameters", key).strip() + data = value[1:-1].split(",") + self.set(key, (int(data[0]), float(data[1]), data[2])) + else: + value = config.get("Parameters", key).strip() + data = value[1:-1].split(",") + self.set(key, tuple(map(int, data))) + + elif section == "Options": + attributes_and_types = get_type_hints(Options()) + for key in config["Options"].keys(): + if key in attributes_and_types.keys(): + if attributes_and_types[key] is bool: + value = config.getboolean("Options", key) + elif attributes_and_types[key] is float or attributes_and_types[key] is int: + value = config.getfloat("Options", key) + setattr(self.options, key, value) + + elif section == "Tolerances": + attributes_and_types = get_type_hints(Tolerances()) + for key in config["Tolerances"].keys(): + if key in attributes_and_types.keys(): + if attributes_and_types[key] is bool: + value = config.getboolean("Tolerances", key) + elif attributes_and_types[key] is float: + value = config.getfloat("Tolerances", key) + setattr(self.tolerances, key, value) + + elif section == "MCNP_Numeric_Format": + attributes_and_types = get_type_hints(NumericFormat()) + PdEntry = False + for key in config["MCNP_Numeric_Format"].keys(): + if key in attributes_and_types.keys(): + value = config.get("MCNP_Numeric_Format", key) + setattr(self.numeric_format, key, value) + if key == "P_d": + PdEntry = True + + else: + logger.info(f"bad section name : {section}") + + if self.__dict__["geometryName"] == "": + self.__dict__["geometryName"] = self.__dict__["stepFile"][:-4] + + # TODO see if we can find another way to do this + if self.options.prnt3PPlane and not PdEntry: + self.NumericFormat.P_d = "22.15e" + + logger.info(self.__dict__) + + # TODO add tests as set is not currently tested + def set(self, kwrd, value): + + if kwrd == "stepFile": + if isinstance(value, (list, tuple)): + for v in value: + if not isinstance(v, str): + logger.info(f"elemt in {kwrd} list should be string") + return + elif not isinstance(value, str): + logger.info(f"{kwrd} should be string or tuple of strings") + return + + elif kwrd == "UCARD": + if value == "None": + value = None + elif value.isdigit(): + value = int(value) + else: + logger.info(f"{kwrd} value should be None or integer") + return + elif kwrd == "outFormat": + if len(value) == 0: + return + elif kwrd in ("geometryName", "matFile", "exportSolids"): + if not isinstance(value, str): + logger.info(f"{kwrd} value should be str instance") + return + elif kwrd in ("cellRange", "voidMat", "voidExclude"): + if not isinstance(value, (list, tuple)): + logger.info(f"{kwrd} value should be list or tuple") + return + elif kwrd in ("minVoidSize", "maxSurf", "maxBracket", "startCell", "startSurf"): + if not isinstance(value, int): + logger.info(f"{kwrd} value should be integer") + return + elif kwrd in ( + "voidGen", + "debug", + "compSolids", + "simplifyCTable", + "volSDEF", + "volCARD", + "dummyMat", + "cellSummaryFile", + "cellCommentFile", + "sort_enclosure", + ): + if not isinstance(value, bool): + logger.info(f"{kwrd} value should be boolean") + return + + self.__dict__[kwrd] = value + if kwrd == "stepFile" and self.__dict__["geometryName"] == "": + if isinstance(value, (tuple, list)): + self.__dict__["geometryName"] == "joined_step_files" + else: + self.__dict__["geometryName"] == value[:-4] + + def start(self): + + logger.info("start") + freecad_version = ".".join(FreeCAD.Version()[:3]) + logger.info(f"GEOUNED version {GEOUNED_Version} {GEOUNED_ReleaseDate} \nFreeCAD version {freecad_version}") + + if self.stepFile == "": + raise ValueError("Cannot run the code. Step file name is missing") + + if isinstance(self.stepFile, (tuple, list)): + for stp in self.stepFile: + if not path.isfile(stp): + raise FileNotFoundError(f"Step file {stp} not found.\nStop.") + else: + if not path.isfile(self.stepFile): + raise FileNotFoundError(f"Step file {self.stepFile} not found.\nStop.") + + startTime = datetime.now() + + if isinstance(self.stepFile, (list, tuple)): + step_files = self.stepFile + else: + step_files = [self.stepFile] + MetaChunk = [] + EnclosureChunk = [] + for stp in tqdm(step_files, desc="Loading CAD files"): + logger.info(f"read step file : {stp}") + Meta, Enclosure = Load.load_cad(stp, self.settings, self.options) + MetaChunk.append(Meta) + EnclosureChunk.append(Enclosure) + self.MetaList = join_meta_lists(MetaChunk) + EnclosureList = join_meta_lists(EnclosureChunk) + + logger.info("End of loading phase") + tempstr1 = str(datetime.now() - startTime) + logger.info(tempstr1) + tempTime = datetime.now() + + # Select a specific solid range from original STEP solids + if self.settings.cellRange: + self.MetaList = self.MetaList[self.settings.cellRange[0] : self.settings.cellRange[1]] + + # export in STEP format solids read from input file + # terminate excution + if self.settings.exportSolids != "": + solids = [] + for m in self.MetaList: + if m.IsEnclosure: + continue + solids.extend(m.Solids) + Part.makeCompound(solids).exportStep(self.settings.exportSolids) + msg = f"Solids exported in file {self.settings.exportSolids}\n" "GEOUNED Finish. No solid translation performed." + raise ValueError(msg) + + # set up Universe + if EnclosureList: + self.UniverseBox = get_universe(self.MetaList + EnclosureList) + else: + self.UniverseBox = get_universe(self.MetaList) + + self.Surfaces = UF.SurfacesDict(offset=self.settings.startSurf - 1) + + warnSolids = [] + warnEnclosures = [] + coneInfo = dict() + tempTime0 = datetime.now() + if not self.options.Facets: + + # decompose all solids in elementary solids (convex ones) + warningSolidList = decompose_solids( + self.MetaList, + self.Surfaces, + self.UniverseBox, + self.settings, + True, + self.options, + self.tolerances, + self.numeric_format, + "Decomposing solids", + ) + + # decompose Enclosure solids + if self.settings.voidGen and EnclosureList: + warningEnclosureList = decompose_solids( + EnclosureList, + self.Surfaces, + self.UniverseBox, + self.settings, + False, + self.options, + self.tolerances, + self.numeric_format, + "Decomposing enclosure solids", + ) + + logger.info("End of decomposition phase") + + # start Building CGS cells phase + + for j, m in enumerate(tqdm(self.MetaList, desc="Translating solid cells")): + if m.IsEnclosure: + continue + logger.info(f"Building cell: {j+1}") + cones = Conv.cellDef( + m, + self.Surfaces, + self.UniverseBox, + self.options, + self.tolerances, + self.numeric_format, + ) + if cones: + coneInfo[m.__id__] = cones + if j in warningSolidList: + warnSolids.append(m) + if not m.Solids: + logger.info(f"none {j}, {m.__id__}") + logger.info(m.Definition) + + if self.options.forceNoOverlap: + Conv.no_overlapping_cell(self.MetaList, self.Surfaces, self.options) + + else: + translate( + self.MetaList, + self.Surfaces, + self.UniverseBox, + self.settings, + self.options, + self.tolerances, + ) + # decompose Enclosure solids + if self.settings.voidGen and EnclosureList: + warningEnclosureList = decompose_solids( + EnclosureList, + self.Surfaces, + self.UniverseBox, + self.settings, + False, + self.options, + self.tolerances, + self.numeric_format, + "Decomposing enclosure solids", + ) + + tempstr2 = str(datetime.now() - tempTime) + logger.info(tempstr2) + + # building enclosure solids + + if self.settings.voidGen and EnclosureList: + for j, m in enumerate(EnclosureList): + logger.info(f"Building Enclosure Cell: {j + 1}") + cones = Conv.cellDef( + m, + self.Surfaces, + self.UniverseBox, + self.options, + self.tolerances, + self.numeric_format, + ) + if cones: + coneInfo[m.__id__] = cones + if j in warningEnclosureList: + warnEnclosures.append(m) + + tempTime1 = datetime.now() + + # void generation phase + MetaVoid = [] + if self.settings.voidGen: + logger.info("Build Void") + logger.info(self.settings.voidExclude) + if not self.settings.voidExclude: + MetaReduced = self.MetaList + else: + MetaReduced = exclude_cells(self.MetaList, self.settings.voidExclude) + + if self.MetaList: + init = self.MetaList[-1].__id__ - len(EnclosureList) + else: + init = 0 + MetaVoid = void.void_generation( + MetaReduced, + EnclosureList, + self.Surfaces, + self.UniverseBox, + self.settings, + init, + self.options, + self.tolerances, + self.numeric_format, + ) + + # if self.settings.simplify == 'full' and not self.options.forceNoOverlap: + if self.settings.simplify == "full": + Surfs = {} + for lst in self.Surfaces.values(): + for s in lst: + Surfs[s.Index] = s + + for c in tqdm(self.MetaList, desc="Simplifying"): + if c.Definition.level == 0 or c.IsEnclosure: + continue + logger.info(f"simplify cell {c.__id__}") + Box = UF.get_box(c) + CT = build_c_table_from_solids(Box, (c.Surfaces, Surfs), option="full") + c.Definition.simplify(CT) + c.Definition.clean() + if type(c.Definition.elements) is bool: + logger.info(f"unexpected constant cell {c.__id__} :{c.Definition.elements}") + + tempTime2 = datetime.now() + logger.info(f"build Time: {tempTime2} - {tempTime1}") + + logger.info(datetime.now() - startTime) + + cellOffSet = self.settings.startCell - 1 + if EnclosureList and self.settings.sort_enclosure: + # sort group solid cell / void cell sequence in each for each enclosure + # if a solid belong to several enclosure, its definition will be written + # for the highest enclosure level or if same enclosure level in the first + # enclosure found + self.MetaList = sort_enclosure(self.MetaList, MetaVoid, cellOffSet) + else: + # remove Null Cell and apply cell numbering offset + deleted = [] + idLabel = {0: 0} + icount = cellOffSet + for i, m in enumerate(self.MetaList): + if m.NullCell or m.IsEnclosure: + deleted.append(i) + continue + + icount += 1 + m.label = icount + idLabel[m.__id__] = m.label + + for i in reversed(deleted): + del self.MetaList[i] + + lineComment = """\ +########################################################## + VOID CELLS +##########################################################""" + mc = UF.GeounedSolid(None) + mc.Comments = lineComment + self.MetaList.append(mc) + + deleted = [] + for i, m in enumerate(MetaVoid): + if m.NullCell: + deleted.append(i) + continue + icount += 1 + m.label = icount + update_comment(m, idLabel) + for i in reversed(deleted): + del MetaVoid[i] + + self.MetaList.extend(MetaVoid) + + print_warning_solids(warnSolids, warnEnclosures) + + # add plane definition to cone + process_cones( + self.MetaList, + coneInfo, + self.Surfaces, + self.UniverseBox, + self.options, + self.tolerances, + self.numeric_format, + ) + + logger.info("Process finished") + logger.info(datetime.now() - startTime) + + logger.info(f"Translation time of solid cells {tempTime1} - {tempTime0}") + logger.info(f"Translation time of void cells {tempTime2} - {tempTime1}") + + +def decompose_solids( + MetaList, + Surfaces, + UniverseBox, + setting, + meta, + options, + tolerances, + numeric_format, + description, +): + totsolid = len(MetaList) + warningSolids = [] + for i, m in enumerate(tqdm(MetaList, desc=description)): + if meta and m.IsEnclosure: + continue + logger.info(f"Decomposing solid: {i + 1}/{totsolid}") + if setting.debug: + logger.info(m.Comments) + if not path.exists("debug"): + mkdir("debug") + if m.IsEnclosure: + m.Solids[0].exportStep(f"debug/origEnclosure_{i}.stp") + else: + m.Solids[0].exportStep(f"debug/origSolid_{i}.stp") + + comsolid, err = Decom.SplitSolid( + Part.makeCompound(m.Solids), + UniverseBox, + options, + tolerances, + numeric_format, + ) + + if err != 0: + if not path.exists("Suspicious_solids"): + mkdir("Suspicious_solids") + if m.IsEnclosure: + Part.CompSolid(m.Solids).exportStep(f"Suspicious_solids/Enclosure_original_{i}.stp") + comsolid.exportStep(f"Suspicious_solids/Enclosure_split_{i}.stp") + else: + Part.CompSolid(m.Solids).exportStep(f"Suspicious_solids/Solid_original_{i}.stp") + comsolid.exportStep(f"Suspicious_solids/Solid_split_{i}.stp") + + warningSolids.append(i) + + if setting.debug: + if m.IsEnclosure: + comsolid.exportStep(f"debug/compEnclosure_{i}.stp") + else: + comsolid.exportStep(f"debug/compSolid_{i}.stp") + Surfaces.extend( + Decom.extract_surfaces( + comsolid, + "All", + UniverseBox, + options, + tolerances, + numeric_format, + MakeObj=True, + ), + options, + tolerances, + numeric_format, + ) + m.set_cad_solid() + m.update_solids(comsolid.Solids) + + return warningSolids + + +def update_comment(meta, idLabel): + if meta.__commentInfo__ is None: + return + if meta.__commentInfo__[1] is None: + return + newLabel = (idLabel[i] for i in meta.__commentInfo__[1]) + meta.set_comments(void.void_comment_line((meta.__commentInfo__[0], newLabel))) + + +def process_cones(MetaList, coneInfo, Surfaces, UniverseBox, options, tolerances, numeric_format): + cellId = tuple(coneInfo.keys()) + for m in MetaList: + if m.__id__ not in cellId and not m.Void: + continue + + if m.Void and m.__commentInfo__ is not None: + if m.__commentInfo__[1] is None: + continue + cones = set() + for Id in m.__commentInfo__[1]: + if Id in cellId: + cones.update(-x for x in coneInfo[Id]) + Conv.add_cone_plane( + m.Definition, + cones, + Surfaces, + UniverseBox, + options, + tolerances, + numeric_format, + ) + elif not m.Void: + Conv.add_cone_plane( + m.Definition, + coneInfo[m.__id__], + Surfaces, + UniverseBox, + options, + tolerances, + numeric_format, + ) + + +def get_universe(MetaList): + d = 10 + Box = MetaList[0].BoundBox + xmin = Box.XMin + xmax = Box.XMax + ymin = Box.YMin + ymax = Box.YMax + zmin = Box.ZMin + zmax = Box.ZMax + for m in MetaList[1:]: + # MIO. This was removed since in HELIAS the enclosure cell is the biggest one + # if m.IsEnclosure: continue + xmin = min(m.BoundBox.XMin, xmin) + xmax = max(m.BoundBox.XMax, xmax) + ymin = min(m.BoundBox.YMin, ymin) + ymax = max(m.BoundBox.YMax, ymax) + zmin = min(m.BoundBox.ZMin, zmin) + zmax = max(m.BoundBox.ZMax, zmax) + + return FreeCAD.BoundBox( + FreeCAD.Vector(xmin - d, ymin - d, zmin - d), + FreeCAD.Vector(xmax + d, ymax + d, zmax + d), + ) + + +def print_warning_solids(warnSolids, warnEnclosures): + + solids_logger = logging.getLogger("solids_logger") + + if warnSolids or warnEnclosures: + pass + else: + return + + if warnSolids: + lines = "Solids :\n" + for sol in warnSolids: + lines += "\n" + lines += f"{sol.label}\n" + lines += f"{sol.Comments}\n" + lines += f"{write_mcnp_cell_def(sol.Definition)}\n" + solids_logger.info(lines) + + if warnEnclosures: + lines = "Enclosures :\n" + for sol in warnEnclosures: + lines += "\n" + lines += f"{sol.label}\n" + lines += f"{sol.Comments}\n" + lines += f"{write_mcnp_cell_def(sol.Definition)}\n" + + solids_logger.info(lines) + + +def join_meta_lists(MList): + + newMetaList = MList[0] + if MList[0]: + for M in MList[1:]: + lastID = newMetaList[-1].__id__ + 1 + for i, meta in enumerate(M): + meta.__id__ = lastID + i + newMetaList.append(meta) + + return newMetaList + + +def exclude_cells(MetaList, labelList): + voidMeta = [] + for m in MetaList: + if m.IsEnclosure: + continue + found = False + for label in labelList: + if label in m.Comments: + found = True + break + if not found: + voidMeta.append(m) + + return voidMeta + + +def sort_enclosure(MetaList, MetaVoid, offSet=0): + + newList = {} + for m in MetaVoid: + if m.EnclosureID in newList.keys(): + newList[m.EnclosureID].append(m) + else: + newList[m.EnclosureID] = [m] + + icount = offSet + idLabel = {0: 0} + newMeta = [] + for m in MetaList: + if m.NullCell: + continue + if m.IsEnclosure: + lineComment = f"""########################################################## + ENCLOSURE {m.EnclosureID} +##########################################################""" + mc = UF.GeounedSolid(None) + mc.Comments = lineComment + newMeta.append(mc) + for e in newList[m.EnclosureID]: + if e.NullCell: + continue + icount += 1 + e.label = icount + idLabel[e.__id__] = e.label + newMeta.append(e) + lineComment = f"""########################################################## + END ENCLOSURE {m.EnclosureID} +##########################################################""" + mc = UF.GeounedSolid(None) + mc.Comments = lineComment + newMeta.append(mc) + + else: + icount += 1 + m.label = icount + idLabel[m.__id__] = m.label + newMeta.append(m) + + lineComment = """\ +########################################################## + VOID CELLS +##########################################################""" + mc = UF.GeounedSolid(None) + mc.Comments = lineComment + newMeta.append(mc) + + for v in newList[0]: + if v.NullCell: + continue + icount += 1 + v.label = icount + idLabel[v.__id__] = v.label + newMeta.append(v) + + for m in newMeta: + if not m.Void: + continue + if m.IsEnclosure: + continue + update_comment(m, idLabel) + + return newMeta diff --git a/src/geouned/GEOUNED/Decompose/__init__.py b/src/geouned/GEOUNED/cuboid/__init__.py similarity index 100% rename from src/geouned/GEOUNED/Decompose/__init__.py rename to src/geouned/GEOUNED/cuboid/__init__.py diff --git a/src/geouned/GEOUNED/cuboid/translate.py b/src/geouned/GEOUNED/cuboid/translate.py new file mode 100644 index 00000000..f9ab03d0 --- /dev/null +++ b/src/geouned/GEOUNED/cuboid/translate.py @@ -0,0 +1,172 @@ +import logging + +import FreeCAD +import Part + +from ..decompose import decom_one as Decom +from ..utils import basic_functions_part2 as BF +from ..utils import geometry_gu as GU +from ..utils.basic_functions_part1 import is_opposite, is_parallel +from ..utils.boolean_function import BoolSequence + +logger = logging.getLogger("general_logger") + + +def commonEdge(face1, face2): + for e1 in face1.Edges: + for e2 in face2.Edges: + if e1.isSame(e2): + return e1 + return None + + +def isConvex(face1, face2, edge): + de = 0.1 + tol = 1.0e-5 + delta_point = edge.Vertexes[1].Point - edge.Vertexes[0].Point + delta_point.normalize() + axis = delta_point.cross(face2.Surface.Axis) + + point = edge.CenterOfMass + de * axis + if not face2.__face__.isInside(point, tol, True): + axis = -axis + + convex = False + if face1.Surface.Axis.dot(axis) < 0: + if face1.Orientation == "Forward": + convex = True + else: + if face1.Orientation == "Reversed": + convex = True + return convex + + +def removeElement(Faces, idf): + for i, f in enumerate(Faces): + if f[0] == idf: + del Faces[i] + break + + +def is_inverted(solid): + face = solid.Faces[0] + parameter_range = face.ParameterRange + u = (parameter_range[1] + parameter_range[0]) / 2.0 + v = (parameter_range[3] + parameter_range[2]) / 2.0 + + point2 = face.CenterOfMass.add(face.normalAt(u, v).multiply(1.0e-6)) + + if solid.isInside(point2, 1e-7, False): + return True + else: + return False + + +# TODO rename this function as there are two with the same name +def get_id(face_in, Surfaces, options, tolerances, numeric_format): + + if is_parallel(face_in.Axis, FreeCAD.Vector(1, 0, 0), tolerances.pln_angle): + plane = "PX" + elif is_parallel(face_in.Axis, FreeCAD.Vector(0, 1, 0), tolerances.pln_angle): + plane = "PY" + elif is_parallel(face_in.Axis, FreeCAD.Vector(0, 0, 1), tolerances.pln_angle): + plane = "PZ" + else: + plane = "P" + + for s in Surfaces[plane]: + if BF.is_same_plane( + face_in, + s.Surf, + options=options, + tolerances=tolerances, + numeric_format=numeric_format, + ): + return s.Index + + return 0 + + +def translate(meta_list, surfaces, universe_box, setting, options, tolerances, numeric_format): + tot_solid = len(meta_list) + for i, m in enumerate(meta_list): + if m.IsEnclosure: + continue + logger.info(f"Decomposing solid: {i}/{tot_solid}") + if setting.debug: + logger.info(m.Comments) + if m.IsEnclosure: + m.Solids[0].exportStep(f"origEnclosure_{i}.stp") + else: + m.Solids[0].exportStep(f"origSolid_{i}.stp") + + surfaces.extend( + Decom.extract_surfaces( + Part.makeCompound(m.Solids), + "Plane3Pts", + universe_box, + options, + tolerances, + numeric_format, + MakeObj=False, + ) + ) + set_definition(m, surfaces, options, tolerances) + + +# TODO rename this, but be careful as there are other functions in the code with the same name +def set_definition(meta_obj, surfaces, options, tolerances): + solids = meta_obj.Solids + s_def = BoolSequence(operator="OR") + + for sol in solids: + subSol = BoolSequence(operator="AND") + flag_inv = is_inverted(sol) + solid_gu = GU.SolidGu(sol, tolerances=tolerances, plane3Pts=True) + + faces = [] + for face in solid_gu.Faces: + if abs(face.Area) < 1e-2: + continue + if face.Area < 0: + logger.warning("Negative surface Area") + if str(face.Surface) != "": + logger.warning("All surfaces must be plane") + continue + if face.Orientation not in ("Forward", "Reversed"): + continue + + id = get_id(face.Surface, surfaces, options, tolerances) + s = surfaces.get_surface(id) + if is_opposite(face.Surface.Axis, s.Surf.Axis, tolerances.pln_angle): + id = -id + if face.Orientation == "Forward": + id = -id + if flag_inv: + id = -id + faces.append((id, face)) + + while len(faces) > 0: + id1, face1 = faces[0] + no_convex = [] + for id2, face2 in faces[1:]: + edge = commonEdge(face1, face2) + if edge is None: + continue + if not isConvex(face1, face2, edge): + no_convex.append(id2) + + if no_convex != []: + no_convex.insert(0, id1) + for i in no_convex: + removeElement(faces, i) + orPlanes = BoolSequence(operator="OR") + orPlanes.append(*no_convex) + subSol.append(orPlanes) + else: + removeElement(faces, id1) + subSol.append(id1) + + s_def.append(subSol) + + meta_obj.set_definition(s_def) diff --git a/src/geouned/GEOUNED/LoadFile/__init__.py b/src/geouned/GEOUNED/decompose/__init__.py similarity index 100% rename from src/geouned/GEOUNED/LoadFile/__init__.py rename to src/geouned/GEOUNED/decompose/__init__.py diff --git a/src/geouned/GEOUNED/Decompose/Decom_one.py b/src/geouned/GEOUNED/decompose/decom_one.py similarity index 63% rename from src/geouned/GEOUNED/Decompose/Decom_one.py rename to src/geouned/GEOUNED/decompose/decom_one.py index 3246e4db..9b2064e3 100644 --- a/src/geouned/GEOUNED/Decompose/Decom_one.py +++ b/src/geouned/GEOUNED/decompose/decom_one.py @@ -2,49 +2,55 @@ # Only one solid and planar surfaces # +import logging import math from collections import OrderedDict import FreeCAD import Part -from ..Conversion import CellDefinition as CD -from ..Utils import Functions as UF -from ..Utils import Geometry_GU as GU -from ..Utils.BasicFunctions_part1 import isInLine, isInPlane, isParallel, isSameValue -from ..Utils.BasicFunctions_part2 import isDuplicateInList -from ..Utils.Options.Classes import Options as opt -from ..Utils.Options.Classes import Tolerances as tol +from ..conversion import cell_definition as CD +from ..utils import functions as UF +from ..utils import geometry_gu as GU +from ..utils.basic_functions_part1 import ( + is_in_line, + is_in_plane, + is_parallel, + is_same_value, +) +from ..utils.basic_functions_part2 import is_duplicate_in_list + +logger = logging.getLogger("general_logger") twoPi = math.pi * 2 -def splitFullCylinder(solid): +def split_full_cylinder(solid, options, tolerances, numeric_format): explode = [] - Bases = [solid] + bases = [solid] while True: - newBases = [] - for base in Bases: - cutSolids = cutFullCylinder(base) - if len(cutSolids) == 1: - explode.extend(cutSolids) + new_bases = [] + for base in bases: + cut_solids = cut_full_cylinder(base, options, tolerances, numeric_format) + if len(cut_solids) == 1: + explode.extend(cut_solids) else: - newBases.extend(cutSolids) - if len(newBases) == 0: + new_bases.extend(cut_solids) + if len(new_bases) == 0: break else: - Bases = newBases + bases = new_bases return Part.makeCompound(explode) -def cutFullCylinder(solid): - solid_GU = GU.solid_GU(solid) - Surfaces = UF.Surfaces_dict() - flag_inv = CD.isInverted(solid_GU.solid) - UniverseBox = solid.BoundBox +def cut_full_cylinder(solid, options, tolerances, numeric_format): + solid_gu = GU.SolidGu(solid, tolerances=tolerances) + surfaces = UF.SurfacesDict() + flag_inv = CD.is_inverted(solid_gu.solid) + universe_box = solid.BoundBox - for face in solid_GU.Faces: + for face in solid_gu.Faces: surf = str(face.Surface) if surf == "": if flag_inv: @@ -60,66 +66,64 @@ def cutFullCylinder(solid): dir = face.Surface.Axis orig = face.Surface.Center rad = face.Surface.Radius - dimL = face.ParameterRange[3] - face.ParameterRange[2] - cylinder = UF.GEOUNED_Surface( - ("Cylinder", (orig, dir, rad, dimL)), UniverseBox - ) - cylinder.buildSurface() - Surfaces.addCylinder(cylinder, False) + dim_l = face.ParameterRange[3] - face.ParameterRange[2] + cylinder = UF.GeounedSurface(("Cylinder", (orig, dir, rad, dim_l)), universe_box) + cylinder.build_surface() + surfaces.add_cylinder(cylinder, options, tolerances, numeric_format, False) # add planes if cylinder axis is cut by a plane (plane quasi perpedicular to axis) - for p in CylBoundPlanes(face, UniverseBox): - p.buildSurface() - Surfaces.addPlane(p, False) + for p in cyl_bound_planes(face, universe_box): + p.build_surface() + surfaces.add_plane(p, options, tolerances, numeric_format, False) break - Planes = [] + planes = [] for P in ("PX", "PY", "PZ", "P"): - Planes.extend(Surfaces[P]) - Planes = sortPlanes(Planes, True) + planes.extend(surfaces[P]) + planes = sort_planes(planes, True) - if len(Planes) == 0: + if len(planes) == 0: return [solid] - if len(Planes[-1]) < opt.nPlaneReverse: - Planes.reverse() + if len(planes[-1]) < options.nPlaneReverse: + planes.reverse() cut = False - for pp in Planes: + for pp in planes: # cut with more external parallel planes - pp[0].buildSurface() + pp[0].build_surface() if len(pp) != 1: - pp[-1].buildSurface() + pp[-1].build_surface() tools = (pp[0].shape, pp[-1].shape) else: tools = (pp[0].shape,) try: - comsolid = UF.splitBOP(solid, tools, opt.splitTolerance) + comsolid = UF.split_bop(solid, tools, options.splitTolerance, options) except: comsolid = solid if len(comsolid.Solids) > 1: - outSolid = comsolid.Solids + out_solid = comsolid.Solids cut = True break if cut: - return outSolid + return out_solid - tool = (Surfaces["Cyl"][0].shape,) + tool = (surfaces["Cyl"][0].shape,) try: - comsolid = UF.splitBOP(solid, tool, opt.splitTolerance) + comsolid = UF.split_bop(solid, tool, options.splitTolerance, options) except: comsolid = solid if len(comsolid.Solids) > 1: - outSolid = comsolid.Solids + out_solid = comsolid.Solids else: - outSolid = [solid] + out_solid = [solid] - return outSolid + return out_solid -def GenPlane(pos, normal, diag): +def gen_plane(pos, normal, diag): plane = Part.makePlane(diag, diag, pos, normal) vec_on_plane = plane.Vertexes[3].Point.sub(plane.Vertexes[0].Point) new_pos = plane.Vertexes[0].Point.sub(vec_on_plane) @@ -127,7 +131,7 @@ def GenPlane(pos, normal, diag): return plane_center -def CylBoundPlanes(face, boundBox): +def cyl_bound_planes(face, boundBox): Edges = face.OuterWire.Edges planes = [] for e in Edges: @@ -141,7 +145,7 @@ def CylBoundPlanes(face, boundBox): center = e.Curve.Center dim1 = e.Curve.Radius dim2 = e.Curve.Radius - plane = UF.GEOUNED_Surface(("Plane", (center, dir, dim1, dim2)), boundBox) + plane = UF.GeounedSurface(("Plane", (center, dir, dim1, dim2)), boundBox) planes.append(plane) elif curve == "": @@ -149,16 +153,16 @@ def CylBoundPlanes(face, boundBox): center = e.Curve.Center dim1 = e.Curve.MinorRadius dim2 = e.Curve.MajorRadius - plane = UF.GEOUNED_Surface(("Plane", (center, dir, dim1, dim2)), boundBox) + plane = UF.GeounedSurface(("Plane", (center, dir, dim1, dim2)), boundBox) planes.append(plane) return planes -def TorusBoundPlanes(face, boundBox): +def torus_bound_planes(face, boundBox, tolerances): params = face.ParameterRange planes = [] - if isSameValue(params[1] - params[0], twoPi, tol.value): + if is_same_value(params[1] - params[0], twoPi, tolerances.value): return planes Edges = face.OuterWire.Edges @@ -171,13 +175,11 @@ def TorusBoundPlanes(face, boundBox): if curve[0:6] == "Circle": dir = e.Curve.Axis - if not isParallel(dir, face.Surface.Axis, tol.angle): + if not is_parallel(dir, face.Surface.Axis, tolerances.angle): center = e.Curve.Center dim1 = e.Curve.Radius dim2 = e.Curve.Radius - plane = UF.GEOUNED_Surface( - ("Plane", (center, dir, dim1, dim2)), boundBox - ) + plane = UF.GeounedSurface(("Plane", (center, dir, dim1, dim2)), boundBox) planes.append(plane) elif curve == "": @@ -185,53 +187,53 @@ def TorusBoundPlanes(face, boundBox): center = e.Curve.Center dim1 = e.Curve.MinorRadius dim2 = e.Curve.MajorRadius - plane = UF.GEOUNED_Surface(("Plane", (center, dir, dim1, dim2)), boundBox) + plane = UF.GeounedSurface(("Plane", (center, dir, dim1, dim2)), boundBox) planes.append(plane) elif curve == "": - planeParams = PlaneSplineCurve(e) + planeParams = plane_spline_curve(e, tolerances) if planeParams is not None: - plane = UF.GEOUNED_Surface(("Plane", planeParams), boundBox) + plane = UF.GeounedSurface(("Plane", planeParams), boundBox) planes.append(plane) return planes -def PlaneSplineCurve(edge): +def plane_spline_curve(edge, tolerances): normal = edge.derivative1At(0).cross(edge.derivative1At(0.5)) normal.normalize() - curve2D = True + curve_2d = True for p in (0.25, 0.75, 1): # check if derivative orthogonal to curve normal vector - if abs(normal.dot(edge.derivative1At(p))) > tol.value: - curve2D = False + if abs(normal.dot(edge.derivative1At(p))) > tolerances.value: + curve_2d = False break r = edge.valueAt(0.25) - edge.valueAt(0.75) - if curve2D: + if curve_2d: return (edge.valueAt(0), normal, r.Length, r.Length) else: return None -def ExtractSurfaces(solid, kind, UniverseBox, MakeObj=False): +def extract_surfaces(solid, kind, universe_box, options, tolerances, numeric_format, MakeObj): if kind == "All": fuzzy = True - solidParts = [] + solid_parts = [] for s in solid.Solids: - solidParts.append(GU.solid_GU(s)) + solid_parts.append(GU.SolidGu(s, tolerances=tolerances)) else: fuzzy = False if kind == "Plane3Pts": P3P = True else: P3P = False - solidParts = [GU.solid_GU(solid, P3P)] + solid_parts = [GU.SolidGu(solid, tolerances=tolerances, plane3Pts=P3P)] - Surfaces = UF.Surfaces_dict() + surfaces = UF.SurfacesDict() - for solid_GU in solidParts: + for solid_GU in solid_parts: # Get the parameter of all faces of the solid # Add auxillary planes for Cylinders and Cones @@ -242,66 +244,60 @@ def ExtractSurfaces(solid, kind, UniverseBox, MakeObj=False): pos = face.CenterOfMass dim1 = face.ParameterRange[1] - face.ParameterRange[0] dim2 = face.ParameterRange[3] - face.ParameterRange[2] - plane = UF.GEOUNED_Surface( - ("Plane", (pos, normal, dim1, dim2)), UniverseBox - ) + plane = UF.GeounedSurface(("Plane", (pos, normal, dim1, dim2)), universe_box) if MakeObj: - plane.buildSurface() - Surfaces.addPlane(plane, fuzzy) + plane.build_surface() + surfaces.add_plane(plane, options, tolerances, numeric_format, fuzzy) elif surf == "": dir = face.Surface.Axis orig = face.Surface.Center rad = face.Surface.Radius - dimL = face.ParameterRange[3] - face.ParameterRange[2] + dim_l = face.ParameterRange[3] - face.ParameterRange[2] if kind in ["Cyl", "All"]: - cylinder = UF.GEOUNED_Surface( - ("Cylinder", (orig, dir, rad, dimL)), UniverseBox - ) + cylinder = UF.GeounedSurface(("Cylinder", (orig, dir, rad, dim_l)), universe_box) if MakeObj: - cylinder.buildSurface() - Surfaces.addCylinder(cylinder, fuzzy) + cylinder.build_surface() + surfaces.add_cylinder(cylinder, options, tolerances, numeric_format, fuzzy) if kind in ["Planes", "All"]: # add planes if cylinder axis is cut by a plane (plane quasi perpedicular to axis) - for p in CylBoundPlanes(face, UniverseBox): + for p in cyl_bound_planes(face, universe_box): if MakeObj: - p.buildSurface() - Surfaces.addPlane(p) + p.build_surface() + surfaces.add_plane(p, options, tolerances, numeric_format, False) elif surf == "": dir = face.Surface.Axis apex = face.Surface.Apex - halfAngle = face.Surface.SemiAngle - dimL = face.ParameterRange[3] - face.ParameterRange[2] + half_angle = face.Surface.SemiAngle + dim_l = face.ParameterRange[3] - face.ParameterRange[2] dimR = face.Surface.Radius if kind in ["Cone", "All"]: - cone = UF.GEOUNED_Surface( - ("Cone", (apex, dir, halfAngle, dimL, dimR)), UniverseBox - ) + cone = UF.GeounedSurface(("Cone", (apex, dir, half_angle, dim_l, dimR)), universe_box) if MakeObj: - cone.buildSurface() - Surfaces.addCone(cone) + cone.build_surface() + surfaces.add_cone(cone, tolerances) if kind in ["Planes", "All"]: - for p in CylBoundPlanes(face, UniverseBox): + for p in cyl_bound_planes(face, universe_box): if MakeObj: - p.buildSurface() - Surfaces.addPlane(p) + p.build_surface() + surfaces.add_plane(p, options, tolerances, numeric_format, False) elif surf[0:6] == "Sphere" and kind in ["Sph", "All"]: rad = face.Surface.Radius pnt = face.Surface.Center - sphere = UF.GEOUNED_Surface(("Sphere", (pnt, rad)), UniverseBox) + sphere = UF.GeounedSurface(("Sphere", (pnt, rad)), universe_box) if MakeObj: - sphere.buildSurface() - Surfaces.addSphere(sphere) + sphere.build_surface() + surfaces.add_sphere(sphere, tolerances) if kind in ["Planes", "All"]: - for p in CylBoundPlanes(face, UniverseBox): + for p in cyl_bound_planes(face, universe_box): if MakeObj: - p.buildSurface() - Surfaces.addPlane(p) + p.build_surface() + surfaces.add_plane(p, options, tolerances, numeric_format, False) elif surf == "": if kind in ["Tor", "All"]: @@ -309,18 +305,16 @@ def ExtractSurfaces(solid, kind, UniverseBox, MakeObj=False): radMin = face.Surface.MinorRadius center = face.Surface.Center dir = face.Surface.Axis - torus = UF.GEOUNED_Surface( - ("Torus", (center, dir, radMaj, radMin)), UniverseBox - ) + torus = UF.GeounedSurface(("Torus", (center, dir, radMaj, radMin)), universe_box) if MakeObj: - torus.buildSurface() - Surfaces.addTorus(torus) + torus.build_surface() + surfaces.add_torus(torus, tolerances) if kind in ["Planes", "All"]: - for p in TorusBoundPlanes(face, UniverseBox): + for p in torus_bound_planes(face, universe_box, tolerances): if MakeObj: - p.buildSurface() - Surfaces.addPlane(p) + p.build_surface() + surfaces.add_plane(p, options, tolerances, numeric_format, False) elif surf == "" and kind == "Plane3Pts": pos = face.CenterOfMass @@ -329,22 +323,21 @@ def ExtractSurfaces(solid, kind, UniverseBox, MakeObj=False): dim2 = face.ParameterRange[3] - face.ParameterRange[2] points = tuple(v.Point for v in face.Vertexes) - plane = UF.GEOUNED_Surface( - ("Plane3Pts", (pos, normal, dim1, dim2, points)), UniverseBox - ) + plane = UF.GeounedSurface(("Plane3Pts", (pos, normal, dim1, dim2, points)), universe_box) if MakeObj: - plane.buildSurface() - Surfaces.addPlane(plane, fuzzy) + plane.build_surface() + surfaces.add_plane(plane, options, tolerances, numeric_format, fuzzy) - return Surfaces + return surfaces -def isAlreadyInPlanes(plane, Array): +# TODO check if this function is used as it appears to be not used elsewhere in the src folder +def is_already_in_planes(plane, Array): for elem in Array: - if plane.Surface.Axis.cross(elem.Surface.Axis) == FreeCAD.Vector( - 0, 0, 0 - ) and isInPlane(plane.Surface.Position, elem.Surface): + if plane.Surface.Axis.cross(elem.Surface.Axis) == FreeCAD.Vector(0, 0, 0) and is_in_plane( + plane.Surface.Position, elem.Surface + ): return True return False @@ -354,11 +347,11 @@ def isAlreadyInPlanes(plane, Array): # # Check if to faces are joint # -def ContiguousFace(face1, face2): - return face1.distToShape(face2)[0] < tol.distance +def contiguous_face(face1, face2, tolerances): + return face1.distToShape(face2)[0] < tolerances.distance -def SameFaces(Faces): +def same_faces(Faces, tolerances): Connection = OrderedDict() if len(Faces) == 1: return [] @@ -368,7 +361,7 @@ def SameFaces(Faces): if not Faces[i + 1 :]: continue for j, face2 in enumerate(Faces[i + 1 :]): - if ContiguousFace(face1, face2): + if contiguous_face(face1, face2, tolerances): Couples.append(i + 1 + j) @@ -388,9 +381,7 @@ def SameFaces(Faces): lista.extend(Connection[elem]) else: for elem2 in Connection[elem]: - if ( - elem2 in lista - ): # si una de sus dependencias esta en lista lo esta la clave + if elem2 in lista: # si una de sus dependencias esta en lista lo esta la clave lista.append(elem) return list(set(lista)) @@ -398,7 +389,7 @@ def SameFaces(Faces): # Tolerance in this function are not the general once # function should be reviewed -def GenPlaneCylinder(face, solid): +def gen_plane_cylinder(face, solid, tolerances): Surf = face.Surface rad = Surf.Radius if face.Area < 1e-2: @@ -422,20 +413,18 @@ def GenPlaneCylinder(face, solid): if ( face2.Surface.Axis.isEqual(face.Surface.Axis, 1e-5) and face2.Surface.Radius == rad - and isInLine( - face2.Surface.Center, face.Surface.Axis, face.Surface.Center - ) + and is_in_line(face2.Surface.Center, face.Surface.Axis, face.Surface.Center) ): face_index_0.append(i) - # prueba SameFaces, parece ok + # prueba same_faces, parece ok Faces_p = [] for ind in face_index_0: Faces_p.append(solid.Faces[ind]) face_index = [face_index_0[0]] # la face de entrada - for k in SameFaces(Faces_p): + for k in same_faces(Faces_p, tolerances): face_index.append(face_index_0[k]) # commented to let plane cut rounded corner @@ -473,16 +462,15 @@ def GenPlaneCylinder(face, solid): Uval_str_cl = [] for i, elem1 in enumerate(Uval): - num_str1 = "%11.4E" % elem1 + num_str1 = f"{elem1:11.4E}" if abs(elem1) < 1.0e-5: num_str1 = "%11.4E" % 0.0 - if not (isDuplicateInList(num_str1, i, Uval)): + if not (is_duplicate_in_list(num_str1, i, Uval)): Uval_str_cl.append(num_str1) if len(Uval_str_cl) < 2: - if opt.verbose: - print("GenPlaneCylinder : Uval_str_cl should no be void ") + logger.info("gen_plane_cylinder : Uval_str_cl should no be void") return None face_index_2 = [face_index[0], face_index[0]] @@ -513,8 +501,7 @@ def GenPlaneCylinder(face, solid): V2 = solid.Faces[face_index_2[1]].valueAt(Node_max[0], Node_max[1]) if V1.isEqual(V2, 1e-5): - if opt.verbose: - print("Error in the additional plane definition") + logger.error("in the additional plane definition") return None normal = V2.sub(V1).cross(face.Surface.Axis) @@ -526,7 +513,7 @@ def GenPlaneCylinder(face, solid): # Tolerance in this function are not the general once # function should be reviewed -def GenPlaneCone(face, solid): +def gen_plane_cone(face, solid, tolerances): if face.Area < 1e-2: return None @@ -561,7 +548,7 @@ def GenPlaneCone(face, solid): face_index = [face_index_0[0]] # la face de entrada - for k in SameFaces(Faces_p): + for k in same_faces(Faces_p, tolerances): face_index.append(face_index_0[k]) # same as cylinder commennt @@ -597,15 +584,14 @@ def GenPlaneCone(face, solid): Uval_str_cl = [] for i, elem1 in enumerate(Uval): - num_str1 = "%11.4E" % elem1 + num_str1 = f"{elem1:11.4E}" if abs(elem1) < 1.0e-5: num_str1 = "%11.4E" % 0.0 - if not (isDuplicateInList(num_str1, i, Uval)): + if not (is_duplicate_in_list(num_str1, i, Uval)): Uval_str_cl.append(num_str1) if len(Uval_str_cl) < 2: - if opt.verbose: - print("GenPlaneCone : Uval_str_cl should no be void ") + logger.info("gen_plane_cone : Uval_str_cl should no be void") return None face_index_2 = [face_index[0], face_index[0]] @@ -634,8 +620,7 @@ def GenPlaneCone(face, solid): V2 = solid.Faces[face_index_2[1]].valueAt(Node_max[0], Node_max[1]) if V1.isEqual(V2, 1e-5): - if opt.verbose: - print("Error in the additional plane definition") + logger.error("in the additional plane definition") return None # normal=V2.sub(V1).cross(face.Surface.Axis) @@ -646,7 +631,7 @@ def GenPlaneCone(face, solid): return plane -def Plane2ndOrder(solid_GU, face, flag_inv, convex=True): +def plane_2nd_order(solid_GU, face, flag_inv, tolerances, convex=True): planes = [] if face is None: @@ -663,13 +648,13 @@ def Plane2ndOrder(solid_GU, face, flag_inv, convex=True): if convex and orient == "Reversed": # convex face condition pp = None if str(face.Surface) == "": - pp = GenPlaneCylinder(face, solid_GU) + pp = gen_plane_cylinder(face, solid_GU, tolerances) elif str(face.Surface) == "": - pp = GenPlaneCone(face, solid_GU) + pp = gen_plane_cone(face, solid_GU, tolerances) if pp is not None: planes.append(pp) # elif str(face.Surface)[0:6] == 'Sphere': - # return GenPlaneSphere(face,solid_GU) + # return gen_plane_sphere(face,solid_GU) else: if flag_inv: @@ -683,23 +668,23 @@ def Plane2ndOrder(solid_GU, face, flag_inv, convex=True): if not convex and orient == "Forward": if str(face.Surface) == "": - pp = GenPlaneCylinder(face, solid_GU) + pp = gen_plane_cylinder(face, solid_GU, tolerances) elif str(face.Surface) == "": - pp = GenPlaneCone(face, solid_GU) + pp = gen_plane_cone(face, solid_GU, tolerances) if pp is not None: planes.append(pp) return planes -def SplitPlanes(Solids, UniverseBox, newVersion=True): +def split_planes(Solids, universe_box, options, tolerances, numeric_format, newVersion=True): if newVersion: - return SplitPlanes_new(Solids, UniverseBox) + return split_planes_new(Solids, universe_box, options, tolerances, numeric_format) else: - return SplitPlanes_org(Solids, UniverseBox) + return split_planes_org(Solids, universe_box, options, tolerances, numeric_format) -def SplitPlanes_new(Solids, UniverseBox): +def split_planes_new(Solids, universe_box, options, tolerances, numeric_format): Bases = Solids[:] simpleSolid = [] @@ -707,11 +692,11 @@ def SplitPlanes_new(Solids, UniverseBox): while True: newBases = [] for base in Bases: - cutSolids = splitPPlanes_new(base, UniverseBox) - if len(cutSolids) == 1: - simpleSolid.extend(cutSolids) + cut_solids = split_p_planes_new(base, universe_box, options, tolerances, numeric_format) + if len(cut_solids) == 1: + simpleSolid.extend(cut_solids) else: - newBases.extend(cutSolids) + newBases.extend(cut_solids) if len(newBases) == 0: break else: @@ -720,7 +705,7 @@ def SplitPlanes_new(Solids, UniverseBox): return simpleSolid, 0 -def SplitPlanes_org(Solids, UniverseBox): +def split_planes_org(Solids, universe_box, options, tolerances, numeric_format): Bases = [] err = 0 for sol in Solids: @@ -737,7 +722,15 @@ def SplitPlanes_org(Solids, UniverseBox): base = item[0] index = item[1] - SPlanes = ExtractSurfaces(base, "Planes", UniverseBox, MakeObj=True) + SPlanes = extract_surfaces( + base, + "Planes", + universe_box, + options, + tolerances, + numeric_format, + MakeObj=True, + ) Planes = [SPlanes["PX"], SPlanes["PY"], SPlanes["PZ"]] for i in index: del Planes[i] @@ -751,22 +744,14 @@ def SplitPlanes_org(Solids, UniverseBox): if len(Planes[imin]) != 0: Tools = [] for p in Planes[imin]: - p.buildSurface() + p.build_surface() Tools.append(p.shape) - comsolid = UF.splitBOP(base, Tools, opt.splitTolerance) + comsolid = UF.split_bop(base, Tools, options.splitTolerance, options) if len(comsolid.Solids) == 1: - if ( - abs(comsolid.Solids[0].Volume - base.Volume) / base.Volume - > tol.relativePrecision - ): - if opt.verbose: - print( - "Warning. Part of the split object is missing original base is used instead", - abs(comsolid.Solids[0].Volume - base.Volume) - / base.Volume, - comsolid.Solids[0].Volume, - base.Volume, - ) + if abs(comsolid.Solids[0].Volume - base.Volume) / base.Volume > tolerances.relativePrecision: + logger.warning( + f"Part of the split object is missing original base is used instead {abs(comsolid.Solids[0].Volume - base.Volume) / base.Volume} {comsolid.Solids[0].Volume} {base.Volume}" + ) base.exportStep("tmp_base.stp") s = Part.Shape() s.read("tmp_base.stp") @@ -798,11 +783,11 @@ def SplitPlanes_org(Solids, UniverseBox): while True: newBases = [] for base in Bases: - cutSolids = splitPPlanes_org(base, UniverseBox) - if len(cutSolids) == 1: - simpleSolid.extend(cutSolids) + cut_solids = split_p_planes_org(base, universe_box) + if len(cut_solids) == 1: + simpleSolid.extend(cut_solids) else: - newBases.extend(cutSolids) + newBases.extend(cut_solids) if len(newBases) == 0: break else: @@ -810,14 +795,14 @@ def SplitPlanes_org(Solids, UniverseBox): return simpleSolid, err -class parallelPlanes: +class ParallelPlanes: def __init__(self, plane): self.Axis = plane.Surf.Axis self.elements = [plane] self.count = 1 def append(self, plane): - if isParallel(plane.Surf.Axis, self.Axis, 0.1): + if is_parallel(plane.Surf.Axis, self.Axis, 0.1): self.elements.append(plane) self.count += 1 return True @@ -837,10 +822,10 @@ def sort(self): self.elements = sort -def sortPlanes(PlaneList, sortElements=False): +def sort_planes(PlaneList, sortElements=False): if not PlaneList: return [] - newList = [parallelPlanes(PlaneList[0])] + newList = [ParallelPlanes(PlaneList[0])] for p in PlaneList[1:]: found = False for pp in newList: @@ -848,7 +833,7 @@ def sortPlanes(PlaneList, sortElements=False): found = True break if not found: - newList.append(parallelPlanes(p)) + newList.append(ParallelPlanes(p)) lenList = [] for i, pp in enumerate(newList): @@ -864,48 +849,48 @@ def sortPlanes(PlaneList, sortElements=False): return sortedPlanes -def splitPPlanes_new(solid, UniverseBox): - SPlanes = ExtractSurfaces(solid, "Planes", UniverseBox) +def split_p_planes_new(solid, universe_box, options, tolerances, numeric_format): + SPlanes = extract_surfaces(solid, "Planes", universe_box, options, tolerances, numeric_format, False) Planes = [] for P in ("PX", "PY", "PZ", "P"): Planes.extend(SPlanes[P]) - Planes = sortPlanes(Planes) + Planes = sort_planes(Planes) if len(Planes) == 0: return [solid] - if len(Planes[-1]) < opt.nPlaneReverse: + if len(Planes[-1]) < options.nPlaneReverse: Planes.reverse() - outSolid = [solid] + out_solid = [solid] for pp in Planes: for p in pp: - p.buildSurface() + p.build_surface() tools = tuple(p.shape for p in pp) - comsolid = UF.splitBOP(solid, tools, opt.splitTolerance) + comsolid = UF.split_bop(solid, tools, options.splitTolerance, options) if len(comsolid.Solids) > 1: - outSolid = comsolid.Solids + out_solid = comsolid.Solids break - return outSolid + return out_solid -def splitPPlanes_org(solid, UniverseBox): - SPlanes = ExtractSurfaces(solid, "Planes", UniverseBox) +def split_p_planes_org(solid, universe_box, options, tolerances, numeric_format): + SPlanes = extract_surfaces(solid, "Planes", universe_box, options, tolerances, numeric_format, False) if len(SPlanes["P"]) == 0: return [solid] - outSolid = [solid] + out_solid = [solid] for p in SPlanes["P"]: - p.buildSurface() - comsolid = UF.splitBOP(solid, [p.shape], opt.splitTolerance) + p.build_surface() + comsolid = UF.split_bop(solid, [p.shape], options.splitTolerance, options) if len(comsolid.Solids) > 1: - outSolid = comsolid.Solids + out_solid = comsolid.Solids break - return outSolid + return out_solid -def Split2ndOrder(Solids, UniverseBox): +def split_2nd_order(Solids, universe_box, options, tolerances, numeric_format): err = 0 Base = Solids for kind in ["Cyl", "Cone", "Sph", "Tor"]: @@ -913,15 +898,23 @@ def Split2ndOrder(Solids, UniverseBox): while True: cutBase = [] for solid in Base: - Surfaces = ExtractSurfaces(solid, kind, UniverseBox) + Surfaces = extract_surfaces( + solid, + kind, + universe_box, + options, + tolerances, + numeric_format, + False, + ) if len(Surfaces[kind]) == 0: kindBase.append(solid) else: simple = True for s in Surfaces[kind]: - s.buildSurface() + s.build_surface() try: - comsolid = UF.splitBOP(solid, [s.shape], opt.splitTolerance) + comsolid = UF.split_bop(solid, [s.shape], options.splitTolerance, options) solidsInCom = [] for s in comsolid.Solids: if s.Volume > 1e-9: @@ -932,12 +925,7 @@ def Split2ndOrder(Solids, UniverseBox): cutBase.extend(solidsInCom) break except: - if opt.verbose: - print( - "Failed split base with {} surface".format( - s.shape.Faces[0].Surface - ) - ) + logger.info("Failed split base with {s.shape.Faces[0].Surface} surface") err += 2 if simple: @@ -952,18 +940,18 @@ def Split2ndOrder(Solids, UniverseBox): return Base, err -def Split2ndOrderPlanes(Solids): +def split_2nd_order_planes(Solids, options, tolerances): err = 0 simpleSolid = [] Bases = Solids while True: newBases = [] for base in Bases: - cutSolids, err = split2ndOPlane(base) - if len(cutSolids) == 1: - simpleSolid.extend(cutSolids) + cut_solids, err = split_2nd_o_plane(base, options, tolerances) + if len(cut_solids) == 1: + simpleSolid.extend(cut_solids) else: - newBases.extend(cutSolids) + newBases.extend(cut_solids) if len(newBases) == 0: break else: @@ -972,22 +960,22 @@ def Split2ndOrderPlanes(Solids): return simpleSolid, err -def split2ndOPlane(solid): +def split_2nd_o_plane(solid, options, tolerances): err = 0 - flag_inv = CD.isInverted(solid) - solid_GU = GU.solid_GU(solid) - planes = Plane2ndOrder(solid_GU, None, flag_inv, convex=True) + flag_inv = CD.is_inverted(solid) + solid_GU = GU.SolidGu(solid, tolerances=tolerances) + planes = plane_2nd_order(solid_GU, None, flag_inv, tolerances, convex=True) if not planes: return [solid], err for p in planes: - comsolid = UF.splitBOP(solid, [p], opt.splitTolerance) + comsolid = UF.split_bop(solid, [p], options.splitTolerance, options) if not comsolid.Solids: comsolid = solid continue if len(comsolid.Solids) > 1: - removed, err = removeSolids(comsolid.Solids) + removed, err = remove_solids(comsolid.Solids) comsolid = Part.makeCompound(removed) if len(removed) > 1: break @@ -995,17 +983,13 @@ def split2ndOPlane(solid): return comsolid.Solids, err -def removeSolids(Solids): +def remove_solids(Solids): err = 0 Vol_tol = 1e-2 Solids_Clean = [] for solid in Solids: if solid.Volume <= Vol_tol: - if opt.verbose: - print( - "Warning: removeSolids degenerated solids are produced", - solid.Volume, - ) + logger.warning(f"remove_solids degenerated solids are produced {solid.Volume}") err = 2 continue Solids_Clean.append(solid) @@ -1013,7 +997,7 @@ def removeSolids(Solids): return Solids_Clean, err -def splitComponent(solidShape, UniverseBox): +def split_component(solidShape, universe_box, options, tolerances, numeric_format): err = 0 err2 = 0 @@ -1021,22 +1005,20 @@ def splitComponent(solidShape, UniverseBox): Solids = solidShape.Solids # Split with explicit planes bounding the solid and # implicit planes interface of two 2nd order surfaces - split0, err = SplitPlanes(Solids, UniverseBox, opt.newSplitPlane) + split0, err = split_planes(Solids, universe_box, options, tolerances, numeric_format) # Split with explicit 2nd order surfaces bounding the solid - split1, err1 = Split2ndOrder(split0, UniverseBox) + split1, err1 = split_2nd_order(split0, universe_box, options, tolerances, numeric_format) err += err1 - split, err2 = Split2ndOrderPlanes(split1) + split, err2 = split_2nd_order_planes(split1, options, tolerances) err += err2 Pieces = [] for part in split: if part.Volume <= 1e-10: - if opt.verbose: - print( - "Warning: splitComponent degenerated solids are produced", - part.Volume, - ) + logger.warning( + f"split_component degenerated solids are produced {part.Volume}", + ) err += 2 continue Pieces.append(part) @@ -1047,21 +1029,21 @@ def splitComponent(solidShape, UniverseBox): Volch = (Volini - Volend) / Volini if abs(Volch) > 1e-2: # 1% of Volume change - if opt.verbose: - print("Warning: Volume has changed after decomposition: %11.4E" % Volch) + logger.warning(f"Volume has changed after decomposition: {Volch:11.4E}") err += 4 return comsolid, err -def SplitSolid(solidShape, UniverseBox): +# TODO rename this function as there are two with the name name +def SplitSolid(solidShape, universe_box, options, tolerances, numeric_format): - solidParts = [] + solid_parts = [] for solid in solidShape.Solids: - explode = splitFullCylinder(solid) - piece, err = splitComponent(explode, UniverseBox) - solidParts.append(piece) + explode = split_full_cylinder(solid, options, tolerances, numeric_format) + piece, err = split_component(explode, universe_box, options, tolerances, numeric_format) + solid_parts.append(piece) - return Part.makeCompound(solidParts), err + return Part.makeCompound(solid_parts), err diff --git a/src/geouned/GEOUNED/Utils/__init__.py b/src/geouned/GEOUNED/loadfile/__init__.py similarity index 100% rename from src/geouned/GEOUNED/Utils/__init__.py rename to src/geouned/GEOUNED/loadfile/__init__.py diff --git a/src/geouned/GEOUNED/loadfile/load_functions.py b/src/geouned/GEOUNED/loadfile/load_functions.py new file mode 100644 index 00000000..bcaf669f --- /dev/null +++ b/src/geouned/GEOUNED/loadfile/load_functions.py @@ -0,0 +1,374 @@ +import logging +import re + +import FreeCAD + +from ..utils.functions import GeounedSolid + +logger = logging.getLogger("general_logger") + + +def get_label(label, options): + """Deleting the last word of the string if this is a number + Only if the option delLastNumber is True""" + if not options.delLastNumber: + return label + wrd = label.split() + try: + new_label = " ".join(wrd[:-1]) + return new_label + except: + return label + + +def getCommentTree(obj, options): + recursive_list = [] + c_obj = obj + while c_obj.InList: + label = get_label(c_obj.InList[0].Label, options) + recursive_list.append(label) + c_obj = c_obj.InList[0] + + comment = "" + for label in reversed(recursive_list): + comment += "/" + label + return comment + + +def joinEnvelopes(meta_list): + join_list = [] + old_id = -1 + new_list = [] + for i, m in enumerate(meta_list): + if m.CellType != "envelope": + continue + if m.EnclosureID != old_id: + join_list.append(new_list) + new_list = [i] + old_id = m.EnclosureID + else: + new_list.append(i) + + join_list.append(new_list) + del join_list[0] + + for id_list in join_list: + fuse_meta_obj(meta_list, id_list[0], id_list[-1] + 1) + + +def fuse_meta_obj(meta_list, init, end): + if end - init == 1: + meta_list[init].__id__ = init + 1 + return + solids = [] + for m in meta_list[init:end]: + solids.extend(m.Solids) + + new_meta = GeounedSolid(init + 1, solids) + new_meta.EnclosureID = meta_list[init].EnclosureID + new_meta.ParentEnclosureID = meta_list[init].ParentEnclosureID + new_meta.IsEnclosure = meta_list[init].IsEnclosure + new_meta.CellType = meta_list[init].CellType + + del meta_list[init:end] + meta_list.insert(init, new_meta) + + +# Paco mod +# TODO check if this function is actually used in to code +def check_cad_file_material(material, mdict): + """Function to check that if a component in the CAD file has a material, it exist in the material file""" + templist = [elem for elem in set(material) if elem not in mdict] + if templist: + raise ValueError( + "At least one material in the CAD model is not present in the material file\n" + f"List of not present materials:{templist}\n" + "Code STOPS\n" + ) + return + + +# Paco mod +def set_enclosure_solid_list(meta_list): + """Function to define DataList list.""" + return [elem for elem in meta_list if elem.IsEnclosure] + + +def remove_enclosure(meta_list): + """This function removes Enclosure solid and actualises __id__ attributes to take into account the removal from meta_list list of nested enclosures originally loaded from CAD model.""" + + # update id of solids + len_meta_list = len(meta_list) + i_count = 0 + for i, m in enumerate(meta_list): + if m.IsEnclosure: + i_count += 1 + else: + meta_list[i].__id__ -= i_count + + # remove Enclosure solids from metaList + for i in range(len_meta_list - 1, -1, -1): + if meta_list[i].IsEnclosure: + meta_list.pop(i) + + +def set_enclosure_levels(enclosure_list): + nested_list = [[0], []] + nested_enclosure = [[]] + parent_level = 0 + temp_list = enclosure_list[:] + + while len(temp_list) > 0: + remove = [] + for i, encl in enumerate(temp_list): + if encl.ParentEnclosureID in nested_list[parent_level]: + nested_list[parent_level + 1].append(encl.EnclosureID) + nested_enclosure[parent_level].append(encl) + remove.append(i) + + nested_list.append([]) + nested_enclosure.append([]) + parent_level += 1 + remove.reverse() + for i in remove: + del temp_list[i] + + nested_enclosure_list = [] + for Level in nested_enclosure: + temp = [] + for i, encl in enumerate(Level): + temp.append((encl.ParentEnclosureID, i)) + temp.sort() + + grouped = [] + for t in temp: + grouped.append(Level[t[1]]) + nested_enclosure_list.append(grouped) + + for i, Level in enumerate(nested_enclosure_list[0:-1]): + for encl in Level: + child_list = [] + for child in nested_enclosure_list[i + 1]: + if child.ParentEnclosureID == encl.EnclosureID: + child_list.append(child) + encl.SonEnclosures = child_list[:] + + return nested_enclosure_list + + +def check_enclosure(freecad_doc, enclosure_list): + + stop = False + # check all enclosure labels have an associated solid + temp_list = [] + for elem in freecad_doc.Objects: + if elem.TypeId == "Part::Feature": + if elem.Shape.Solids: + if elem.InList: + templabel = re.search( + "enclosure(?P[0-9]+)_(?P[0-9]+)_", + elem.InList[0].Label, + ) + if templabel is not None: + if elem.TypeId == "Part::Feature" and len(elem.Shape.Solids) == 0: + temp_list.append(elem) + + if temp_list: + stop = True + logger.info("One or more nested enclosure labels in CAD solid tree view/structure tree do not have any CAD solid.") + logger.info("Each nested enclosure must have only one solid. Code STOPS.") + logger.info("List of problematic nested enclosure labels:") + + for elem in temp_list: + logger.info(elem.EnclosureID) + + # check enclosure Labels don't make loops + + # 1) check at leat one 0 is in Parent enclosure label + # 2) check 0 is not in child enclosure label + # 3) check child enclosure label is not repeated + + sid_list = [] + pid_set = set() + repeated_id = set() + + for encl in enclosure_list: + pid_set.add(encl.ParentEnclosureID) + if encl.EnclosureID in sid_list: + repeated_id.add(encl.EnclosureID) + else: + sid_list.append(encl.EnclosureID) + + if 0 in sid_list: + stop = True + logger.info('"0" cannot be label on child Enclosure') + if 0 not in pid_set: + stop = True + logger.info('"0" should parent label of most external enclosure(s)') + if repeated_id: + stop = True + logger.info("Child label cannot be repeated.\nRepeated labels :") + for lab in repeated_id: + logger.info(lab) + + # this stop should not be move to the end of routine + # if previous point not satisfied point 4) may lead to infinite loop + if stop: + raise ValueError("Exiting to avoid infinite loop") + + # 4) look for explicit loops + encl_dict = dict() + for encl in enclosure_list: + if encl.ParentEnclosureID in encl_dict.keys(): + encl_dict[encl.ParentEnclosureID].append(encl.EnclosureID) + else: + encl_dict[encl.ParentEnclosureID] = [encl.EnclosureID] + + parent = [0] + cont = True + while cont: + cont = False + next_parent = [] + for p in parent: + if p in encl_dict.keys(): + cont = True + next_parent.extend(encl_dict[p]) + del encl_dict[p] + parent = next_parent + + if encl_dict.keys(): + logger.info("Following enclosure produce loop") + for p in encl_dict.keys(): + for s in encl_dict[p]: + logger.info(f"{s}_{p}") + raise ValueError("GEOUNED.LoadFunctions.check_enclosure failed") + + # check enclosures solids are correctly nested and don't overlap each other + nested_levels = set_enclosure_levels(enclosure_list) + + overlap = [] + encl_tree = [[0]] + for level in nested_levels: + encl_tree = update_tree(encl_tree, level) + for encl in level: + same_parent = dict() + if encl.ParentEnclosureID in same_parent.keys(): + same_parent[encl.ParentEnclosureID].append(encl.EnclosureID) + else: + same_parent[encl.ParentEnclosureID] = [encl.EnclosureID] + for encl in same_parent.values(): + overlap.extend(check_overlap(encl)) + + not_embedded = [] + for chain in encl_tree: + up = chain[0] + for low in chain[1:]: + inter = up.check_intersection(low.CADSolid) + if inter != -2: + not_embedded.append((low.EnclosureID, up.EnclosureID)) + + if not_embedded: + stop = True + logger.info("Following enclosures are not fully embedded in Parent enclosure") + for elemt in not_embedded: + logger.info(f"{elemt[0]}_{elemt[1]}") + + if overlap: + stop = True + logger.info("Following enclosures overlapping ") + for elemt in overlap: + logger.info(f"{elemt[0]}_{elemt[1]}") + + if stop: + raise ValueError("GEOUNED.LoadFunctions.check_enclosure failed") + + +def check_overlap(enclosures): + overlap = [] + for i, enc1 in enumerate(enclosures): + for enc2 in enclosures[i + 1 :]: + inter = enc1.check_intersection(enc2.CADSolid) + if inter != 1: + overlap.append((enc1.EnclosureID, enc2.EnclosureID)) + return overlap + + +def update_tree(Tree, level): + new_tree = [] + for encl in level: + for lst in Tree: + if lst[-1] == encl.ParentEnclosureID: + new_tree.append(lst + [encl.EnclosureID]) + continue + return new_tree + + +def set_doc_options(): + # set import step document options for FreeCAD version >0.18 compatible with FreeCAD0.18 opening options + p0 = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Import") + p0.SetBool("UseLinkGroup", False) + p0.SetBool("ReduceObjects", False) + p0.SetBool("ExpandCompound", False) + + p1 = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Import/hSTEP") + p1.SetBool("UseLinkGroup", False) + p1.SetBool("ReadShapeCompoundMode", True) + + +def check_index(docList, index, returnObject=False): + subList = docList.ElementList + for i in index[:-1]: + if len(subList) > i: + subList = subList[i] + if subList.TypeId != "App::LinkGroup": + return None + subList = subList.ElementList + else: + return None + + if len(subList) > index[-1]: + if subList[index[-1]].TypeId == "App::LinkGroup": + return "Link" + else: + if returnObject: + return subList[index[-1]] + else: + return "Feature" + else: + return None + + +def next_index(docList, lastIndex=None): + if lastIndex is None: + lastIndex = [0] + while check_index(docList, lastIndex) != "Feature": + lastIndex.append(0) + else: + return lastIndex + + tmp = lastIndex[:] + move = True + while True: + if move: + tmp[-1] = tmp[-1] + 1 + obj = check_index(docList, tmp) + if obj == "Feature": + return tmp + elif obj == "Link": + tmp.append(0) + move = False + else: + tmp = tmp[0:-1] + move = True + if len(tmp) == 0: + return None + + +# TODO check this function is used in the code +def solid_generator(doclist): + last = None + while True: + last = next_index(doclist, last) + if last is None: + break + yield check_index(doclist, last, True) diff --git a/src/geouned/GEOUNED/loadfile/load_step.py b/src/geouned/GEOUNED/loadfile/load_step.py new file mode 100644 index 00000000..cc136c02 --- /dev/null +++ b/src/geouned/GEOUNED/loadfile/load_step.py @@ -0,0 +1,177 @@ +# +# Module to load a STEP file +# +import logging +import os +import re + +import FreeCAD +import Part +from FreeCAD import Import + +from ..utils import functions as UF +from . import load_functions as LF + +logger = logging.getLogger("general_logger") + + +# Paco mod +def extract_materials(filename): + rho_real = [] + m_dict = {} # _ Material dictionary + with open(filename, "rt") as file: + for line in file: + vals = line.split() + if vals[0].startswith("#"): + continue + mat_label = int(vals[0]) + rho_real = -float(vals[1]) + matname = " ".join(vals[2:]) + m_dict[mat_label] = (rho_real, matname) + return m_dict + + +def load_cad(filename, settings, options): + + # Set document solid tree options when opening CAD differing from version 0.18 + if int(FreeCAD.Version()[1]) > 18: + LF.set_doc_options() + + cad_simplificado_doc = FreeCAD.newDocument("CAD_simplificado") + Import.insert(filename, "CAD_simplificado") + + if settings.matFile != "": + if os.path.exists(settings.matFile): + m_dict = extract_materials(settings.matFile) + else: + logger.info(f"Material definition file {settings.matFile} does not exist.") + m_dict = {} + else: + m_dict = {} + + s = Part.Shape() + s.read(filename) + Solids = s.Solids + meta_list = [] + for i, s in enumerate(Solids): + meta_list.append(UF.GeounedSolid(i + 1, s)) + + i_solid = 0 + missing_mat = set() + + doc_objects = cad_simplificado_doc.Objects + + for elem in doc_objects: + if elem.TypeId == "Part::Feature": + comment = LF.getCommentTree(elem, options) + if not elem.Shape.Solids: + logger.warning("Element {:} has no associated solid".format(comment + "/" + elem.Label)) + continue + else: + tempre_mat = None + tempre_dil = None + + # MIO: lightly modification of label if required + label = LF.get_label(elem.Label, options) + comment = comment + "/" + label + if elem.InList: + # MIO: lightly modification of label if required + label_in_list = LF.get_label(elem.InList[0].Label, options) + encl_label = re.search("enclosure(?P[0-9]+)_(?P[0-9]+)_", label_in_list) + if not encl_label: + encl_label = re.search("enclosure(?P[0-9]+)_(?P[0-9]+)_", label) + + envel_label = re.search("envelope(?P[0-9]+)_(?P[0-9]+)_", label_in_list) + if not envel_label: + envel_label = re.search("envelope(?P[0-9]+)_(?P[0-9]+)_", label) + + # tempre_mat = re.search("(m(?P\d+)_)",elem.Label) + # if not tempre_mat : + # tempre_mat = re.search("(m(?P\d+)_)",elem.InList[0].Label) + + # tempre_dil = re.search("(_d(?P\d*\.\d*)_)",elem.Label) + # if not tempre_dil : + # tempre_dil = re.search("(_d(?P\d*\.\d*)_)",elem.InList[0].Label) + + # Paco modifications + # Search for material definition in tree + xelem = [elem] + while xelem and not tempre_mat: + # MIO: Modification of label if required + temp_label = LF.get_label(xelem[0].Label, options) + tempre_mat = re.search("_m(?P\d+)_", "_" + temp_label) + xelem = xelem[0].InList + + # Search for dilution definition in tree + xelem = [elem] + while xelem and not tempre_dil: + # MIO: Modification of label if required + temp_label = LF.get_label(xelem[0].Label, options) + tempre_dil = re.search("_d(?P\d*\.\d*)_", temp_label) + xelem = xelem[0].InList + # Paco end + else: + encl_label = None + envel_label = None + + # compSolid Diferent solid of the same cell are stored in the same metaObject (compSolid) + # enclosures and envelopes are always stored as compound + if settings.compSolids or encl_label or envel_label: + + init = i_solid + end = i_solid + len(elem.Shape.Solids) + LF.fuse_meta_obj(meta_list, init, end) + n_solids = 1 + else: + n_solids = len(elem.Shape.Solids) + + for i in range(n_solids): + meta_list[i_solid].set_comments(f"{comment}{i + 1}") + meta_list[i_solid].set_cad_solid() + + if tempre_mat: + mat_label = int(tempre_mat.group("mat")) + if mat_label in m_dict.keys(): + meta_list[i_solid].set_material(mat_label, m_dict[mat_label][0], m_dict[mat_label][1]) + else: + if mat_label == 0: + meta_list[i_solid].set_material(mat_label, 0, 0) + else: + meta_list[i_solid].set_material( + mat_label, + -100, + "Missing material density information", + ) + missing_mat.add(mat_label) + else: + # logger.warning('No material label associated to solid {}.\nDefault material used instead.'.format(comment)) + if settings.voidMat: + meta_list[i_solid].set_material(*settings.voidMat) + if tempre_dil: + meta_list[i_solid].set_dilution(float(tempre_dil.group("dil"))) + + if encl_label is not None: + meta_list[i_solid].EnclosureID = int(encl_label.group("encl")) + meta_list[i_solid].ParentEnclosureID = int(encl_label.group("parent")) + meta_list[i_solid].IsEnclosure = True + meta_list[i_solid].CellType = "void" + + if envel_label is not None: + meta_list[i_solid].EnclosureID = int(envel_label.group("env")) + meta_list[i_solid].ParentEnclosureID = int(envel_label.group("parent")) + meta_list[i_solid].IsEnclosure = True + meta_list[i_solid].CellType = "envelope" + i_solid += 1 + + LF.joinEnvelopes(meta_list) + if missing_mat: + logger.warning("At least one material in the CAD model is not present in the material file") + logger.info(f"List of not present materials: {missing_mat}") + + enclosure_list = LF.set_enclosure_solid_list(meta_list) + if enclosure_list: + LF.check_enclosure(cad_simplificado_doc, enclosure_list) + # LF.remove_enclosure(meta_list) + return meta_list, enclosure_list + else: + return meta_list, [] diff --git a/src/geouned/GEOUNED/Void/__init__.py b/src/geouned/GEOUNED/scripts/__init__.py similarity index 100% rename from src/geouned/GEOUNED/Void/__init__.py rename to src/geouned/GEOUNED/scripts/__init__.py diff --git a/src/geouned/GEOUNED/scripts/geouned_cadtocsg.py b/src/geouned/GEOUNED/scripts/geouned_cadtocsg.py new file mode 100644 index 00000000..5e761799 --- /dev/null +++ b/src/geouned/GEOUNED/scripts/geouned_cadtocsg.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 + +""" +Convert Cad geometry to CSG format for use in Monte Carlo Codes +""" + +import argparse + +import geouned + +parser = argparse.ArgumentParser(description=__doc__) +parser.add_argument( + "-i", + "--input", + type=str, + default="config.json", + help="The path to the config JSON file", +) +args = parser.parse_args() + + +def main(): + geouned.CadToCsg.from_json(args.input) + + +if __name__ == "__main__": + main() diff --git a/src/geouned/GEOUNED/Write/__init__.py b/src/geouned/GEOUNED/utils/__init__.py similarity index 100% rename from src/geouned/GEOUNED/Write/__init__.py rename to src/geouned/GEOUNED/utils/__init__.py diff --git a/src/geouned/GEOUNED/utils/basic_functions_part1.py b/src/geouned/GEOUNED/utils/basic_functions_part1.py new file mode 100644 index 00000000..a29e0c6e --- /dev/null +++ b/src/geouned/GEOUNED/utils/basic_functions_part1.py @@ -0,0 +1,198 @@ +# +# Set of useful functions used in different parts of the code +# +import math + +import FreeCAD + + +def is_same_value(v1, v2, tolerance=1e-6): + return abs(v1 - v2) < tolerance + + +def is_opposite(vector_1, vector_2, tolerance=1e-6): + return abs(vector_1.getAngle(-vector_2)) < tolerance + + +def is_parallel(vector_1, vector_2, tolerance=1e-6): + angle = abs(vector_1.getAngle(vector_2)) + return angle < tolerance or is_same_value(angle, math.pi, tolerance) + + +def is_in_line(point, dir, pnt_line, tolerance=1e-6): + r12 = point - pnt_line + return is_parallel(dir, r12) or (r12.Length < tolerance) + + +# TODO check this function is used in the code +def is_in_points(point, points, tolerance=1e-5): + if len(points) > 0: + for p in points: + if point.isEqual(p, tolerance): + return True + return False + + +# TODO check this function is used in the code +def is_in_edge(edge1, edge2, tolerance=1e-8): + ver1 = edge1.Vertexes + ver2 = edge2.Vertexes + con1 = ver1[0].Point.isEqual(ver2[0].Point, tolerance) or ver1[0].Point.isEqual(ver2[1].Point, tolerance) + con2 = ver1[1].Point.isEqual(ver2[0].Point, tolerance) or ver1[1].Point.isEqual(ver2[1].Point, tolerance) + return con1 and con2 + + +def is_in_plane(point, plane, d_tolerance=1e-7): + return abs(point.distanceToPlane(plane.Surf.Position, plane.Surf.Axis)) < d_tolerance + + +def is_in_tolerance(val, tol, fuzzy_low, fuzzy_high): + if abs(val) < fuzzy_low: + return True, False # 1) isintolerance 2) fuzzy + elif abs(val) < tol: + return True, True + elif abs(val) > fuzzy_high: + return False, False + else: + return False, True + + +def sign_plane(point, plane): + value = plane.Surf.Axis.dot(point - plane.Surf.Position) + if value >= 0.0: + sign = 1 + else: + sign = -1 + return sign + + +def points_to_coeffs(points): + p1, p2, p3 = points[0:3] + scf = (p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, p3.x, p3.y, p3.z) + + # mcnp implementation to convert 3 point plane to + # plane parameters + + tpp = [0] * 4 + for i in range(1, 4): + j = i % 3 + 1 + k = 6 - i - j + k -= 1 + j -= 1 + tpp[i - 1] = ( + scf[j] * (scf[k + 3] - scf[k + 6]) + scf[j + 3] * (scf[k + 6] - scf[k]) + scf[j + 6] * (scf[k] - scf[k + 3]) + ) + tpp[3] += scf[i - 1] * (scf[j + 3] * scf[k + 6] - scf[j + 6] * scf[k + 3]) + + xm = 0 + coeff = [0] * 4 + for i in range(1, 5): + if xm == 0 and tpp[4 - i] != 0: + xm = 1 / tpp[4 - i] + coeff[4 - i] = tpp[4 - i] * xm + + axis = FreeCAD.Vector(coeff[0:3]) + distance = coeff[3] / axis.Length + axis.normalize() + + return axis, distance + + +class Plane3PtsParams: + def __init__(self, params, real=True): + self.Position = params[0] + self.Axis = params[1] + self.dimL1 = params[2] + self.dimL2 = params[3] + self.Points = params[4] + self.real = real + self.pointDef = True + + def __str__(self): + # outstr = '''Plane : + # Point 1 : {P1[0]} {P1[1]} {P1[2]} + # Point 2 : {P2[0]} {P2[1]} {P2[2]} + # Point 3 : {P3[0]} {P3[1]} {P3[2]} '''.format(P1=self.Points[0], P2=self.Points[1], P3=self.Points[2]) + pos = self.Axis.dot(self.Position) + outstr = f"""Plane : + Axis : {self.Axis.x} {self.Axis.y} {self.Axis.z} + Position : {pos} """ + return outstr + + +class PlaneParams: + def __init__(self, params, real=True): + self.Position = params[0] + self.Axis = params[1] + self.dimL1 = params[2] + self.dimL2 = params[3] + self.real = real + self.pointDef = False + + def __str__(self): + pos = self.Axis.dot(self.Position) + outstr = f"""Plane : + Axis : {self.Axis.x} {self.Axis.y} {self.Axis.z} + Position : {pos} """ + return outstr + + +class CylinderParams: + def __init__(self, params, real=True): + self.Center = params[0] + self.Axis = params[1] + self.Radius = params[2] + self.dimL = params[3] + self.real = real + + def __str__(self): + outstr = f"""Cylinder : + Axis : {self.Axis.x} {self.Axis.y} {self.Axis.z} + Center : {self.Center.x} {self.Center.y} {self.Center.z} + Radius : {self.Radius} """ + return outstr + + +class ConeParams: + def __init__(self, params, real=True): + self.Apex = params[0] + self.Axis = params[1] + self.SemiAngle = params[2] + self.dimL = params[3] + self.dimR = params[4] + self.real = real + + def __str__(self): + outstr = f"""Cone : + Axis : {self.Axis.x} {self.Axis.y} {self.Axis.z} + Center : {self.Apex.x} {self.Apex.y} {self.Apex.z} + SemiAngle: {self.SemiAngle} """ + return outstr + + +class SphereParams: + def __init__(self, params): + self.Center = params[0] + self.Radius = params[1] + + def __str__(self): + outstr = f"""Sphere : + Center : {self.Center.x} {self.Center.y} {self.Center.z} + Radius : {self.Radius} """ + return outstr + + +class TorusParams: + def __init__(self, params): + self.Center = params[0] + self.Axis = params[1] + self.MajorRadius = params[2] + self.MinorRadius = params[3] + + def __str__(self): + outstr = f"""Torus : + Axis : {self.Axis.x} {self.Axis.y} {self.Axis.z} + Center : {self.Center.x} {self.Center.y} {self.Center.z} + MajorRadius: {self.MajorRadius} + MinorRadius: {self.MinorRadius} """ + return outstr diff --git a/src/geouned/GEOUNED/utils/basic_functions_part2.py b/src/geouned/GEOUNED/utils/basic_functions_part2.py new file mode 100644 index 00000000..f257ac17 --- /dev/null +++ b/src/geouned/GEOUNED/utils/basic_functions_part2.py @@ -0,0 +1,333 @@ +# +# Set of useful functions used in different parts of the code +# +import logging +import math + +import FreeCAD + +from .basic_functions_part1 import ( + is_in_line, + is_in_plane, + is_in_tolerance, + is_opposite, + is_parallel, + is_same_value, +) +from ..write.functions import mcnp_surface + + +def Fuzzy(index, dtype, surf1, surf2, val, tol, options, tolerances, numeric_format): + + fuzzy_logger = logging.getLogger("fuzzy_logger") + + same = val <= tol + + if dtype == "plane": + p1str = mcnp_surface(index, "Plane", surf1, options, tolerances, numeric_format) + p2str = mcnp_surface(0, "Plane", surf2, options, tolerances, numeric_format) + fuzzy_logger.info(f"Same surface : {same}") + fuzzy_logger.info(f"Plane distance / Tolerance : {val} {tol}\n {p1str}\n {p2str}\n\n") + + elif dtype == "cylRad": + cyl1str = mcnp_surface(index, "Cylinder", surf1, options, tolerances, numeric_format) + cyl2str = mcnp_surface(0, "Cylinder", surf2, options, tolerances, numeric_format) + fuzzy_logger.info(f"Same surface : {same}") + fuzzy_logger.info(f"Diff Radius / Tolerance: {val} {tol}") + fuzzy_logger.info(f"{cyl1str}\n {cyl2str}\n\n") + + elif dtype == "cylAxs": + cyl1str = mcnp_surface(index, "Cylinder", surf1, options, tolerances, numeric_format) + cyl2str = mcnp_surface(0, "Cylinder", surf2, options, tolerances, numeric_format) + fuzzy_logger.info(f"Same surface : {same}") + fuzzy_logger.info(f"Dist Axis / Tolerance: {val} {tol}") + fuzzy_logger.info(f"{cyl1str} {cyl2str}") + + +def is_same_plane(p1, p2, options, tolerances, numeric_format, fuzzy=(False, 0)): + if is_parallel(p1.Axis, p2.Axis, tolerances.pln_angle): + d1 = p1.Axis.dot(p1.Position) + d2 = p2.Axis.dot(p2.Position) + if is_opposite(p1.Axis, p2.Axis, tolerances.pln_angle): + d2 = -d2 + d = abs(d1 - d2) + if tolerances.relativeTol: + tol = tolerances.pln_distance * max(p2.dimL1, p2.dimL2) + else: + tol = tolerances.pln_distance + + isSame, is_fuzzy = is_in_tolerance(d, tol, 0.5 * tol, 2 * tol) + if is_fuzzy and fuzzy[0]: + Fuzzy(fuzzy[1], "plane", p2, p1, d, tol, options, tolerances, numeric_format) + return isSame + return False + + +def is_same_cylinder( + cyl1, + cyl2, + options, + tolerances, + numeric_format, + fuzzy=(False, 0), +): + if tolerances.relativeTol: + rtol = tolerances.cyl_distance * max(cyl2.Radius, cyl1.Radius) + else: + rtol = tolerances.cyl_distance + + is_same_rad, is_fuzzy = is_in_tolerance(cyl2.Radius - cyl1.Radius, rtol, 0.5 * rtol, 2 * rtol) + if is_fuzzy and fuzzy[0]: + Fuzzy( + fuzzy[1], + "cylRad", + cyl2, + cyl1, + abs(cyl2.Radius - cyl1.Radius), + rtol, + options, + tolerances, + numeric_format, + ) + + if is_same_rad: + if is_parallel(cyl1.Axis, cyl2.Axis, tolerances.cyl_angle): + c12 = cyl1.Center - cyl2.Center + d = cyl1.Axis.cross(c12).Length + + if tolerances.relativeTol: + tol = tolerances.cyl_distance * max(cyl1.Center.Length, cyl2.Center.Length) + else: + tol = tolerances.cyl_distance + + is_same_center, is_fuzzy = is_in_tolerance(d, tol, 0.5 * tol, 2 * tol) + if is_fuzzy and fuzzy[0]: + Fuzzy( + fuzzy[1], + "cylAxs", + cyl1, + cyl2, + d, + tol, + options, + tolerances, + numeric_format, + ) + + return is_same_center + return False + + +def is_same_cone(cone1, cone2, dtol=1e-6, atol=1e-6, rel_tol=True): + if is_same_value(cone1.SemiAngle, cone2.SemiAngle, atol): + if is_parallel(cone1.Axis, cone2.Axis, atol): + if rel_tol: + tol = dtol * max(cone1.Apex.Length, cone2.Apex.Length) + else: + tol = dtol + return cone1.Apex.isEqual(cone2.Apex, tol) + return False + + +def is_same_sphere(sph1, sph2, tolerance=1e-6, rel_tol=True): + if rel_tol: + rtol = tolerance * max(sph2.Radius, sph1.Radius) + else: + rtol = tolerance + if is_same_value(sph1.Radius, sph2.Radius, rtol): + if rel_tol: + ctol = tolerance * max(sph2.Center.Length, sph1.Center.Length) + else: + ctol = tolerance + return sph1.Center.isEqual(sph2.Center, ctol) + + return False + + +def is_same_torus(tor1, tor2, dtol=1e-6, atol=1e-6, rel_tol=True): + if is_parallel(tor1.Axis, tor2.Axis, atol): + if tor1.Axis.dot(tor2.Axis) < 0: + return False # Assume same cone with oposite axis as different + if rel_tol: + Rtol = dtol * max(tor1.MajorRadius, tor2.MajorRadius) + rtol = dtol * max(tor1.MinorRadius, tor2.MinorRadius) + else: + Rtol = dtol + rtol = dtol + + if is_same_value(tor1.MajorRadius, tor2.MajorRadius, Rtol) and is_same_value(tor1.MinorRadius, tor2.MinorRadius, rtol): + if rel_tol: + ctol = dtol * max(tor1.Center.Length, tor2.Center.Length) + else: + ctol = dtol + return tor1.Center.isEqual(tor2.Center, ctol) + return False + + +def is_duplicate_in_list(num_str1, i, lista): + for j, elem2 in enumerate(lista): + if i == j: + continue + num_str2 = f"{elem2:11.4E}" + num_str3 = f"{elem2 + 2.0 * math.pi:11.4E}" + num_str4 = f"{elem2 - 2.0 * math.pi:11.4E}" + + if abs(float(num_str2)) < 1.0e-5: + num_str2 = "%11.4E" % 0.0 + + if abs(float(num_str3)) < 1.0e-5: + num_str3 = "%11.4E" % 0.0 + + if abs(float(num_str4)) < 1.0e-5: + num_str4 = "%11.4E" % 0.0 + + if num_str1 == num_str2 or num_str1 == num_str3 or num_str1 == num_str4: + return True + + return False + + +# TODO check if this function is used +def is_in_faces(face, faces): + + if faces == []: + return False + vector_nulo = FreeCAD.Vector(0, 0, 0) + surface = face.Surface + kind_surf = str(face.Surface) + if kind_surf == "": + axis = surface.Axis + position = surface.Position + + elif kind_surf == "": + axis = surface.Axis + radius = surface.Radius + center = surface.Center + + elif kind_surf == "": + axis = surface.Axis + apex = surface.Apex + semi_angle = surface.SemiAngle + + elif kind_surf[0:6] == "Sphere": + center = surface.Center + radius = surface.Radius + + elif kind_surf == "": + axis = surface.Axis + center = surface.Center + major_radius = surface.MajorRadius + minor_radius = surface.MinorRadius + + for elem in faces: + surf = elem.Surface + ##print surf + if str(surf) == "" and kind_surf == "": + vector_cross = axis.cross(surf.Axis) + if vector_cross == vector_nulo and is_in_plane(position, surf): + return True + elif str(surf) == "" and kind_surf == "": + dir = surf.Axis + rad = surf.Radius + pnt = surf.Center + vector_cross = axis.cross(surf.Axis) + if vector_cross == vector_nulo and radius == rad and is_in_line(center, dir, pnt): + return True + elif str(surf) == "" and kind_surf == "": + # corresponding logic for cone + dir = surf.Axis + punta = surf.Apex + semiangle = surf.SemiAngle + if axis.isEqual(dir, 1e-5) and apex.isEqual(punta, 1e-5) and (semi_angle - semiangle) < 1e-6: + return True + elif str(surf)[0:6] == "Sphere" and kind_surf[0:6] == "Sphere": + # corresponding logic for sphere + rad = surf.Radius + pnt = surf.Center + if center == pnt and radius == rad: + return True + + elif str(surf) == "" and kind_surf == "": + # corresponding logic for Torus + rad_maj = surf.MajorRadius + rad_min = surf.MinorRadius + pnt = surf.Center + dir = surf.Axis + if (axis.isEqual(dir, 1e-5) and center.isEqual(pnt, 1e-5) and (major_radius - rad_maj) < 1e-6) and ( + minor_radius - rad_min + ) < 1e-6: + return True + + return False + + +# TODO check if this function is used +def is_in_faces_2(face, faces): + + if faces == []: + return False + vector_nulo = FreeCAD.Vector(0, 0, 0) + surface = face + kind_surf = face.type + if kind_surf == "": + axis = surface.Axis + position = surface.Position + + elif kind_surf == "": + axis = surface.Axis + radius = surface.Radius + center = surface.Center + + elif kind_surf == "": + axis = surface.Axis + apex = surface.Apex + semi_angle = surface.SemiAngle + + elif kind_surf[0:6] == "Sphere": + center = surface.Center + radius = surface.Radius + + elif kind_surf == "": + axis = surface.Axis + center = surface.Center + major_radius = surface.MajorRadius + minor_radius = surface.MinorRadius + + for elem in faces: + ##print surf + if elem.type == "" and kind_surf == "": + vector_cross = axis.cross(elem.Axis) + if vector_cross == vector_nulo and is_in_plane(position, elem.Surface): + # if (is_parallel(elem.Axis,elem.Surface.Axis) and is_in_plane(Position,elem.Surface)): + return True + elif elem.type == "" and kind_surf == "": + dir = elem.Axis + rad = elem.Radius + pnt = elem.Center + vector_cross = axis.cross(elem.Axis) + if vector_cross == vector_nulo and radius == rad and is_in_line(center, dir, pnt): + return True + elif elem.type == "" and kind_surf == "": + # corresponding logic for cone + dir = elem.Axis + punta = elem.Apex + semiangle = elem.SemiAngle + if axis.isEqual(dir, 1e-5) and apex.isEqual(punta, 1e-5) and (semi_angle - semiangle) < 1e-6: + return True + elif elem.type == "Sphere" and kind_surf == "Sphere": + # corresponding logic for sphere + rad = elem.Radius + pnt = elem.Center + if center == pnt and radius == rad: + return True + elif elem.type == "" and kind_surf == "": + # corresponding logic for Torus + rad_maj = elem.MajorRadius + rad_min = elem.MinorRadius + pnt = elem.Center + dir = elem.Axis + if (axis.isEqual(dir, 1e-5) and center.isEqual(pnt, 1e-5) and (major_radius - rad_maj) < 1e-6) and ( + minor_radius - rad_min + ) < 1e-6: + return True + return False diff --git a/src/geouned/GEOUNED/Utils/booleanFunction.py b/src/geouned/GEOUNED/utils/boolean_function.py similarity index 63% rename from src/geouned/GEOUNED/Utils/booleanFunction.py rename to src/geouned/GEOUNED/utils/boolean_function.py index 5cb886b6..58650c18 100644 --- a/src/geouned/GEOUNED/Utils/booleanFunction.py +++ b/src/geouned/GEOUNED/utils/boolean_function.py @@ -1,31 +1,44 @@ +# +# Define the class storing Boolean expression of the cell definition +# + +import logging import re +logger = logging.getLogger("general_logger") + mostinner = re.compile(r"\([^\(^\)]*\)") # identify most inner parentheses -number = re.compile(r"(?P[-+]?\d+)") -mix = re.compile(r"(?P([-+]?\d+|\[0+\]))") -TFX = re.compile(r"(?P[FTXo]+)") -PValue = re.compile(r"P\d+") -NValue = re.compile(r"N\d+") -conversion = {"T": True, "F": False, "X": None} +number = re.compile(r"(?P[-+]?\d+)") # identify signed integer and record its value in +mix = re.compile(r"(?P([-+]?\d+|\[0+\]))") # identify signed integer or [000...] pattern. Record the value. +TFX = re.compile(r"(?P[FTXo]+)") # identify pattern incluinding F,T,X, or o sequence ( in any order). +PValue = re.compile(r"P\d+") # identify pattern "P" + integer pattern (e.g. P3915). +NValue = re.compile(r"N\d+") # identify pattern "N" + integer pattern (e.g. N3358). +conversion = { + "T": True, + "F": False, + "X": None, +} # associate "T", "F", "X" with associated Boolean value (or None for X) class BoolSequence: + """Class storing Boolean expression and operating on it""" + def __init__(self, definition=None, operator=None): if definition: self.elements = [] - self.setDef(definition) + self.set_def(definition) else: self.elements = [] self.operator = operator self.level = 0 def __str__(self): - out = "{}[".format(self.operator) + out = f"{self.operator}[" if type(self.elements) is bool: return " True " if self.elements else " False " for e in self.elements: if type(e) is int or type(e) is bool or type(e) is str: - out += " {} ".format(e) + out += f" {e} " else: out += e.__str__() @@ -33,6 +46,11 @@ def __str__(self): return out def append(self, *seq): + """Append a BoolSequence Objects. seq may be : + - An iterable containing allowed BoolSequence Objects + - A BoolSequence object + - An integer value + - A Boolean value""" for s in seq: if type(s) is int: level = -1 @@ -55,12 +73,7 @@ def append(self, *seq): else: level = s.level if type(s.elements) is bool: - if ( - self.operator == "AND" - and not s.elements - or self.operator == "OR" - and s.elements - ): + if self.operator == "AND" and not s.elements or self.operator == "OR" and s.elements: self.level = -1 self.elements = s.elements return @@ -70,45 +83,46 @@ def append(self, *seq): self.elements.append(s) self.level = max(self.level, level + 1) - def assign(self, Seq): - if type(Seq) is bool: + def assign(self, seq): + """Assign the BoolSequence Seq to the self instance BoolSequence""" + if type(seq) is bool: self.operator == "AND" - self.elements = Seq + self.elements = seq self.level = -1 return - self.operator = Seq.operator - self.elements = Seq.elements - self.level = Seq.level + self.operator = seq.operator + self.elements = seq.elements + self.level = seq.level - def update(self, Seq, pos): + def update(self, seq, pos): if len(pos) == 0: - self.assign(Seq) + self.assign(seq) return elif len(pos) == 1: base = self else: - base = self.getElement(pos[:-1]) + base = self.get_element(pos[:-1]) indexes = pos[-1] indexes.sort() for i in reversed(indexes): del base.elements[i] - if type(Seq.elements) is bool: - base.elements = Seq.elements + if type(seq.elements) is bool: + base.elements = seq.elements base.level = -1 else: - base.append(Seq) - base.joinOperators() - self.clean(selfLevel=True) + base.append(seq) + base.join_operators() + self.clean(self_level=True) return - def getElement(self, pos): + def get_element(self, pos): if len(pos) == 1: return self.elements[pos[0]] else: - return self.elements[pos[0]].getElement(pos[1:]) + return self.elements[pos[0]].get_element(pos[1:]) def copy(self): cp = BoolSequence() @@ -124,9 +138,9 @@ def copy(self): cp.elements.append(e.copy()) return cp - def getComplementary(self): - - c = BoolSequence(operator=self.compOperator()) + # TODO rename to snake case, care as multiple functions with same name + def get_complementary(self): + c = BoolSequence(operator=self.comp_operator()) c.level = self.level if self.level == 0: @@ -134,117 +148,118 @@ def getComplementary(self): c.elements.append(-e) return c else: - self.groupSingle() + self.group_single() for e in self.elements: - c.elements.append(e.getComplementary()) + c.elements.append(e.get_complementary()) return c - def compOperator(self): + def comp_operator(self): if self.operator == "AND": return "OR" else: return "AND" - def simplify(self, CT, depth=0): + def simplify(self, CT=None, depth=0): + """Simplification by recursive calls to the inner BoolSequence objects.""" if self.level > 0: for seq in self.elements: seq.simplify(CT, depth + 1) self.clean() - self.joinOperators() - self.levelUpdate() + self.join_operators() + self.level_update() - if type(self.elements) is not bool and ( - self.level > 0 or len(self.elements) > 1 - ): + if type(self.elements) is not bool and (self.level > 0 or len(self.elements) > 1): levIn = self.level - self.simplifySequence(CT) + self.simplify_sequence(CT) if self.level > levIn and depth < 10: self.simplify(CT, depth + 1) - def simplifySequence(self, CT): + def simplify_sequence(self, CT=None): + """Carry out the simplification process of the BoolSequence.""" if self.level < 1 and CT is None: self.clean() return - surfNames = self.getSurfacesNumbers() - if not surfNames: + surf_names = self.get_surfaces_numbers() + if not surf_names: return - newNames = surfNames - for valname in surfNames: - if valname in newNames: + newNames = surf_names + for val_name in surf_names: + if val_name in newNames: if CT is None: - trueSet = {abs(valname): True} - falseSet = {abs(valname): False} + true_set = {abs(val_name): True} + false_set = {abs(val_name): False} else: - trueSet, falseSet = CT.getConstraintSet(valname) + true_set, false_set = CT.get_constraint_set(val_name) - if not self.doFactorize(valname, trueSet, falseSet): + if not self.do_factorize(val_name, true_set, false_set): continue - self.factorize(valname, trueSet, falseSet) + self.factorize(val_name, true_set, false_set) if type(self.elements) is bool: return - newNames = self.getSurfacesNumbers() - - def doFactorize(self, valname, trueSet, falseSet): + newNames = self.get_surfaces_numbers() + def do_factorize(self, val_name, true_set, false_set): + """For level 0 sequence check if the factorization would lead to a simplification.""" if self.level > 0: return True - if trueSet is None and falseSet is None: - print(f"{valname} is not true nor false") + if true_set is None and false_set is None: + logger.info(f"{val_name} is not true nor false") return False - if trueSet is None or falseSet is None: + if true_set is None or false_set is None: return True - valSet = self.getSurfacesNumbers() - TSet = set(trueSet.keys()) & valSet - FSet = set(falseSet.keys()) & valSet + val_set = self.get_surfaces_numbers() + t_set = set(true_set.keys()) & val_set + f_set = set(false_set.keys()) & val_set - if len(TSet) == 1 and len(FSet) == 1: + if len(t_set) == 1 and len(f_set) == 1: return False value = None for val in self.elements: - if abs(val) == valname: + if abs(val) == val_name: value = val break if value is None: return False - if len(TSet) == 1: + if len(t_set) == 1: if self.operator == "AND": - # if value > 0 and TSet[valname] or value < 0 and not TSet[valname] : return False + # if value > 0 and t_set[val_name] or value < 0 and not t_set[val_name] : return False if value > 0: return False # TrueSet[Valname] always True else: - # if value < 0 and TSet[valname] or value > 0 and not TSet[valname] : return False + # if value < 0 and t_set[val_name] or value > 0 and not t_set[val_name] : return False if value < 0: return False - elif len(FSet) == 1: + elif len(f_set) == 1: if self.operator == "AND": - # if value > 0 and FSet[valname] or value < 0 and not FSet[valname] : return False + # if value > 0 and f_set[val_name] or value < 0 and not f_set[val_name] : return False if value < 0: return False else: - # if value < 0 and FSet[valname] or value > 0 and not FSet[valname] : return False + # if value < 0 and f_set[val_name] or value > 0 and not f_set[val_name] : return False if value > 0: return False return True - # check if level 0 sequence have oposite value a & -a = 0 , a|-a = 1 + # check if level 0 sequence have opposite value a & -a = 0 , a|-a = 1 # return the value of the sequence None(unknown), True, False def check(self, level0=False): + """Check BoolSequence in level 0 have oposite values a & -a = 0 , a|-a = 1.""" if type(self.elements) is bool: return self.elements if self.level == 0: - signedSurf = set(self.elements) - surfname = self.getSurfacesNumbers() - if len(signedSurf) == len(surfname): + signed_surf = set(self.elements) + surf_name = self.get_surfaces_numbers() + if len(signed_surf) == len(surf_name): return None # means same surface has not positive and negative value elif self.operator == "AND": self.elements = False @@ -255,8 +270,8 @@ def check(self, level0=False): self.level = -1 return True elif not level0: - self.groupSingle() - noneVal = False + self.group_single() + none_val = False for e in reversed(self.elements): e.check() if type(e.elements) is bool: @@ -265,7 +280,7 @@ def check(self, level0=False): res = None if res is None: - noneVal = True + none_val = True elif self.operator == "AND" and res is False: self.level = -1 self.elements = False @@ -277,7 +292,7 @@ def check(self, level0=False): else: self.elements.remove(e) - if noneVal: + if none_val: return None elif self.operator == "AND": self.level = -1 @@ -289,6 +304,9 @@ def check(self, level0=False): return False def substitute(self, var, val): + """Substitute in the BoolSequence the variable "var" by the value "val". + "val" can be an Boolean value or an integer representing another surface variable. + """ if val is None: return if type(self.elements) is bool: @@ -307,15 +325,15 @@ def substitute(self, var, val): else: if name == e: - boolValue = val + bool_value = val else: - boolValue = not val + bool_value = not val - if self.operator == "AND" and not boolValue: + if self.operator == "AND" and not bool_value: self.elements = False self.level = -1 return - elif self.operator == "OR" and boolValue: + elif self.operator == "OR" and bool_value: self.elements = True self.level = -1 return @@ -330,18 +348,18 @@ def substitute(self, var, val): self.level = -1 return - self.clean(selfLevel=True) + self.clean(self_level=True) self.check(level0=True) - self.joinOperators(selfLevel=True) + self.join_operators(self_level=True) - # remove sequence whom elements are boolean values instead of list - def clean(self, selfLevel=False): + def clean(self, self_level=False): + """Remove sequences whom elements are boolean values instead of list.""" if type(self.elements) is bool: return self.elements for e in reversed(self.elements): if type(e) is int: continue - eVal = e if selfLevel else e.clean() + eVal = e if self_level else e.clean() if type(eVal) is not bool: eVal = eVal.elements @@ -366,15 +384,16 @@ def clean(self, selfLevel=False): else: return self - # join redundant operators in sequence - def joinOperators(self, selfLevel=False): + # TODO rename to snake case, care as multiple functions with same name + def join_operators(self, self_level=False): + """Join redundant operators in found in the sequence.""" if type(self.elements) is bool: return - self.clean(selfLevel=True) - self.levelUpdate() + self.clean(self_level=True) + self.level_update() if self.level == 0: return - self.groupSingle() + self.group_single() ANDop = [] ORop = [] @@ -389,7 +408,7 @@ def joinOperators(self, selfLevel=False): for s in ANDop: newSeq.elements.extend(s.elements) self.elements.remove(s) - newSeq.levelUpdate() + newSeq.level_update() self.append(newSeq) elif len(ORop) > 1 and self.operator == "OR": @@ -397,32 +416,32 @@ def joinOperators(self, selfLevel=False): for s in ORop: newSeq.elements.extend(s.elements) self.elements.remove(s) - newSeq.levelUpdate() + newSeq.level_update() self.append(newSeq) if self.level > 0 and len(self.elements) == 1: self.operator = self.elements[0].operator self.elements[:] = self.elements[0].elements[:] self.level -= 1 - self.joinOperators() + self.join_operators() if self.level == 0: self.check() return - if not selfLevel: + if not self_level: if type(self.elements) is bool: return for e in self.elements: - e.joinOperators() + e.join_operators() - def getSubSequence(self, setIn): + def get_sub_sequence(self, setIn): if type(setIn) is set: - valSet = setIn + val_set = setIn elif type(setIn) is int: - valSet = {setIn} + val_set = {setIn} else: - valSet = set(setIn.keys()) + val_set = set(setIn.keys()) if self.level == 0: return ([], self) @@ -431,53 +450,53 @@ def getSubSequence(self, setIn): subSeq = BoolSequence(operator=self.operator) for pos, e in enumerate(self.elements): - surf = e.getSurfacesNumbers() - if len(surf & valSet) != 0: + surf = e.get_surfaces_numbers() + if len(surf & val_set) != 0: subSeq.append(e) position.append(pos) if len(position) == 1 and subSeq.elements[0].level > 0: - subList, subSeq = subSeq.elements[0].getSubSequence(valSet) + subList, subSeq = subSeq.elements[0].get_sub_sequence(val_set) subList.insert(0, position[0]) else: subList = [position] return subList, subSeq - def factorize(self, valname, trueSet, falseSet): - - if trueSet is None: # valname cannot take True value - falseFunc = self.evaluate(falseSet) + def factorize(self, valname, true_set, false_set): + """Make the factorization of the Sequence on variable valname using Shannon's theorem.""" + if true_set is None: # valname cannot take True value + falseFunc = self.evaluate(false_set) self.assign(falseFunc) return True - if falseSet is None: # valname cannot take false value - trueFunc = self.evaluate(trueSet) + if false_set is None: # valname cannot take false value + trueFunc = self.evaluate(true_set) self.assign(trueFunc) return True - valSet = set(trueSet.keys()) - valSet.update(falseSet.keys()) - pos, subSeq = self.getSubSequence(valSet) + val_set = set(true_set.keys()) + val_set.update(false_set.keys()) + pos, subSeq = self.get_sub_sequence(val_set) updt = True if len(pos) == 0: subSeq = self updt = False - trueFunc = subSeq.evaluate(trueSet) + trueFunc = subSeq.evaluate(true_set) - falseFunc = subSeq.evaluate(falseSet) + falseFunc = subSeq.evaluate(false_set) - if trueFunc == False: + if trueFunc is False: newSeq = BoolSequence(operator="AND") - if falseFunc == True: + if falseFunc is True: newSeq.append(-valname) - elif falseFunc == False: + elif falseFunc is False: newSeq.elements = False newSeq.level = -1 else: newSeq.append(-valname, falseFunc) - newSeq.joinOperators(selfLevel=True) + newSeq.join_operators(self_level=True) if updt: self.update(newSeq, pos) @@ -485,16 +504,16 @@ def factorize(self, valname, trueSet, falseSet): self.assign(newSeq) return True - elif trueFunc == True: + elif trueFunc is True: newSeq = BoolSequence(operator="OR") - if falseFunc == True: + if falseFunc is True: newSeq.elements = True newSeq.level = -1 - elif falseFunc == False: + elif falseFunc is False: newSeq.append(valname) else: newSeq.append(valname, falseFunc) - newSeq.joinOperators(selfLevel=True) + newSeq.join_operators(self_level=True) if updt: self.update(newSeq, pos) @@ -502,32 +521,32 @@ def factorize(self, valname, trueSet, falseSet): self.assign(newSeq) return True - if falseFunc == False: + if falseFunc is False: newSeq = BoolSequence(operator="AND") - if trueFunc == True: + if trueFunc is True: newSeq.append(valname) - elif trueFunc == False: + elif trueFunc is False: newSeq.elements = False newSeq.level = -1 else: newSeq.append(valname, trueFunc) - newSeq.joinOperators(selfLevel=True) + newSeq.join_operators(self_level=True) if updt: self.update(newSeq, pos) else: self.assign(newSeq) return True - elif falseFunc == True: + elif falseFunc is True: newSeq = BoolSequence(operator="OR") - if trueFunc == True: + if trueFunc is True: newSeq.elements = True newSeq.level = -1 - elif trueFunc == False: + elif trueFunc is False: newSeq.append(-valname) else: newSeq.append(-valname, trueFunc) - newSeq.joinOperators(selfLevel=True) + newSeq.join_operators(self_level=True) if updt: self.update(newSeq, pos) else: @@ -535,10 +554,11 @@ def factorize(self, valname, trueSet, falseSet): return True def evaluate(self, valueSet): - + """Return the result of the evaluation of the BoolSequence given the known values of the variables in "valueSet". + Result can be a Boolean value or the reduced expresion of the BoolSequence.""" if type(self.elements) is bool: return self.elements - self.groupSingle() + self.group_single() newSeq = self.copy() for name, value in valueSet.items(): newSeq.substitute(name, value) @@ -547,14 +567,17 @@ def evaluate(self, valueSet): return newSeq.elements if type(newSeq.elements) is bool else newSeq - def setDef(self, expression): - terms, operator = outterTerms(expression) + def set_def(self, expression): + """Set the expression of the Boolean function in the BoolSequence instance. + "expresion" is the string object. The definition should have MCNP syntax cell definition. + """ + terms, operator = outer_terms(expression) self.operator = operator self.level = 0 lev0Seq = set() lev0SeqAbs = set() for t in terms: - if isInteger(t): + if is_integer(t): val = int(t.strip("(").strip(")")) lev0Seq.add(val) lev0SeqAbs.add(abs(val)) @@ -574,9 +597,11 @@ def setDef(self, expression): else: self.append(*lev0Seq) - self.groupSingle() + self.group_single() - def groupSingle(self): + def group_single(self): + """group integers found in Sequence with level > 1. + (e.g. change AND[1 2 3 OR[2 4]] to AND[ AND[1 2 3] OR[2 3]] ).""" if self.level == 0: return if type(self.elements) is bool: @@ -598,7 +623,8 @@ def groupSingle(self): seq.level = 0 self.elements.insert(0, seq) - def getSurfacesNumbers(self): + def get_surfaces_numbers(self): + """Return the list of all surfaces in the BoolSequence definition.""" if type(self.elements) is bool: return tuple() surf = set() @@ -606,10 +632,11 @@ def getSurfacesNumbers(self): if type(e) is int: surf.add(abs(e)) else: - surf.update(e.getSurfacesNumbers()) + surf.update(e.get_surfaces_numbers()) return surf - def levelUpdate(self): + def level_update(self): + """Update the level value of the BoolSequence.""" if type(self.elements) is bool: self.level = 0 return @@ -618,32 +645,36 @@ def levelUpdate(self): for e in self.elements: if type(e) is int: continue - e.levelUpdate() + e.level_update() self.level = max(e.level + 1, self.level) -def insertInSequence(Seq, trgt, nsrf, operator): - +def insert_in_sequence(Seq, trgt, nsrf, operator): + """Substitute the variable trgt by the sequence "(trgt:nsrg)" or "(trgt nsf)" in the + BoolSequence Seq""" if operator == "OR": newSeq = BoolSequence(f"{trgt}:{nsrf}") else: newSeq = BoolSequence(f"{trgt} {nsrf}") - substituteIntegerElement(Seq, trgt, newSeq) - Seq.levelUpdate() - # Seq.joinOperators() + substitute_integer_element(Seq, trgt, newSeq) + Seq.level_update() + # Seq.join_operators() -def substituteIntegerElement(Seq, target, newElement): +def substitute_integer_element(Seq, target, newElement): + """Substitute the variable target by the sequence newElement in the + BoolSequence Seq""" for i, e in enumerate(Seq.elements): if type(e) is int: if e == target: Seq.elements[i] = newElement else: - substituteIntegerElement(e, target, newElement) + substitute_integer_element(e, target, newElement) -def outterTerms(expression, value="number"): +def outer_terms(expression, value="number"): + """Return the list and the boolean operator of the outter terms of the expression.""" if value == "number": # reValue = number reValue = mix @@ -668,13 +699,7 @@ def outterTerms(expression, value="number"): cont = True if redundant(m, expr): # remove redundant parentheses - expr = ( - expr[: m.start()] - + " " - + expr[m.start() + 1 : m.end() - 1] - + " " - + expr[m.end() :] - ) + expr = expr[: m.start()] + " " + expr[m.start() + 1 : m.end() - 1] + " " + expr[m.end() :] else: # replace no redundant parentheses by 0 and : by ; zeros = "[" + nullVal * (m.end() - m.start() - 2) + "]" @@ -686,12 +711,12 @@ def outterTerms(expression, value="number"): terms = [] pos = 0 while True: - newpos = expr.find(":", pos) - if newpos == -1: + new_pos = expr.find(":", pos) + if new_pos == -1: terms.append(expression[pos:].strip()) break - terms.append(expression[pos:newpos].strip()) - pos = newpos + 1 + terms.append(expression[pos:new_pos].strip()) + pos = new_pos + 1 return (terms, "OR") else: terms = [] @@ -706,18 +731,18 @@ def outterTerms(expression, value="number"): def redundant(m, geom): - """check if the inner parentheses are redundant""" + """Check if the inner parentheses are redundant.""" term = m.group() # Find first valid character at the left of the parenthese - leftOK = True + left_ok = True left = m.start() - 1 while left > -1: if geom[left] in ("\n", "C", "$", " "): left -= 1 else: if geom[left] not in ("(", ":"): - leftOK = False + left_ok = False break # check if no ':' (or) are inside the parenthese @@ -726,27 +751,27 @@ def redundant(m, geom): return True # Find first valid character at the right of the parenthese - rightOK = True + right_ok = True right = m.end() while right < len(geom): if geom[right] in ("\n", "C", "$", " "): right += 1 else: if geom[right] not in (")", ":"): - rightOK = False + right_ok = False break # if parentheses are like: # {( or : } ( ....... ) {) or :} # parentheses are redundants - if leftOK and rightOK: + if left_ok and right_ok: return True else: return False -def isInteger(x): +def is_integer(x): try: int(x.strip("(").strip(")")) return True diff --git a/src/geouned/GEOUNED/Utils/BooleanSolids.py b/src/geouned/GEOUNED/utils/boolean_solids.py similarity index 82% rename from src/geouned/GEOUNED/Utils/BooleanSolids.py rename to src/geouned/GEOUNED/utils/boolean_solids.py index e1d9cf5c..82591f76 100644 --- a/src/geouned/GEOUNED/Utils/BooleanSolids.py +++ b/src/geouned/GEOUNED/utils/boolean_solids.py @@ -2,16 +2,18 @@ # Conversion to MCNP v0.0 # Only one solid and planar surfaces # +import logging import math import FreeCAD -from ..Utils.booleanFunction import BoolSequence -from ..Utils.Functions import GEOUNED_Surface, splitBOP -from ..Utils.Options.Classes import Options as opt +from .boolean_function import BoolSequence +from .functions import GeounedSurface, split_bop BoolVals = (None, True, False) +logger = logging.getLogger("general_logger") + class CTelement: def __init__(self, val=None, S1=None, S2=None): @@ -28,14 +30,12 @@ def __init__(self, val=None, S1=None, S2=None): self.type = val.count(0) self.val = val - def getTranspose(self): + def get_transpose(self): if self.diagonal: return self.val - return CTelement( - (self.val[0], self.val[3], self.val[2], self.val[1]), self.S2, self.S1 - ) + return CTelement((self.val[0], self.val[3], self.val[2], self.val[1]), self.S2, self.S1) - def getDependence(self): + def get_dependence(self): if self.diagonal: if self.val == 0: return True, False @@ -96,70 +96,70 @@ def __str__(self): line = "" for name in varName: element = self[name][name] - line += " {:4d} : {}\n".format(name, element.val) + line += f" {name:4d} : {element.val}\n" return line outstr = " " for name in varName: - outstr = outstr + " {:3d}".format(name) + outstr = outstr + f" {name:3d}" outstr = outstr + "\n" for name1 in varName: - line = " {:3d} ".format(name1) - linenot = "~{:3d} ".format(name1) + line = f" {name1:3d} " + linenot = f"~{name1:3d} " for name2 in varName: elmt = self[name1][name2] if elmt.diagonal: - line += " {:>2d} ".format(elmt.val) + line += f" {elmt.val:>2d} " linenot += " " else: - line += " {}{} ".format(elmt.val[0], elmt.val[1]) - linenot += " {}{} ".format(elmt.val[3], elmt.val[2]) + line += f" {elmt.val[0]}{elmt.val[1]} " + linenot += f" {elmt.val[3]}{elmt.val[2]} " outstr += line + "\n" outstr += linenot + "\n" return outstr - def addElement(self, k1, k2, val): + def add_element(self, k1, k2, val): if k1 in self.keys(): self[k1][k2] = val else: self[k1] = {k2: val} - def fillMissingElements(self): + def fill_missing_elements(self): keys = list(self.keys()) missing = [] for i, k1 in enumerate(keys): for k2 in keys[i + 1 :]: if k2 in self[k1].keys(): elmt = self[k1][k2] - self[k2][k1] = elmt.getTranspose() + self[k2][k1] = elmt.get_transpose() else: missing.append((k1, k2)) for k1, k2 in missing: diag1 = self[k1][k1] diag2 = self[k2][k2] - new = combineDiagElements(diag1, diag2) + new = combine_diag_elements(diag1, diag2) new.S1 = k1 new.S2 = k2 self[k1][k2] = new - self[k2][k1] = new.getTranspose() + self[k2][k1] = new.get_transpose() - def getOutSurfaces(self): + def get_out_surfaces(self): out = [] for k in self.keys(): if self[k][k].val != 0: out.append(k) return out - def getConstraintSet(self, valname): + def get_constraint_set(self, valname): trueSet = {} falseSet = {} TNull = False FNull = False for k in self.keys(): - TValue, FValue = self[valname][k].getDependence() + TValue, FValue = self[valname][k].get_dependence() if TValue == "Null": TNull = True TValue = None @@ -177,8 +177,8 @@ def getConstraintSet(self, valname): falseSet = None return trueSet, falseSet - def solidInBox(self, Seq): # Sequence of the cell - surfs = tuple(Seq.getSurfacesNumbers()) + def solid_in_box(self, Seq): # Sequence of the cell + surfs = tuple(Seq.get_surfaces_numbers()) if self.diagonal: seqValues = dict() for s in surfs: @@ -192,7 +192,7 @@ def solidInBox(self, Seq): # Sequence of the cell return res if type(res) is bool else None else: - trueSet, falseSet = self.getConstraintSet(surfs[0]) + trueSet, falseSet = self.get_constraint_set(surfs[0]) if trueSet is not None: trueVal = Seq.evaluate(trueSet) trueVal = trueVal if type(trueVal) is bool else None @@ -213,11 +213,11 @@ def solidInBox(self, Seq): # Sequence of the cell falseVal = Seq.evaluate(falseSet) return falseVal if type(falseVal) is bool else None else: - print("Bad trouble surfaces {} is on none side of the box!!") + logger.info("Bad trouble surfaces is on none side of the box!!") return False -def combineDiagElements(d1, d2): +def combine_diag_elements(d1, d2): if d1.val == 0 and d2.val == 0: return CTelement((1, 1, 1, 1)) @@ -239,7 +239,7 @@ def combineDiagElements(d1, d2): return CTelement((0, 0, 1, 0)) -def buildCTableFromSolids(Box, SurfInfo, option="diag"): +def build_c_table_from_solids(Box, SurfInfo, option, options): if type(SurfInfo) is dict: surfaces = SurfInfo @@ -250,11 +250,11 @@ def buildCTableFromSolids(Box, SurfInfo, option="diag"): surfaces = SurfInfo.Surfaces surfaceList = SurfInfo.surfaceList - if type(surfaces[surfaceList[0]]) is GEOUNED_Surface: + if type(surfaces[surfaceList[0]]) is GeounedSurface: for s in surfaceList: ss = surfaces[s] ss.__boundBox__ = Box.BoundBox - ss.buildSurface() + ss.build_surface() else: for s in surfaceList: surfaces[s].buildShape(Box.BoundBox) @@ -266,10 +266,10 @@ def buildCTableFromSolids(Box, SurfInfo, option="diag"): CTable.diagonal = False for i, s1 in enumerate(surfaceList): - res, splitRegions = splitSolid_fast(Box, surfaces[s1], True) - # res,splitRegions = splitSolid_fast(Box,Surfaces.getSurface(s1),True) + res, splitRegions = split_solid_fast(Box, surfaces[s1], True, options) + # res,splitRegions = split_solid_fast(Box,Surfaces.get_surface(s1),True) - CTable.addElement(s1, s1, CTelement(res, s1, s1)) + CTable.add_element(s1, s1, CTelement(res, s1, s1)) if option == "diag": continue if splitRegions is None: @@ -280,9 +280,9 @@ def buildCTableFromSolids(Box, SurfInfo, option="diag"): pos0 = None for solid in posS1: - pos = splitSolid_fast(solid, surfaces[s2], False) + pos = split_solid_fast(solid, surfaces[s2], False, options) - # pos = splitSolid_fast(solid,Surfaces.getSurface(s2),False) + # pos = split_solid_fast(solid,Surfaces.get_surface(s2),False) if pos == (1, 1): break # s2 intersect S1 Region if pos0 is None: @@ -294,8 +294,8 @@ def buildCTableFromSolids(Box, SurfInfo, option="diag"): neg0 = None for solid in negS1: - # neg = splitSolid_fast(solid,Surfaces.getSurface(s2),False) - neg = splitSolid_fast(solid, surfaces[s2], False) + # neg = split_solid_fast(solid,Surfaces.get_surface(s2),False) + neg = split_solid_fast(solid, surfaces[s2], False, options) if neg == (1, 1): break # s2 intersect S1 Region if neg0 is None: @@ -306,17 +306,17 @@ def buildCTableFromSolids(Box, SurfInfo, option="diag"): break val = (pos[0], pos[1], neg[1], neg[0]) - CTable.addElement(s1, s2, CTelement(val, s1, s2)) + CTable.add_element(s1, s2, CTelement(val, s1, s2)) # if some surfaces don't cross the box some elements in Constraint table are not filled if option != "diag": - CTable.fillMissingElements() + CTable.fill_missing_elements() return CTable -def removeExtraSurfaces(CellSeq, CTable): +def remove_extra_surfaces(CellSeq, CTable): # checking is make on solid cell definition to be removed from void cell - outSurfaces = set(CTable.getOutSurfaces()) + outSurfaces = set(CTable.get_out_surfaces()) newDef = BoolSequence(operator="OR") # Loop over all compound solids of the metaSolid @@ -337,30 +337,30 @@ def removeExtraSurfaces(CellSeq, CTable): else: chk = None - if chk == False: # the cell doesn't exist + if chk is False: # the cell doesn't exist nullcell = True - elif chk == True: # the cell describe the full universe + elif chk is True: # the cell describe the full universe newDef.elements = True newDef.level = -1 return newDef # if subcell has finite volume check it intersection with the box if not nullcell: - res = CTable.solidInBox(subCell) - if res == None: + res = CTable.solid_in_box(subCell) + if res is None: # subcell intersect the box # get the surfaces of the solids out of the box # get reduced definition # if subcell lev!= 0 remove surface operation is not valid if subCell.level == 0: - removeSurf = outSurfaces & subCell.getSurfacesNumbers() + removeSurf = outSurfaces & subCell.get_surfaces_numbers() for s in removeSurf: val = True if CTable[s][s].val > 0 else False subCell.substitute(s, val) if type(subCell.elements) is bool: - if subCell.elements == False: # cell does not intersect void box + if subCell.elements is False: # cell does not intersect void box continue else: # cell cover fully void box newDef.elements = True @@ -369,7 +369,7 @@ def removeExtraSurfaces(CellSeq, CTable): else: newDef.append(subCell) - elif res == True: + elif res is True: # subcell cover the full box region Void cell doesn't exist newDef.elements = True newDef.level = -1 @@ -380,17 +380,17 @@ def removeExtraSurfaces(CellSeq, CTable): return newDef -def splitSolid_fast(solid, surf, box): +def split_solid_fast(solid, surf, box, options): if box: if surf.shape: - comsolid = splitBOP(solid, [surf.shape], opt.splitTolerance) + comsolid = split_bop(solid, [surf.shape], options.splitTolerance, options) else: - return checkSign(solid, surf), None + return check_sign(solid, surf), None if len(comsolid.Solids) <= 1: - return checkSign(solid, surf), None - # sgn = checkSign(solid,surf) # if "box" and single object => the box is not split, surface s1 out of the box. + return check_sign(solid, surf), None + # sgn = check_sign(solid,surf) # if "box" and single object => the box is not split, surface s1 out of the box. # if sgn == 1 : # The sign is the side of surface s1 where the box is located # # return ((1,0),(0,0)),None # return the diagonal element of the Constraint Table for s1 # return ((1,0),(0,0)),None # return the diagonal element of the Constraint Table for s1 @@ -401,7 +401,7 @@ def splitSolid_fast(solid, surf, box): posSol = [] negSol = [] for s in comsolid.Solids: - sgn = checkSign(s, surf) + sgn = check_sign(s, surf) if sgn == 1: posSol.append(s) else: @@ -419,7 +419,7 @@ def splitSolid_fast(solid, surf, box): else: dist = 1.0 if dist > 1e-6: # face doesn't intersect solid - sgn = checkSign(solid, surf) + sgn = check_sign(solid, surf) if sgn == 1: return ( 1, @@ -460,7 +460,7 @@ def point_inside(solid): BBox = solid.optimalBoundingBox(False) box = [BBox.XMin, BBox.XMax, BBox.YMin, BBox.YMax, BBox.ZMin, BBox.ZMax] - boxes, centers = CutBox(box) + boxes, centers = cut_box(box) n = 0 while True: @@ -472,7 +472,7 @@ def point_inside(solid): subbox = [] centers = [] for b in boxes: - btab, ctab = CutBox(b) + btab, ctab = cut_box(b) subbox.extend(btab) centers.extend(ctab) boxes = subbox @@ -481,10 +481,10 @@ def point_inside(solid): if n == cut_box: break - return pointFromSurface(solid) + return point_from_surface(solid) -def pointFromSurface(solid): +def point_from_surface(solid): for face in solid.Faces: parameters = face.ParameterRange @@ -510,12 +510,12 @@ def pointFromSurface(solid): d *= 0.5 pp = pface + d * normal - print("Solid not found in bounding Box (Volume : {})".format(solid.Volume)) + logger.info(f"Solid not found in bounding Box (Volume : {solid.Volume})") return None # divide a box into 8 smaller boxes -def CutBox(Box): +def cut_box(Box): xmid = (Box[1] + Box[0]) * 0.5 ymid = (Box[3] + Box[2]) * 0.5 zmid = (Box[5] + Box[4]) * 0.5 @@ -547,7 +547,7 @@ def CutBox(Box): return (b1, b2, b3, b4, b5, b6, b7, b8), (p1, p2, p3, p4, p5, p6, p7, p8) -def checkSign(solid, surf): +def check_sign(solid, surf): point = point_inside(solid) diff --git a/src/geouned/GEOUNED/utils/data_classes.py b/src/geouned/GEOUNED/utils/data_classes.py new file mode 100644 index 00000000..b29de9a2 --- /dev/null +++ b/src/geouned/GEOUNED/utils/data_classes.py @@ -0,0 +1,835 @@ +from numbers import Real + + +class Options: + """A class for containing conversion options + + Args: + forceCylinder (bool, optional): Use cylinder (instead of cones) as + ancillary surface where unclosed torus surfaces are involved in + the solid definition. Defaults to False. + newSplitPlane (bool, optional): New method to consider plane as + cutting surface during the decomposition process. Former method + split first planes perpendicular to X,Y,Z axis and then the + other planes involved in the solid definition. New method group + all parallel planes independently whether their normal are + along X,Y,Z axes, and start the decomposition process cutting + first with the group having the highest number of parallel + planes. Defaults to True. + delLastNumber (bool, optional): Deleting the last word in the + comment if it is a number. Defaults to False. + enlargeBox (Real, optional): Enlarge box boundary when evaluating + the constraint table during the simplification of the void cell + definition. (unit is millimeter). Defaults to 2. + nPlaneReverse (int, optional): Threshold value to determine whether + cut with parallel planes should be carried out first. Defaults + to 0. + splitTolerance (Real, optional): Fuzzy tolerance value used in the + FreeCAD function “BOPTools.SplitAPI.slice”. This function is + used during the solid decomposition process. Defaults to 0. + scaleUp (bool, optional): Scale up Fuzzy tolerance once get below + 1e-12. Defaults to True. + quadricPY (bool, optional): In openMC python script format, the + cones or cylinders no aligned with the X,Y, or Z axis can be + defined using the openmc.Cone or open.Cylinder methods but can + also be defined with their quadric parameter. If “quadricPY” is + 11 True then all cones and cylinders will be defined in the + openMC python script format under their quadric form. Defaults + to False. + Facets (bool, optional): use alternative conversion module when + geometry is defined by cells compound by only triangular plane + faces. Defaults to False. + prnt3PPlane (bool, optional): print 3 point plane definition in + output as 3 points coordinates. Defaults to False. + forceNoOverlap (bool, optional): force no overlaping cell + definition. Adjacent cell definition are rested from current + cell definition. Defaults to False. + """ + + def __init__( + self, + forceCylinder: bool = False, + newSplitPlane: bool = True, + delLastNumber: bool = False, + enlargeBox: Real = 2.0, + nPlaneReverse: int = 0, + splitTolerance: Real = 0.0, + scaleUp: bool = True, + quadricPY: bool = False, + Facets: bool = False, + prnt3PPlane: bool = False, + forceNoOverlap: bool = False, + ): + + self.forceCylinder = forceCylinder + self.newSplitPlane = newSplitPlane + self.delLastNumber = delLastNumber + self.enlargeBox = enlargeBox + self.nPlaneReverse = nPlaneReverse + self.splitTolerance = splitTolerance + self.scaleUp = scaleUp + self.quadricPY = quadricPY + self.Facets = Facets + self.prnt3PPlane = prnt3PPlane + self.forceNoOverlap = forceNoOverlap + + @property + def forceCylinder(self): + return self._forceCylinder + + @forceCylinder.setter + def forceCylinder(self, value: bool): + if not isinstance(value, bool): + raise TypeError(f"geouned.Options.forceCylinder should be a bool, not a {type(value)}") + self._forceCylinder = value + + @property + def newSplitPlane(self): + return self._newSplitPlane + + @newSplitPlane.setter + def newSplitPlane(self, value: bool): + if not isinstance(value, bool): + raise TypeError(f"geouned.Options.newSplitPlane should be a bool, not a {type(value)}") + self._newSplitPlane = value + + @property + def delLastNumber(self): + return self._delLastNumber + + @delLastNumber.setter + def delLastNumber(self, value: bool): + if not isinstance(value, bool): + raise TypeError(f"geouned.Options.delLastNumber should be a bool, not a {type(value)}") + self._delLastNumber = value + + @property + def enlargeBox(self): + return self._enlargeBox + + @enlargeBox.setter + def enlargeBox(self, value: Real): + if not isinstance(value, Real): + raise TypeError(f"geouned.Options.enlargeBox should be a Real, not a {type(value)}") + if value < 0: + raise ValueError(f"geouned.Options.enlargeBox should be above 0, not {value}") + self._enlargeBox = value + + @property + def nPlaneReverse(self): + return self._nPlaneReverse + + @nPlaneReverse.setter + def nPlaneReverse(self, value: int): + if not isinstance(value, int): + raise TypeError(f"geouned.Options.nPlaneReverse should be a int, not a {type(value)}") + self._nPlaneReverse = value + + @property + def splitTolerance(self): + return self._splitTolerance + + @splitTolerance.setter + def splitTolerance(self, value: Real): + if not isinstance(value, Real): + raise TypeError(f"geouned.Options.splitTolerance should be a Real, not a {type(value)}") + if value < 0: + raise ValueError(f"geouned.Options.splitTolerance should be above 0, not {value}") + self._splitTolerance = value + + @property + def scaleUp(self): + return self._scaleUp + + @scaleUp.setter + def scaleUp(self, value: bool): + if not isinstance(value, bool): + raise TypeError(f"geouned.Options.scaleUp should be a bool, not a {type(value)}") + self._scaleUp = value + + @property + def quadricPY(self): + return self._quadricPY + + @quadricPY.setter + def quadricPY(self, value: bool): + if not isinstance(value, bool): + raise TypeError(f"geouned.Options.quadricPY should be a bool, not a {type(value)}") + self._quadricPY = value + + @property + def Facets(self): + return self._Facets + + @Facets.setter + def Facets(self, value: bool): + if not isinstance(value, bool): + raise TypeError(f"geouned.Options.Facets should be a bool, not a {type(value)}") + self._Facets = value + + @property + def prnt3PPlane(self): + return self._prnt3PPlane + + @prnt3PPlane.setter + def prnt3PPlane(self, value: bool): + if not isinstance(value, bool): + raise TypeError(f"geouned.Options.prnt3PPlane should be a bool, not a {type(value)}") + self._prnt3PPlane = value + + @property + def forceNoOverlap(self): + return self._forceNoOverlap + + @forceNoOverlap.setter + def forceNoOverlap(self, value: bool): + if not isinstance(value, bool): + raise TypeError(f"geouned.Options.forceNoOverlap should be a bool, not a {type(value)}") + self._forceNoOverlap = value + + +class Tolerances: + """A class for containing tolerances values + + Args: + relativeTol (bool, optional): _description_. Defaults to False. + relativePrecision (float, optional): relative precision. Defaults to 1.0e-6. + value (float, optional): Tolerance in single value comparison. Defaults to 1.0e-6. + distance (float, optional): General Distance Tolerance. Defaults to 1.0e-4. + angle (float, optional): General Angle Tolerance. Defaults to 1.0e-4. + pln_distance (float, optional): distance between planes equal planes if distance between parallel planes < 1e-4 cm. Defaults to 1.0e-4. + pln_angle (float, optional): angle between axis. 1e-4 : planes separate each other 0.1mm each 1m. Defaults to 1.0e-4. + cyl_distance (float, optional): distance between radius/center. Defaults to 1.0e-4. + cyl_angle (float, optional): angle between axis. Defaults to 1.0e-4. + sph_distance (float, optional): distance between radius/center. Defaults to 1.0e-4. + kne_distance (float, optional): distance between apex. Defaults to 1.0e-4. + kne_angle (float, optional): angle between semiangles/axis. Defaults to 1.0e-4. + tor_distance (float, optional): distance between Major/Minor radii/center. Defaults to 1.0e-4. + tor_angle (float, optional): angle between axis. Defaults to 1.0e-4. + min_area (float, optional): minimum face area to consider in cell definition. Defaults to 1.0e-2. + """ + + def __init__( + self, + relativeTol: bool = False, + relativePrecision: float = 1.0e-6, + value: float = 1.0e-6, + distance: float = 1.0e-4, + angle: float = 1.0e-4, + pln_distance: float = 1.0e-4, + pln_angle: float = 1.0e-4, + cyl_distance: float = 1.0e-4, + cyl_angle: float = 1.0e-4, + sph_distance: float = 1.0e-4, + kne_distance: float = 1.0e-4, + kne_angle: float = 1.0e-4, + tor_distance: float = 1.0e-4, + tor_angle: float = 1.0e-4, + min_area: float = 1.0e-2, + ): + + self.relativeTol = relativeTol + self.relativePrecision = relativePrecision + self.value = value + self.distance = distance + self.angle = angle + self.pln_distance = pln_distance + self.pln_angle = pln_angle + self.cyl_distance = cyl_distance + self.cyl_angle = cyl_angle + self.sph_distance = sph_distance + self.kne_distance = kne_distance + self.kne_angle = kne_angle + self.tor_distance = tor_distance + self.tor_angle = tor_angle + self.min_area = min_area + + @property + def relativeTol(self): + return self._relativeTol + + @relativeTol.setter + def relativeTol(self, value: bool): + if not isinstance(value, bool): + raise TypeError(f"geouned.Tolerances.relativeTol should be a bool, not a {type(value)}") + self._relativeTol = value + + @property + def relativePrecision(self): + return self._relativePrecision + + @relativePrecision.setter + def relativePrecision(self, value: float): + if not isinstance(value, float): + raise TypeError(f"geouned.Tolerances.relativePrecision should be a float, not a {type(value)}") + self._relativePrecision = value + + @property + def value(self): + return self._value + + @value.setter + def value(self, value: float): + if not isinstance(value, float): + raise TypeError(f"geouned.Tolerances.value should be a float, not a {type(value)}") + self._value = value + + @property + def distance(self): + return self._distance + + @distance.setter + def distance(self, distance: float): + if not isinstance(distance, float): + raise TypeError(f"geouned.Tolerances.distance should be a float, not a {type(distance)}") + self._distance = distance + + @property + def angle(self): + return self._angle + + @angle.setter + def angle(self, angle: float): + if not isinstance(angle, float): + raise TypeError(f"geouned.Tolerances.angle should be a float, not a {type(angle)}") + self._angle = angle + + @property + def pln_distance(self): + return self._pln_distance + + @pln_distance.setter + def pln_distance(self, pln_distance: float): + if not isinstance(pln_distance, float): + raise TypeError(f"geouned.Tolerances.pln_distance should be a float, not a {type(pln_distance)}") + self._pln_distance = pln_distance + + @property + def cyl_distance(self): + return self._cyl_distance + + @cyl_distance.setter + def cyl_distance(self, cyl_distance: float): + if not isinstance(cyl_distance, float): + raise TypeError(f"geouned.Tolerances.cyl_distance should be a float, not a {type(cyl_distance)}") + self._cyl_distance = cyl_distance + + @property + def cyl_angle(self): + return self._cyl_angle + + @cyl_angle.setter + def cyl_angle(self, cyl_angle: float): + if not isinstance(cyl_angle, float): + raise TypeError(f"geouned.Tolerances.cyl_angle should be a float, not a {type(cyl_angle)}") + self._cyl_angle = cyl_angle + + @property + def sph_distance(self): + return self._sph_distance + + @sph_distance.setter + def sph_distance(self, sph_distance: float): + if not isinstance(sph_distance, float): + raise TypeError(f"geouned.Tolerances.sph_distance should be a float, not a {type(sph_distance)}") + self._sph_distance = sph_distance + + @property + def pln_angle(self): + return self._pln_angle + + @pln_angle.setter + def pln_angle(self, pln_angle: float): + if not isinstance(pln_angle, float): + raise TypeError(f"geouned.Tolerances.pln_angle should be a float, not a {type(pln_angle)}") + self._pln_angle = pln_angle + + @property + def kne_distance(self): + return self._kne_distance + + @kne_distance.setter + def kne_distance(self, kne_distance: float): + if not isinstance(kne_distance, float): + raise TypeError(f"geouned.Tolerances.kne_distance should be a float, not a {type(kne_distance)}") + self._kne_distance = kne_distance + + @property + def kne_angle(self): + return self._kne_angle + + @kne_angle.setter + def kne_angle(self, kne_angle: float): + if not isinstance(kne_angle, float): + raise TypeError(f"geouned.Tolerances.kne_angle should be a float, not a {type(kne_angle)}") + self._kne_angle = kne_angle + + @property + def tor_distance(self): + return self._tor_distance + + @tor_distance.setter + def tor_distance(self, tor_distance: float): + if not isinstance(tor_distance, float): + raise TypeError(f"geouned.Tolerances.tor_distance should be a float, not a {type(tor_distance)}") + self._tor_distance = tor_distance + + @property + def tor_angle(self): + return self._tor_angle + + @tor_angle.setter + def tor_angle(self, tor_angle: float): + if not isinstance(tor_angle, float): + raise TypeError(f"geouned.Tolerances.tor_angle should be a float, not a {type(tor_angle)}") + self._tor_angle = tor_angle + + @property + def min_area(self): + return self._min_area + + @min_area.setter + def min_area(self, min_area: float): + if not isinstance(min_area, float): + raise TypeError(f"geouned.Tolerances.min_area should be a float, not a {type(min_area)}") + self._min_area = min_area + + +class NumericFormat: + """Numerical format options for each of the surface types. + + Args: + P_abc (str, optional): Plane general a,b,c params. Defaults to "14.7e". + P_d (str, optional): Plane general d params. Defaults to "14.7e". + P_xyz (str, optional): PX/PY/PZ params. Defaults to "14.7e". + S_r (str, optional): SO/SX/SY/SZ/S radius. Defaults to "14.7e". + S_xyz (str, optional): SO/SX/SY/SZ/S center. Defaults to "14.7e". + C_r (str, optional): Cylinder radius. Defaults to "12f". + C_xyz (str, optional): Cylinder center. Defaults to "12f". + K_xyz (str, optional): Cone apex. Defaults to "13.6e". + K_tan2 (str, optional): Cone tan^2 value. Defaults to "12f". + T_r (str, optional): Torus radii. Defaults to "14.7e". + T_xyz (str, optional): Torus center. Defaults to "14.7e". + GQ_1to6 (str, optional): GQ 1 to 6 coefficients (order 2 x2,y2,z2,xy,...). Defaults to "18.15f". + GQ_7to9 (str, optional): GQ 7 to 9 coefficients (order 1 x,y,z). Defaults to "18.15f". + GQ_10 (str, optional): GQ 10 coefficient. Defaults to "18.15f". + """ + + def __init__( + self, + P_abc: str = "14.7e", + P_d: str = "14.7e", + P_xyz: str = "14.7e", + S_r: str = "14.7e", + S_xyz: str = "14.7e", + C_r: str = "12f", + C_xyz: str = "12f", + K_xyz: str = "13.6e", + K_tan2: str = "12f", + T_r: str = "14.7e", + T_xyz: str = "14.7e", + GQ_1to6: str = "18.15f", + GQ_7to9: str = "18.15f", + GQ_10: str = "18.15f", + ): + + self.P_abc = P_abc + self.P_d = P_d + self.P_xyz = P_xyz + self.S_r = S_r + self.S_xyz = S_xyz + self.C_r = C_r + self.C_xyz = C_xyz + self.K_xyz = K_xyz + self.K_tan2 = K_tan2 + self.T_r = T_r + self.T_xyz = T_xyz + self.GQ_1to6 = GQ_1to6 + self.GQ_7to9 = GQ_7to9 + self.GQ_10 = GQ_10 + + @property + def P_abc(self): + return self._P_abc + + @P_abc.setter + def P_abc(self, P_abc: str): + if not isinstance(P_abc, str): + raise TypeError(f"geouned.Tolerances.P_abc should be a str, not a {type(P_abc)}") + self._P_abc = P_abc + + @property + def P_d(self): + return self._P_d + + @P_d.setter + def P_d(self, P_d: str): + if not isinstance(P_d, str): + raise TypeError(f"geouned.Tolerances.P_d should be a str, not a {type(P_d)}") + self._P_d = P_d + + @property + def P_xyz(self): + return self._P_xyz + + @P_xyz.setter + def P_xyz(self, P_xyz: str): + if not isinstance(P_xyz, str): + raise TypeError(f"geouned.Tolerances.P_xyz should be a str, not a {type(P_xyz)}") + self._P_xyz = P_xyz + + @property + def S_r(self): + return self._S_r + + @S_r.setter + def S_r(self, S_r: str): + if not isinstance(S_r, str): + raise TypeError(f"geouned.Tolerances.S_r should be a str, not a {type(S_r)}") + self._S_r = S_r + + @property + def S_xyz(self): + return self._S_xyz + + @S_xyz.setter + def S_xyz(self, S_xyz: str): + if not isinstance(S_xyz, str): + raise TypeError(f"geouned.Tolerances.S_xyz should be a str, not a {type(S_xyz)}") + self._S_xyz = S_xyz + + @property + def C_r(self): + return self._C_r + + @C_r.setter + def C_r(self, C_r: str): + if not isinstance(C_r, str): + raise TypeError(f"geouned.Tolerances.C_r should be a str, not a {type(C_r)}") + self._C_r = C_r + + @property + def C_xyz(self): + return self._C_xyz + + @C_xyz.setter + def C_xyz(self, C_xyz: str): + if not isinstance(C_xyz, str): + raise TypeError(f"geouned.Tolerances.C_xyz should be a str, not a {type(C_xyz)}") + self._C_xyz = C_xyz + + @property + def K_xyz(self): + return self._K_xyz + + @K_xyz.setter + def K_xyz(self, K_xyz: str): + if not isinstance(K_xyz, str): + raise TypeError(f"geouned.Tolerances.K_xyz should be a str, not a {type(K_xyz)}") + self._K_xyz = K_xyz + + @property + def K_tan2(self): + return self._K_tan2 + + @K_tan2.setter + def K_tan2(self, K_tan2: str): + if not isinstance(K_tan2, str): + raise TypeError(f"geouned.Tolerances.K_tan2 should be a str, not a {type(K_tan2)}") + self._K_tan2 = K_tan2 + + @property + def T_r(self): + return self._T_r + + @T_r.setter + def T_r(self, T_r: str): + if not isinstance(T_r, str): + raise TypeError(f"geouned.Tolerances.T_r should be a str, not a {type(T_r)}") + self._T_r = T_r + + @property + def T_xyz(self): + return self._T_xyz + + @T_xyz.setter + def T_xyz(self, T_xyz: str): + if not isinstance(T_xyz, str): + raise TypeError(f"geouned.Tolerances.T_xyz should be a str, not a {type(T_xyz)}") + self._T_xyz = T_xyz + + @property + def GQ_1to6(self): + return self._GQ_1to6 + + @GQ_1to6.setter + def GQ_1to6(self, GQ_1to6: str): + if not isinstance(GQ_1to6, str): + raise TypeError(f"geouned.Tolerances.GQ_1to6 should be a str, not a {type(GQ_1to6)}") + self._GQ_1to6 = GQ_1to6 + + @property + def GQ_7to9(self): + return self._GQ_7to9 + + @GQ_7to9.setter + def GQ_7to9(self, GQ_7to9: str): + if not isinstance(GQ_7to9, str): + raise TypeError(f"geouned.Tolerances.GQ_7to9 should be a str, not a {type(GQ_7to9)}") + self._GQ_7to9 = GQ_7to9 + + @property + def GQ_10(self): + return self._GQ_10 + + @GQ_10.setter + def GQ_10(self, GQ_10: str): + if not isinstance(GQ_10, str): + raise TypeError(f"geouned.Tolerances.GQ_10 should be a str, not a {type(GQ_10)}") + self._GQ_10 = GQ_10 + + +class Settings: + """Settings for changing the way the CAD to CSG conversion is done + + Args: + stepFile (str, optional): Name of the CAD file (in STEP format) to + be converted. Defaults to "". + matFile (str, optional): _description_. Defaults to "". + voidGen (bool, optional): Generate voids of the geometry. Defaults + to True. + debug (bool, optional): Write step files of original and decomposed + solids, for each solid in the STEP file. Defaults to False. + compSolids (bool, optional): Join subsolids of STEP file as a single + compound solid. Step files generated with SpaceClaim have not + exactly the same level of solids as FreeCAD. It may a happened + that solids defined has separated solids are read by FreeCAD + as a single compound solid (and will produce only one MCNP + cell). In this case compSolids should be set to False. Defaults + to True. + simplify (str, optional): Simplify the cell definition considering + relative surfaces position and using Boolean logics. Available + options are: "no" no optimization, "void" only void cells are + simplified. Algorithm is faster but the simplification is not + optimal. "voidfull" : only void cells are simplified with the + most optimal algorithm. The time of the conversion can be + multiplied by 5 or more. "full" : all the cells (solids and + voids) are simplified. Defaults to "No". + cellRange (list, optional): Range of cell to be converted (only one + range is allowed, e.g [100,220]). Default all solids are + converted. Defaults to []. + exportSolids (str, optional): Export CAD solid after reading. + The execution is stopped after export, the translation is not + carried out. Defaults to "". + minVoidSize (float, optional): Minimum size of the edges of the + void cell. Units are in mm. Defaults to 200.0. + maxSurf (int, optional): #TODO + maxBracket (int, optional): Maximum number of brackets (solid + complementary) allowed in void cell definition. Defaults to 30. + voidMat (list, optional): Assign a material defined by the user + instead of void for cells without material definition and the + cells generated in the automatic void generation. The format + is a 3 valued tuple (mat_label, mat_density, mat_description). + Example (100,1e-3,'Air assigned to Void'). Defaults to []. + voidExclude (list, optional): #TODO see issue 87. Defaults to []. + startCell (int, optional): Starting cell numbering label. Defaults to 1. + startSurf (int, optional): Starting surface numbering label. Defaults to 1. + sort_enclosure (bool, optional): If enclosures are defined in the + CAD models, the voids cells of the enclosure will be located in + the output file in the same location where the enclosure solid + is located in the CAD solid tree.. Defaults to False. + """ + + def __init__( + self, + matFile: str = "", + voidGen: bool = True, + debug: bool = False, + compSolids: bool = True, + simplify: str = "no", + cellRange: list = [], + exportSolids: str = "", + minVoidSize: float = 200.0, # units mm + maxSurf: int = 50, + maxBracket: int = 30, + voidMat: list = [], + voidExclude: list = [], + startCell: int = 1, + startSurf: int = 1, + sort_enclosure: bool = False, + ): + + self.matFile = matFile + self.voidGen = voidGen + self.debug = debug + self.compSolids = compSolids + self.simplify = simplify + self.cellRange = cellRange + self.exportSolids = exportSolids + self.minVoidSize = minVoidSize + self.maxSurf = maxSurf + self.maxBracket = maxBracket + self.voidMat = voidMat + self.voidExclude = voidExclude + self.startCell = startCell + self.startSurf = startSurf + self.sort_enclosure = sort_enclosure + + @property + def matFile(self): + return self._matFile + + @matFile.setter + def matFile(self, matFile: str): + if not isinstance(matFile, str): + raise TypeError(f"geouned.Tolerances.matFile should be a str, not a {type(matFile)}") + self._matFile = matFile + + @property + def voidGen(self): + return self._voidGen + + @voidGen.setter + def voidGen(self, voidGen: bool): + if not isinstance(voidGen, bool): + raise TypeError(f"geouned.Tolerances.voidGen should be a bool, not a {type(voidGen)}") + self._voidGen = voidGen + + @property + def debug(self): + return self._debug + + @debug.setter + def debug(self, debug: bool): + if not isinstance(debug, bool): + raise TypeError(f"geouned.Tolerances.debug should be a bool, not a {type(debug)}") + self._debug = debug + + @property + def compSolids(self): + return self._compSolids + + @compSolids.setter + def compSolids(self, compSolids: bool): + if not isinstance(compSolids, bool): + raise TypeError(f"geouned.Tolerances.compSolids should be a bool, not a {type(compSolids)}") + self._compSolids = compSolids + + @property + def simplify(self): + return self._simplify + + @simplify.setter + def simplify(self, simplify: str): + if not isinstance(simplify, str): + raise TypeError(f"geouned.Tolerances.simplify should be a str, not a {type(simplify)}") + self._simplify = simplify + + @property + def cellRange(self): + return self._cellRange + + @cellRange.setter + def cellRange(self, cellRange: list): + if not isinstance(cellRange, list): + raise TypeError(f"geouned.Tolerances.cellRange should be a list, not a {type(cellRange)}") + for entry in cellRange: + if not isinstance(entry, int): + raise TypeError(f"geouned.Tolerances.cellRange should be a list of ints, not a {type(entry)}") + self._cellRange = cellRange + + @property + def exportSolids(self): + return self._exportSolids + + @exportSolids.setter + def exportSolids(self, exportSolids: str): + if not isinstance(exportSolids, str): + raise TypeError(f"geouned.Tolerances.exportSolids should be a str, not a {type(exportSolids)}") + self._exportSolids = exportSolids + + @property + def minVoidSize(self): + return self._minVoidSize + + @minVoidSize.setter + def minVoidSize(self, minVoidSize: float): + if not isinstance(minVoidSize, float): + raise TypeError(f"geouned.Tolerances.minVoidSize should be a float, not a {type(minVoidSize)}") + self._minVoidSize = minVoidSize + + @property + def maxSurf(self): + return self._maxSurf + + @maxSurf.setter + def maxSurf(self, maxSurf: int): + if not isinstance(maxSurf, int): + raise TypeError(f"geouned.Tolerances.maxSurf should be a int, not a {type(maxSurf)}") + self._maxSurf = maxSurf + + @property + def maxBracket(self): + return self._maxBracket + + @maxBracket.setter + def maxBracket(self, maxBracket: int): + if not isinstance(maxBracket, int): + raise TypeError(f"geouned.Tolerances.maxBracket should be a int, not a {type(maxBracket)}") + self._maxBracket = maxBracket + + @property + def voidMat(self): + return self._voidMat + + @voidMat.setter + def voidMat(self, voidMat: list): + if not isinstance(voidMat, list): + raise TypeError(f"geouned.Tolerances.voidMat should be a list, not a {type(voidMat)}") + for entry in voidMat: + if not isinstance(entry, int): + raise TypeError(f"geouned.Tolerances.voidMat should be a list of ints, not a {type(entry)}") + self._voidMat = voidMat + + @property + def voidExclude(self): + return self._voidExclude + + @voidExclude.setter + def voidExclude(self, voidExclude: list): + if not isinstance(voidExclude, list): + raise TypeError(f"geouned.Tolerances.voidExclude should be a list, not a {type(voidExclude)}") + for entry in voidExclude: + if not isinstance(entry, int): + raise TypeError(f"geouned.Tolerances.voidExclude should be a list of ints, not a {type(entry)}") + self._voidExclude = voidExclude + + @property + def startCell(self): + return self._startCell + + @startCell.setter + def startCell(self, startCell: int): + if not isinstance(startCell, int): + raise TypeError(f"geouned.Tolerances.startCell should be a int, not a {type(startCell)}") + self._startCell = startCell + + @property + def startSurf(self): + return self._startSurf + + @startSurf.setter + def startSurf(self, startSurf: int): + if not isinstance(startSurf, int): + raise TypeError(f"geouned.Tolerances.startSurf should be a int, not a {type(startSurf)}") + self._startSurf = startSurf + + @property + def sort_enclosure(self): + return self._sort_enclosure + + @sort_enclosure.setter + def sort_enclosure(self, sort_enclosure: bool): + if not isinstance(sort_enclosure, bool): + raise TypeError(f"geouned.Tolerances.sort_enclosure should be a bool, not a {type(sort_enclosure)}") + self._sort_enclosure = sort_enclosure diff --git a/src/geouned/GEOUNED/Utils/Functions.py b/src/geouned/GEOUNED/utils/functions.py similarity index 76% rename from src/geouned/GEOUNED/Utils/Functions.py rename to src/geouned/GEOUNED/utils/functions.py index 5c87e7c8..a49b1732 100644 --- a/src/geouned/GEOUNED/Utils/Functions.py +++ b/src/geouned/GEOUNED/utils/functions.py @@ -1,6 +1,7 @@ # # Set of useful functions used in different parts of the code # +import logging import math import BOPTools.SplitAPI @@ -8,23 +9,23 @@ import numpy as np import Part -from ..Utils.BasicFunctions_part1 import ( +logger = logging.getLogger("general_logger") + +from .basic_functions_part1 import ( ConeParams, CylinderParams, Plane3PtsParams, PlaneParams, SphereParams, TorusParams, - isParallel, + is_parallel, ) -from ..Utils.Options.Classes import Options -from ..Utils.Options.Classes import Tolerances as tol -from . import BasicFunctions_part2 as BF +from . import basic_functions_part2 as BF -def getBox(comp): +def get_box(comp, options): bb = FreeCAD.BoundBox(comp.BoundBox) - bb.enlarge(Options.enlargeBox) + bb.enlarge(options.enlargeBox) xMin, yMin, zMin = bb.XMin, bb.YMin, bb.ZMin xLength, yLength, zLength = bb.XLength, bb.YLength, bb.ZLength @@ -37,7 +38,7 @@ def getBox(comp): ) -def makePlane(Plane, Boxin): +def make_plane(Plane, Boxin): normal = Plane.Surf.Axis p0 = Plane.Surf.Position.dot(normal) @@ -80,7 +81,7 @@ def makePlane(Plane, Boxin): return Part.Face(Part.makePolygon([pointEdge[p[1]] for p in orden], True)) -class GEOUNED_Solid: +class GeounedSolid: def __init__(self, id, comsolid=None): refine = True @@ -134,7 +135,7 @@ def __init__(self, id, comsolid=None): self.UniverseBox = None self.NullCell = True - def updateSolids(self, solidList): + def update_solids(self, solidList): self.Solids = solidList vol = 0 self.BoundBox = FreeCAD.BoundBox() @@ -142,18 +143,19 @@ def updateSolids(self, solidList): vol += s.Volume self.BoundBox.add(s.BoundBox) - def setCADSolid(self): + def set_cad_solid(self): self.CADSolid = Part.makeCompound(self.Solids) self.Volume = self.CADSolid.Volume self.BoundBox = self.CADSolid.BoundBox - def setUniverseBox(self, UniverseBox): + def set_universe_box(self, UniverseBox): self.UniverseBox = UniverseBox - def setSonEnclosures(self, sonEnclosures): + # TODO check this function is used in the code + def set_son_enclosures(self, sonEnclosures): self.SonEnclosures = sonEnclosures - def setDefinition(self, definition, simplify=False): + def set_definition(self, definition, simplify=False): if definition is None: self.NullCell = True @@ -167,15 +169,15 @@ def setDefinition(self, definition, simplify=False): self.NullCell = True return - self.Surfaces = tuple(self.Definition.getSurfacesNumbers()) + self.Surfaces = tuple(self.Definition.get_surfaces_numbers()) - def setFaces(self, faces): + def set_faces(self, faces): self.Faces = faces - def setComments(self, comments): + def set_comments(self, comments): self.Comments = comments - def setMaterial(self, material, rho=None, info=None): + def set_material(self, material, rho=None, info=None): self.Material = material self.Rho = rho @@ -187,12 +189,12 @@ def setMaterial(self, material, rho=None, info=None): if material != 0: self.Density = None - def setDilution(self, dilution): + def set_dilution(self, dilution): self.Dilution = dilution if self.Rho is not None: self.Density = self.Rho * dilution - def checkIntersection(self, solid, dtolerance=1.0e-6, vtolerance=1e-10): + def check_intersection(self, solid, dtolerance=1.0e-6, vtolerance=1e-10): """Check if solid intersect with current solid. return : -2 solid fully embedded in self.CADSolid ; -1 self.CADSolid fully embedded in solid ; @@ -205,9 +207,9 @@ def checkIntersection(self, solid, dtolerance=1.0e-6, vtolerance=1e-10): try: distShape = sol1.distToShape(sol2)[0] except: - print("Failed solid1.distToshape(solid2), try with inverted solids") + logger.info("Failed solid1.distToshape(solid2), try with inverted solids") distShape = sol2.distToShape(sol1)[0] - print("inverted disToShape OK", distShape) + logger.info(f"inverted disToShape OK {distShape}") dist = min(dist, distShape) if dist == 0: break @@ -226,7 +228,7 @@ def checkIntersection(self, solid, dtolerance=1.0e-6, vtolerance=1e-10): return 0 -class GEOUNED_Surface: +class GeounedSurface: def __init__(self, params, boundBox, Face=None): @@ -234,9 +236,7 @@ def __init__(self, params, boundBox, Face=None): self.__boundBox__ = boundBox if params[0] == "Plane": self.Type = "Plane" - self.Surf = PlaneParams( - params[1] - ) # plane point defined as the shortest distance to origin + self.Surf = PlaneParams(params[1]) # plane point defined as the shortest distance to origin elif params[0] == "Plane3Pts": self.Type = "Plane" self.Surf = Plane3PtsParams(params[1]) # plane point defined with 3 points @@ -257,13 +257,13 @@ def __init__(self, params, boundBox, Face=None): if type(Face) is str: if Face == "Build": - self.buildSurface() + self.build_surface() if self.shape == "Build": raise ValueError(f"stop {params} {boundBox}") return - def buildSurface(self): + def build_surface(self): if self.Type == "Plane": @@ -306,9 +306,7 @@ def buildSurface(self): orden.append((phi, i)) orden.sort() - self.shape = Part.Face( - Part.makePolygon([pointEdge[p[1]] for p in orden], True) - ) + self.shape = Part.Face(Part.makePolygon([pointEdge[p[1]] for p in orden], True)) return @@ -351,7 +349,6 @@ def buildSurface(self): else: self.shape = None return - self.shape = cone elif self.Type == "Sphere": rad = self.Surf.Radius @@ -369,11 +366,11 @@ def buildSurface(self): self.shape = torus.Faces[0] return else: - print("Error: Type {} is not defined".format(self.Type)) + logger.error(f"Type {self.Type} is not defined") return -class Surfaces_dict(dict): +class SurfacesDict(dict): def __init__(self, surfaces=None, offset=0): self.IndexOffset = offset surfname = ["PX", "PY", "PZ", "P", "Cyl", "Cone", "Sph", "Tor"] @@ -397,10 +394,10 @@ def __init__(self, surfaces=None, offset=0): def __str__(self): for key in self.keys(): - print(key, self[key]) + logger.info(f"{key}, {self[key]}") return "" - def getSurface(self, index): + def get_surface(self, index): lastKey = self.__last_obj__[0] lastInd = self.__last_obj__[1] @@ -416,93 +413,93 @@ def getSurface(self, index): self.__last_obj__ = (key, i) return self[key][i] - print("Index {} not found in Surfaces".format(index)) + logger.info(f"Index {index} not found in Surfaces") return None - def delSurface(self, index): - self.getSurface(index) + def del_surface(self, index): + self.get_surface(index) self.__surfIndex__[self.__last_obj__[0]].remove(index) del self[self.__last_obj__[0]][self.__last_obj__[1]] return - def extend(self, surface): + def extend(self, surface, options, tolerances, numeric_format): for Pkey in ["PX", "PY", "PZ", "P"]: for s in surface[Pkey]: - self.addPlane(s) + self.add_plane(s, options, tolerances, numeric_format, False) for s in surface["Cyl"]: - self.addCylinder(s) + self.add_cylinder(s, options, tolerances, numeric_format, False) for s in surface["Cone"]: - self.addCone(s) + self.add_cone(s, tolerances) for s in surface["Sph"]: - self.addSphere(s) + self.add_sphere(s, tolerances) for s in surface["Tor"]: - self.addTorus(s) + self.add_torus(s, tolerances) - def addPlane(self, plane, fuzzy=False): + def add_plane(self, plane, options, tolerances, numeric_format, fuzzy): ex = FreeCAD.Vector(1, 0, 0) ey = FreeCAD.Vector(0, 1, 0) ez = FreeCAD.Vector(0, 0, 1) - if isParallel(plane.Surf.Axis, ex, tol.pln_angle): - addPlane = True + if is_parallel(plane.Surf.Axis, ex, tolerances.pln_angle): + add_plane = True for i, p in enumerate(self["PX"]): - if BF.isSamePlane( + if BF.is_same_plane( plane.Surf, p.Surf, - dtol=tol.pln_distance, - atol=tol.pln_angle, - relTol=tol.relativeTol, + options=options, + tolerances=tolerances, + numeric_format=numeric_format, fuzzy=(fuzzy, p.Index), ): - addPlane = False + add_plane = False index = p.Index self.__last_obj__ = ("PX", i) break - if addPlane: + if add_plane: self.surfaceNumber += 1 plane.Index = self.surfaceNumber + self.IndexOffset self.__last_obj__ = ("PX", len(self["PX"])) self["PX"].append(plane) self.__surfIndex__["PX"].append(plane.Index) - elif isParallel(plane.Surf.Axis, ey, tol.pln_angle): - addPlane = True + elif is_parallel(plane.Surf.Axis, ey, tolerances.pln_angle): + add_plane = True for i, p in enumerate(self["PY"]): - if BF.isSamePlane( + if BF.is_same_plane( plane.Surf, p.Surf, - dtol=tol.pln_distance, - atol=tol.pln_angle, - relTol=tol.relativeTol, + options=options, + tolerances=tolerances, + numeric_format=numeric_format, fuzzy=(fuzzy, p.Index), ): - addPlane = False + add_plane = False index = p.Index self.__last_obj__ = ("PY", i) break - if addPlane: + if add_plane: self.surfaceNumber += 1 plane.Index = self.surfaceNumber + self.IndexOffset self.__last_obj__ = ("PY", len(self["PY"])) self["PY"].append(plane) self.__surfIndex__["PY"].append(plane.Index) - elif isParallel(plane.Surf.Axis, ez, tol.pln_angle): - addPlane = True + elif is_parallel(plane.Surf.Axis, ez, tolerances.pln_angle): + add_plane = True for i, p in enumerate(self["PZ"]): - if BF.isSamePlane( + if BF.is_same_plane( plane.Surf, p.Surf, - dtol=tol.pln_distance, - atol=tol.pln_angle, - relTol=tol.relativeTol, + options=options, + tolerances=tolerances, + numeric_format=numeric_format, fuzzy=(fuzzy, p.Index), ): - addPlane = False + add_plane = False index = p.Index self.__last_obj__ = ("PZ", i) break - if addPlane: + if add_plane: self.surfaceNumber += 1 plane.Index = self.surfaceNumber + self.IndexOffset self.__last_obj__ = ("PZ", len(self["PZ"])) @@ -510,41 +507,41 @@ def addPlane(self, plane, fuzzy=False): self.__surfIndex__["PZ"].append(plane.Index) else: - addPlane = True + add_plane = True for i, p in enumerate(self["P"]): - if BF.isSamePlane( + if BF.is_same_plane( plane.Surf, p.Surf, - dtol=tol.pln_distance, - atol=tol.pln_angle, - relTol=tol.relativeTol, + options=options, + tolerances=tolerances, + numeric_format=numeric_format, fuzzy=(fuzzy, p.Index), ): - addPlane = False + add_plane = False index = p.Index self.__last_obj__ = ("P", i) break - if addPlane: + if add_plane: self.surfaceNumber += 1 plane.Index = self.surfaceNumber + self.IndexOffset self.__last_obj__ = ("P", len(self["P"])) self["P"].append(plane) self.__surfIndex__["P"].append(plane.Index) - if addPlane: + if add_plane: return plane.Index, False else: return index, True - def addCylinder(self, cyl, fuzzy=False): + def add_cylinder(self, cyl, options, tolerances, numeric_format, fuzzy=False): addCyl = True for i, c in enumerate(self["Cyl"]): - if BF.isSameCylinder( + if BF.is_same_cylinder( cyl.Surf, c.Surf, - dtol=tol.cyl_distance, - atol=tol.cyl_angle, - relTol=tol.relativeTol, + options=options, + tolerances=tolerances, + numeric_format=numeric_format, fuzzy=(fuzzy, c.Index), ): addCyl = False @@ -562,21 +559,21 @@ def addCylinder(self, cyl, fuzzy=False): else: return index, True - def addCone(self, cone): - addCone = True + def add_cone(self, cone, tolerances): + cone_added = True for i, c in enumerate(self["Cone"]): - if BF.isSameCone( + if BF.is_same_cone( cone.Surf, c.Surf, - dtol=tol.kne_distance, - atol=tol.kne_angle, - relTol=tol.relativeTol, + dtol=tolerances.kne_distance, + atol=tolerances.kne_angle, + rel_tol=tolerances.relativeTol, ): - addCone = False + cone_added = False index = c.Index self.__last_obj__ = ("Cone", i) break - if addCone: + if cone_added: self.surfaceNumber += 1 cone.Index = self.surfaceNumber + self.IndexOffset self.__last_obj__ = ("Cone", len(self["Cone"])) @@ -586,17 +583,20 @@ def addCone(self, cone): else: return index, True - def addSphere(self, sph): - addSphere = True + def add_sphere(self, sph, tolerances): + sphere_added = True for i, s in enumerate(self["Sph"]): - if BF.isSameSphere( - sph.Surf, s.Surf, tol.sph_distance, relTol=tol.relativeTol + if BF.is_same_sphere( + sph.Surf, + s.Surf, + tolerances.sph_distance, + rel_tol=tolerances.relativeTol, ): - addSphere = False + sphere_added = False index = s.Index self.__last_obj__ = ("Sph", i) break - if addSphere: + if sphere_added: self.surfaceNumber += 1 sph.Index = self.surfaceNumber + self.IndexOffset self.__last_obj__ = ("Sph", len(self["Sph"])) @@ -606,21 +606,21 @@ def addSphere(self, sph): else: return index, True - def addTorus(self, tor): - addTorus = True + def add_torus(self, tor, tolerances): + add_torus = True for i, s in enumerate(self["Tor"]): - if BF.isSameTorus( + if BF.is_same_torus( tor.Surf, s.Surf, - dtol=tol.tor_distance, - atol=tol.tor_angle, - relTol=tol.relativeTol, + dtol=tolerances.tor_distance, + atol=tolerances.tor_angle, + rel_tol=tolerances.relativeTol, ): - addTorus = False + add_torus = False index = s.Index self.__last_obj__ = ("Tor", i) break - if addTorus: + if add_torus: self.surfaceNumber += 1 tor.Index = self.surfaceNumber + self.IndexOffset self.__last_obj__ = ("Tor", len(self["Tor"])) @@ -631,26 +631,22 @@ def addTorus(self, tor): return index, True -def splitBOP(solid, tools, tolerance, scale=0.1): +def split_bop(solid, tools, tolerance, options, scale=0.1): if tolerance >= 0.1: compSolid = BOPTools.SplitAPI.slice(solid, tools, "Split", tolerance=tolerance) elif tolerance < 1e-12: - if Options.scaleUp: - tol = 1e-13 if Options.splitTolerance == 0 else Options.splitTolerance - compSolid = splitBOP(solid, tools, tol / scale, 1.0 / scale) + if options.scaleUp: + tol = 1e-13 if options.splitTolerance == 0 else options.splitTolerance + compSolid = split_bop(solid, tools, tol / scale, options, 1.0 / scale) else: - compSolid = BOPTools.SplitAPI.slice( - solid, tools, "Split", tolerance=tolerance - ) + compSolid = BOPTools.SplitAPI.slice(solid, tools, "Split", tolerance=tolerance) else: try: - compSolid = BOPTools.SplitAPI.slice( - solid, tools, "Split", tolerance=tolerance - ) + compSolid = BOPTools.SplitAPI.slice(solid, tools, "Split", tolerance=tolerance) except: - compSolid = splitBOP(solid, tools, tolerance * scale, scale) + compSolid = split_bop(solid, tools, tolerance * scale, options, scale) return compSolid diff --git a/src/geouned/GEOUNED/Utils/Geometry_GU.py b/src/geouned/GEOUNED/utils/geometry_gu.py similarity index 62% rename from src/geouned/GEOUNED/Utils/Geometry_GU.py rename to src/geouned/GEOUNED/utils/geometry_gu.py index 8d7945bb..ef0c9515 100644 --- a/src/geouned/GEOUNED/Utils/Geometry_GU.py +++ b/src/geouned/GEOUNED/utils/geometry_gu.py @@ -1,20 +1,26 @@ # # definition of GEOUNED objects to release memory # +# GEOUNED SurfacesGU, SolidsGU, PlaneGU, etc.. objects are created because FreeCAD create a new object +# each time an attribute of FreeCAD object is called. This leads to code crash with memory failure +# when attribues are call large amount of times. Like it is in this code. + +import logging import math import Part -from ..Utils.Options.Classes import Tolerances as tol -from .BasicFunctions_part1 import isSameValue -from .BasicFunctions_part2 import isSameTorus +from .basic_functions_part1 import is_same_value +from .basic_functions_part2 import is_same_torus + +logger = logging.getLogger("general_logger") # SURFACES -class Surfaces_GU(object): +class SurfacesGu(object): + """GEOUNED surface class""" def __init__(self, face): - self.face = face self.Surface = self.face.Surface self.type = str(self.Surface) @@ -25,11 +31,11 @@ def __str__(self): return self.type -class Plane_GU(Surfaces_GU): +class PlaneGu(SurfacesGu): + """GEOUNED Plane Class""" def __init__(self, face, plane3Pts=False): - - Surfaces_GU.__init__(self, face) + SurfacesGu.__init__(self, face) self.Axis = face.Surface.Axis self.Position = face.Surface.Position self.pointDef = plane3Pts @@ -45,22 +51,22 @@ def __init__(self, face, plane3Pts=False): self.dim2 = face.ParameterRange[3] - face.ParameterRange[2] -class Cylinder_GU(Surfaces_GU): +class CylinderGu(SurfacesGu): + """GEOUNED Cylinder Class""" def __init__(self, face): - - Surfaces_GU.__init__(self, face) + SurfacesGu.__init__(self, face) self.Axis = face.Surface.Axis self.Radius = face.Surface.Radius self.Center = face.Surface.Center self.dimL = face.ParameterRange[3] - face.ParameterRange[2] -class Cone_GU(Surfaces_GU): +class ConeGu(SurfacesGu): + """GEOUNED Cone Class""" def __init__(self, face): - - Surfaces_GU.__init__(self, face) + SurfacesGu.__init__(self, face) self.Axis = face.Surface.Axis self.Apex = face.Surface.Apex self.SemiAngle = face.Surface.SemiAngle @@ -69,33 +75,35 @@ def __init__(self, face): self.Radius = face.Surface.Radius -class Sphere_GU(Surfaces_GU): +class SphereGu(SurfacesGu): + """GEOUNED Sphere Class""" def __init__(self, face): - - Surfaces_GU.__init__(self, face) + SurfacesGu.__init__(self, face) self.type = self.type[0:6] self.Center = face.Surface.Center self.Radius = face.Surface.Radius -class Torus_GU(Surfaces_GU): +class TorusGu(SurfacesGu): + """GEOUNED Torus Class""" def __init__(self, face): - - Surfaces_GU.__init__(self, face) + SurfacesGu.__init__(self, face) self.Center = face.Surface.Center self.Axis = face.Surface.Axis self.MajorRadius = face.Surface.MajorRadius self.MinorRadius = face.Surface.MinorRadius -class solid_GU: +class SolidGu: + """GEOUNED Solid Class""" - def __init__(self, solid, plane3Pts=False): + def __init__(self, solid, tolerances, plane3Pts=False): self.solid = solid - faces = DefineListFace_GU(solid.Faces, plane3Pts) + faces = define_list_face_gu(solid.Faces, plane3Pts) self.Faces = faces + self.tolerances = tolerances self.Solids = solid.Solids self.BoundBox = solid.BoundBox self.Edges = solid.Edges @@ -109,39 +117,38 @@ def __init__(self, solid, plane3Pts=False): toroidIndex.append(i) if len(toroidIndex) != 0: - tFaces = self.__sameTorusSurf__(toroidIndex) + tFaces = self.__same_torus_surf__(toroidIndex) for i, tSet in enumerate(tFaces): - URange = self.__mergePeriodicUV__("U", tSet) - VRange = self.__mergePeriodicUV__("V", tSet) + URange = self.__merge_periodic_uv__("U", tSet) + VRange = self.__merge_periodic_uv__("V", tSet) for t in tSet: self.TorusVParams[t] = (i, VRange) self.TorusUParams[t] = (i, URange) - def __sameTorusSurf__(self, torusList): - - # group identical faces + def __same_torus_surf__(self, torusList): + """group as a single face all the neighbour faces of the same torus""" sameTorusFace = [] temp = torusList[:] while len(temp) > 0: i = temp[0] current = [i] for j in temp[1:]: - if isSameTorus( + if is_same_torus( self.Faces[i].Surface, self.Faces[j].Surface, - dtol=tol.tor_distance, - atol=tol.tor_angle, - relTol=tol.relativeTol, + dtol=self.tolerances.tor_distance, + atol=self.tolerances.tor_angle, + rel_tol=self.tolerances.relativeTol, ): current.append(j) for c in current: temp.remove(c) sameTorusFace.append(current) - return self.__separateSurfaces__(sameTorusFace) + return self.__separate_surfaces__(sameTorusFace) - def __separateSurfaces__(self, faceList): - # group all faces forming a continuous surface + def __separate_surfaces__(self, faceList): + """group all faces in faceList forming a continuous surface""" sameSurfaces = [] for tset in faceList: temp = tset[:] @@ -151,10 +158,7 @@ def __separateSurfaces__(self, faceList): removeList = [temp[0]] while len(temp) > 0 and i < len(current): for tindex in temp: - if ( - self.Faces[current[i]].distToShape(self.Faces[tindex])[0] - < tol.distance - ): + if self.Faces[current[i]].distToShape(self.Faces[tindex])[0] < self.tolerances.distance: if tindex not in current: current.append(tindex) removeList.append(tindex) @@ -166,8 +170,8 @@ def __separateSurfaces__(self, faceList): sameSurfaces.append(current) return sameSurfaces - def __mergeNoPeriodicUV__(self, parameter, faceList): - + # TODO check if this function is used as it appears to be nut used in the code + def __merge_no_periodic_uv__(self, parameter, faceList): if parameter == "U": i1 = 0 i2 = 2 @@ -175,17 +179,17 @@ def __mergeNoPeriodicUV__(self, parameter, faceList): i1 = 2 i2 = 4 - Vmin, Vmax = self.Faces[faceList[0]].ParameterRange[i1:i2] + v_min, v_max = self.Faces[faceList[0]].ParameterRange[i1:i2] for face in faceList[1:]: V0, V1 = self.Faces[face].ParameterRange[i1:i2] - Vmin = min(Vmin, V0) - Vmax = max(Vmax, V1) - mergedParams = (False, (Vmin, Vmax)) + v_min = min(v_min, V0) + v_max = max(v_max, V1) + mergedParams = (False, (v_min, v_max)) return mergedParams - def __mergePeriodicUV__(self, parameter, faceList): - twoPi = 2.0 * math.pi + def __merge_periodic_uv__(self, parameter, faceList): + two_pi = 2.0 * math.pi if parameter == "U": i1 = 0 i2 = 2 @@ -203,35 +207,37 @@ def __mergePeriodicUV__(self, parameter, faceList): params.sort() V0 = params[0][0] V1 = params[-1][1] - if arcLength >= twoPi * (1.0 - tol.relativePrecision): - mergedParams = (True, (V0, V0 + twoPi)) + if arcLength >= two_pi * (1.0 - self.tolerances.relativePrecision): + mergedParams = (True, (V0, V0 + two_pi)) else: - if isSameValue(V0, 0.0, tol.relativePrecision) and isSameValue( - V1, twoPi, tol.relativePrecision + if is_same_value(V0, 0.0, self.tolerances.relativePrecision) and is_same_value( + V1, two_pi, self.tolerances.relativePrecision ): for i in range(len(params) - 1): - if not isSameValue( - params[i][1], params[i + 1][0], tol.relativePrecision + if not is_same_value( + params[i][1], + params[i + 1][0], + self.tolerances.relativePrecision, ): break - Vmin = params[i + 1][0] - twoPi - Vmax = params[i][1] + v_min = params[i + 1][0] - two_pi + v_max = params[i][1] else: - Vmin = params[0][0] - Vmax = params[-1][1] - mergedParams = (False, (Vmin, Vmax)) + v_min = params[0][0] + v_max = params[-1][1] + mergedParams = (False, (v_min, v_max)) return mergedParams # FACES -class Faces_GU(object): +class FaceGu(object): + """GEOUNED Face Class""" + def __init__(self, face, Plane3Pts=False): # GEOUNED based atributes self.__face__ = face - self.Surface = DefineSurface( - face, Plane3Pts - ) # Define the appropiate GU Surface of the face + self.Surface = define_surface(face, Plane3Pts) # Define the appropiate GU Surface of the face # FreeCAD based Atributes self.Area = face.Area @@ -266,7 +272,6 @@ def distToShape(self, shape): if shape1 is shape2: return (0,) else: - try: dist2Shape = shape1.distToShape(shape2) except: @@ -275,31 +280,32 @@ def distToShape(self, shape): # Aux functions -def DefineListFace_GU(face_list, plane3Pts=False): +def define_list_face_gu(face_list, plane3Pts=False): """Return the list of the corresponding Face_GU object of a FaceList""" - return tuple(Faces_GU(face, plane3Pts) for face in face_list) + return tuple(FaceGu(face, plane3Pts) for face in face_list) -def DefineSurface(face, plane3Pts): +def define_surface(face, plane3Pts): kind_surf = str(face.Surface) if kind_surf == "": - Surf_GU = Plane_GU(face, plane3Pts) + Surf_GU = PlaneGu(face, plane3Pts) elif kind_surf == "": - Surf_GU = Cylinder_GU(face) + Surf_GU = CylinderGu(face) elif kind_surf == "": - Surf_GU = Cone_GU(face) + Surf_GU = ConeGu(face) elif kind_surf[0:6] == "Sphere": - Surf_GU = Sphere_GU(face) + Surf_GU = SphereGu(face) elif kind_surf == "": - Surf_GU = Torus_GU(face) + Surf_GU = TorusGu(face) else: - print("bad Surface type", kind_surf) + logger.info(f"bad Surface type {kind_surf}") Surf_GU = None return Surf_GU -def ListSurfaces(Surfaces): +# TODO check if this function is being used as it doesn't appear to be used in the code +def list_surfaces(Surfaces): Faces = [] for elem in Surfaces: - Faces.extend(DefineSurface(face) for face in elem) + Faces.extend(define_surface(face) for face in elem) return Faces diff --git a/src/geouned/GEOUNED/utils/log_utils.py b/src/geouned/GEOUNED/utils/log_utils.py new file mode 100644 index 00000000..ef109f37 --- /dev/null +++ b/src/geouned/GEOUNED/utils/log_utils.py @@ -0,0 +1,16 @@ +import logging + +formatter = logging.Formatter("%(asctime)s :: %(levelname)s :: %(funcName)s :: %(lineno)d :: %(message)s") + + +def setup_logger(name, log_file, level=logging.DEBUG): + """To setup as many loggers as you want""" + + handler = logging.FileHandler(log_file) + handler.setFormatter(formatter) + + logger = logging.getLogger(name) + logger.setLevel(level) + logger.addHandler(handler) + + return logger diff --git a/src/geouned/GEOUNED/Utils/Qform.py b/src/geouned/GEOUNED/utils/q_form.py similarity index 90% rename from src/geouned/GEOUNED/Utils/Qform.py rename to src/geouned/GEOUNED/utils/q_form.py index 006139e1..58f26edb 100644 --- a/src/geouned/GEOUNED/Utils/Qform.py +++ b/src/geouned/GEOUNED/utils/q_form.py @@ -7,10 +7,9 @@ import math import FreeCAD -import Part -def RotationMatrix(u, v): +def rotation_matrix(u, v): """Definition of the rotation matrix for two vectors""" # defintion of the axis of rotation @@ -19,8 +18,7 @@ def RotationMatrix(u, v): u.normalize() v.normalize() - # print(u, v) - # print(u.Length, v.Length, Axis.Length) + cose = u.dot(v) seno = u.cross(v).Length @@ -48,7 +46,8 @@ def RotationMatrix(u, v): return R -def RotationMatrixAngleAxis(u, angle): +# TODO check if this is being used +def rotation_matrix_angle_axis(u, angle): """Definition of the rotation matrix for an angle and the rotation axis""" # defintion of the exis of rotation @@ -80,9 +79,9 @@ def RotationMatrixAngleAxis(u, angle): return R -def QFormCyl(Axis, Pos, rad): +def q_form_cyl(Axis, Pos, rad): - R = RotationMatrix(FreeCAD.Vector(1, 0, 0), Axis) + R = rotation_matrix(FreeCAD.Vector(1, 0, 0), Axis) R.transpose() Pos2 = R.multiply(Pos).negative() @@ -103,9 +102,9 @@ def QFormCyl(Axis, Pos, rad): return (A, B, C, D, E, F, G, H, J, K) -def QFormCone(Axis, Pos, tan): +def q_form_cone(Axis, Pos, tan): - R = RotationMatrix(FreeCAD.Vector(1, 0, 0), Axis) + R = rotation_matrix(FreeCAD.Vector(1, 0, 0), Axis) R.transpose() Pos2 = R.multiply(Pos).negative() diff --git a/src/geouned/GEOUNED/void/__init__.py b/src/geouned/GEOUNED/void/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/geouned/GEOUNED/void/void.py b/src/geouned/GEOUNED/void/void.py new file mode 100644 index 00000000..4d438ad1 --- /dev/null +++ b/src/geouned/GEOUNED/void/void.py @@ -0,0 +1,236 @@ +import logging + +import FreeCAD +import Part +from tqdm import tqdm + +from ..loadfile import load_functions as LF +from ..utils.basic_functions_part1 import is_opposite +from ..utils.boolean_function import BoolSequence +from ..utils.functions import GeounedSolid, GeounedSurface +from . import void_functions as VF +from .void_box_class import VoidBox + +logger = logging.getLogger("general_logger") + + +def void_generation( + MetaList, + EnclosureList, + Surfaces, + UniverseBox, + setting, + init, + options, + tolerances, + numeric_format, +): + voidList = [] + + if EnclosureList: + NestedEnclosure = LF.set_enclosure_levels(EnclosureList) + VF.assignEnclosure(MetaList, NestedEnclosure) + + # add to Metalist Level 1 enclosures, remove from list material cells totally embedded in Level 1 enclosures + newMetaList = VF.select_solids(MetaList, NestedEnclosure[0], UniverseBox) + else: + newMetaList = MetaList[:] + NestedEnclosure = [] + + Box = Part.makeBox( + UniverseBox.XLength, + UniverseBox.YLength, + UniverseBox.ZLength, + FreeCAD.Vector(UniverseBox.XMin, UniverseBox.YMin, UniverseBox.ZMin), + FreeCAD.Vector(0, 0, 1), + ) + + EnclosureBox = GeounedSolid(None, Box) + if setting.voidMat: + voidMat = setting.voidMat + EnclosureBox.set_material(voidMat[0], voidMat[1], voidMat[2]) + + # get voids in 0 Level Enclosure (original Universe) + # if exist Level 1 enclosures are considered as material cells + logger.info("Build Void highest enclosure") + + voids = get_void_def( + newMetaList, + Surfaces, + EnclosureBox, + setting, + options, + tolerances, + numeric_format, + Lev0=True, + ) + voidList.append(voids) + + # Perform enclosure void + # Loop until the lowest enclosure level + + for i, Level in enumerate(tqdm(NestedEnclosure, desc="Void generation")): + + logger.info("Build Void highest enclosure") + for j, encl in enumerate(Level): + if encl.CellType == "envelope": + continue + newMetaList = VF.select_solids(MetaList, encl.SonEnclosures, encl) + logger.info(f"Build Void enclosure {j} in enclosure level {i + 1}") + # select solids overlapping current enclosure "encl", and lower level enclosures + voids = get_void_def( + newMetaList, + Surfaces, + encl, + setting, + options, + tolerances, + numeric_format, + ) + voidList.append(voids) + + voidList.append(set_graveyard_cell(Surfaces, UniverseBox, options, tolerances, numeric_format)) + + return VF.update_void_list(init, voidList, NestedEnclosure, setting.sort_enclosure) + + +def get_void_def( + MetaList, + Surfaces, + Enclosure, + setting, + options, + tolerances, + numeric_format, + Lev0=False, +): + + maxsurf = setting.maxSurf + maxbracket = setting.maxBracket + minSize = setting.minVoidSize + + if "full" in setting.simplify.lower(): + simplifyVoid = "full" + elif "void" in setting.simplify.lower(): + simplifyVoid = "diag" + else: + simplifyVoid = "no" + + if Lev0: + Universe = VoidBox(MetaList, Enclosure.BoundBox) + else: + Universe = VoidBox(MetaList, Enclosure.CADSolid) + + Initial = [Universe] + VoidDef = [] + iloop = 0 + while iloop < 50: + Temp = [] + iloop += 1 + nvoid = len(Initial) + logger.info("Loop, Box to Split :{iloop}, {nvoid}") + + for iz, z in enumerate(Initial): + nsurfaces, nbrackets = z.get_numbers() + logger.info(f"{iloop} {iz + 1}/{nvoid} {nsurfaces} {nbrackets}") + + if nsurfaces > maxsurf and nbrackets > maxbracket: + newspace = z.split(minSize) + else: + newspace = None + + if type(newspace) is tuple: + Temp.extend(newspace) + else: + # if len(z.Objects) >= 50 : z.refine() + boxDim = ( + z.BoundBox.XMin * 0.1, + z.BoundBox.XMax * 0.1, + z.BoundBox.YMin * 0.1, + z.BoundBox.YMax * 0.1, + z.BoundBox.ZMin * 0.1, + z.BoundBox.ZMax * 0.1, + ) + + logger.info(f"build complementary {iloop} {iz}") + + cell, CellIn = z.get_void_complementary(Surfaces, options, tolerances, numeric_format, simplify=simplifyVoid) + if cell is not None: + VoidCell = (cell, (boxDim, CellIn)) + VoidDef.append(VoidCell) + + Initial = Temp + if len(Temp) == 0: + break + + voidList = [] + for i, vcell in enumerate(VoidDef): + mVoid = GeounedSolid(i) + mVoid.Void = True + mVoid.CellType = "void" + mVoid.set_definition(vcell[0], simplify=True) + mVoid.set_material(Enclosure.Material, Enclosure.Rho, Enclosure.MatInfo) + mVoid.set_dilution(Enclosure.Dilution) + + mVoid.__commentInfo__ = vcell[1] + + voidList.append(mVoid) + + return voidList + + +def set_graveyard_cell(Surfaces, UniverseBox, options, tolerances, numeric_format): + Universe = VoidBox([], UniverseBox) + + externalBox = get_universe_complementary(Universe, Surfaces, options, tolerances, numeric_format) + center = UniverseBox.Center + radius = 0.51 * UniverseBox.DiagonalLength + sphere = GeounedSurface(("Sphere", (center, radius)), UniverseBox) + id, exist = Surfaces.add_sphere(sphere, tolerances) + + sphdef = BoolSequence(str(-id)) + sphdef.operator = "AND" + sphdef.append(externalBox) + + notsph = BoolSequence(str(id)) + + mVoidSphIn = GeounedSolid(0) + mVoidSphIn.Void = True + mVoidSphIn.CellType = "void" + mVoidSphIn.set_definition(sphdef) + mVoidSphIn.set_material(0, 0, "Graveyard_in") + mVoidSphIn.__commentInfo__ = None + + mVoidSphOut = GeounedSolid(1) + mVoidSphOut.Void = True + mVoidSphOut.CellType = "void" + mVoidSphOut.set_definition(notsph) + mVoidSphOut.set_material(0, 0, "Graveyard") + mVoidSphOut.__commentInfo__ = None + + return (mVoidSphIn, mVoidSphOut) + + +# TODO check this is being used +def get_universe_complementary(Universe, Surfaces, options, tolerances, numeric_format): + Def = BoolSequence(operator="OR") + for p in Universe.get_bound_planes(): + id, exist = Surfaces.add_plane(p, options, tolerances, numeric_format, False) + if not exist: + Def.elements.append(-id) + else: + s = Surfaces.get_surface(id) + if is_opposite(p.Surf.Axis, s.Surf.Axis): + Def.elements.append(id) + else: + Def.elements.append(-id) + return Def + + +def void_comment_line(CellInfo): + boxDef, cellIn = CellInfo + cells = ", ".join(map(str, cellIn)) + box = ", ".join(f"{num:.3f}" for num in boxDef) + line = f"Automatic Generated Void Cell. Enclosure({box})\n" + line += f"Enclosed cells : ({cells})\n" + return line diff --git a/src/geouned/GEOUNED/Void/VoidBoxClass.py b/src/geouned/GEOUNED/void/void_box_class.py similarity index 76% rename from src/geouned/GEOUNED/Void/VoidBoxClass.py rename to src/geouned/GEOUNED/void/void_box_class.py index f4640ea2..038639c9 100644 --- a/src/geouned/GEOUNED/Void/VoidBoxClass.py +++ b/src/geouned/GEOUNED/void/void_box_class.py @@ -1,15 +1,18 @@ """File with the VoidBox class""" +import logging + import FreeCAD import Part -from ..Conversion import CellDefinition as Conv -from ..Decompose import Decom_one as Decom -from ..Utils.BasicFunctions_part1 import isOposite -from ..Utils.booleanFunction import BoolSequence -from ..Utils.BooleanSolids import buildCTableFromSolids, removeExtraSurfaces -from ..Utils.Functions import GEOUNED_Solid, GEOUNED_Surface -from ..Utils.Options.Classes import Options as opt +from ..conversion import cell_definition as Conv +from ..decompose import decom_one as Decom +from ..utils.basic_functions_part1 import is_opposite +from ..utils.boolean_function import BoolSequence +from ..utils.boolean_solids import build_c_table_from_solids, remove_extra_surfaces +from ..utils.functions import GeounedSolid, GeounedSurface + +logger = logging.getLogger("general_logger") class VoidBox: @@ -28,12 +31,12 @@ def __init__(self, MetaSolids, Enclosure): continue if m.BoundBox.isValid(): if self.BoundBox.intersect(m.BoundBox): - Obj = self.__copyMeta__(m) - self.__removeExtraComp__(Obj, self.BoundBox) + Obj = self.__copy_meta__(m) + self.__remove_extra_comp__(Obj, self.BoundBox) self.Objects.append(Obj) return - def Split(self, minSize=200): + def split(self, minSize=200): dims = [self.BoundBox.XLength, self.BoundBox.YLength, self.BoundBox.ZLength] coord = ["X", "Y", "Z"] @@ -99,24 +102,24 @@ def Split(self, minSize=200): box1 = FreeCAD.BoundBox(VMin1, VMax1) box2 = FreeCAD.BoundBox(VMin2, VMax2) - if self.PieceEnclosure == None: + if self.PieceEnclosure is None: Space1 = VoidBox(self.Objects, box1) Space2 = VoidBox(self.Objects, box2) VoidBoxTuple = (Space1, Space2) else: - Space1 = self.PieceEnclosureSplit(box1) - Space2 = self.PieceEnclosureSplit(box2) + Space1 = self.piece_enclosure_split(box1) + Space2 = self.piece_enclosure_split(box2) VoidBoxTuple = (Space1, Space2) - if Space1 == None: + if Space1 is None: VoidBoxTuple = (Space2,) - if Space2 == None: + if Space2 is None: VoidBoxTuple = (Space1,) - if Space1 == None and Space2 == None: + if Space1 is None and Space2 is None: VoidBoxTuple = () return VoidBoxTuple - def PieceEnclosureSplit(self, Box, Tolerance=1.0e-13): + def piece_enclosure_split(self, Box, Tolerance=1.0e-13): """This function creates a box-shaped solid with the new limits of given bounding box and it is intersected with the piece of nested enclosure to create the new void cell. If the limited region does not intersect with the piece, no void cell is created. @@ -158,43 +161,65 @@ def refine(self): ) for m in self.Objects: - self.__removeExtraComp__(m, Cube, mode="dist") + self.__remove_extra_comp__(m, Cube, mode="dist") return - def getVoidComplementary(self, Surfaces, simplify="no"): + def get_void_complementary(self, Surfaces, options, tolerances, numeric_format, simplify="no"): if self.PieceEnclosure is None: boxDef = BoolSequence(operator="AND") center = self.BoundBox.Center bBox = self.BoundBox - for p in self.getBoundPlanes(): - id, exist = Surfaces.addPlane(p) + for p in self.get_bound_planes(): + id, exist = Surfaces.add_plane(p, options, tolerances, numeric_format, False) if exist: - s = Surfaces.getSurface(id) - if isOposite(p.Surf.Axis, s.Surf.Axis): + s = Surfaces.get_surface(id) + if is_opposite(p.Surf.Axis, s.Surf.Axis): id = -id - if isOposite(p.Surf.Axis, p.Surf.Position - center): + if is_opposite(p.Surf.Axis, p.Surf.Position - center): boxDef.elements.append(id) else: boxDef.elements.append(-id) enclosure = False - d = opt.enlargeBox + d = options.enlargeBox else: UniverseBox = self.PieceEnclosure.BoundBox - TempPieceEnclosure = GEOUNED_Solid(None, self.PieceEnclosure) + TempPieceEnclosure = GeounedSolid(None, self.PieceEnclosure) comsolid, err = Decom.SplitSolid( - Part.makeCompound(TempPieceEnclosure.Solids), UniverseBox + Part.makeCompound(TempPieceEnclosure.Solids), + UniverseBox, + options, + tolerances, + numeric_format, ) Surfaces.extend( - Decom.ExtractSurfaces(comsolid, "All", UniverseBox, MakeObj=True) + Decom.extract_surfaces( + comsolid, + "All", + UniverseBox, + options, + tolerances, + numeric_format, + MakeObj=True, + ), + options, + tolerances, + numeric_format, + ) + TempPieceEnclosure.update_solids(comsolid.Solids) + Conv.cellDef( + TempPieceEnclosure, + Surfaces, + UniverseBox, + options, + tolerances, + numeric_format, ) - TempPieceEnclosure.updateSolids(comsolid.Solids) - Conv.cellDef(TempPieceEnclosure, Surfaces, UniverseBox) boxDef = TempPieceEnclosure.Definition bBox = self.PieceEnclosure.BoundBox enclosure = True - d = max(opt.enlargeBox, 2) + d = max(options.enlargeBox, 2) Box = Part.makeBox( bBox.XLength + 2 * d, @@ -213,7 +238,7 @@ def getVoidComplementary(self, Surfaces, simplify="no"): continue cellIn.append(m.__id__) - # voidSolidDef.joinOperators() + # voidSolidDef.join_operators() if not voidSolidDef.elements: return ( @@ -228,10 +253,10 @@ def getVoidComplementary(self, Surfaces, simplify="no"): complementary = BoolSequence(operator="AND") complementary.append(boxDef) if simplify != "no": - surfList = voidSolidDef.getSurfacesNumbers() + surfList = voidSolidDef.get_surfaces_numbers() if enclosure: - surfList.update(boxDef.getSurfacesNumbers()) + surfList.update(boxDef.get_surfaces_numbers()) else: for s in boxDef.elements: val = s > 0 @@ -245,10 +270,10 @@ def getVoidComplementary(self, Surfaces, simplify="no"): if enclosure or res is None: surfaceDict = {} for i in surfList: - surfaceDict[i] = Surfaces.getSurface(i) - CTable = buildCTableFromSolids(Box, surfaceDict, option=simplify) + surfaceDict[i] = Surfaces.get_surface(i) + CTable = build_c_table_from_solids(Box, surfaceDict, option=simplify, options=options) else: - if res == True: + if res is True: return None, None else: return boxDef, None @@ -263,10 +288,10 @@ def getVoidComplementary(self, Surfaces, simplify="no"): voidSolidDef = cellVoid for solDef in voidSolidDef.elements: - newSolid = removeExtraSurfaces(solDef, CTable) + newSolid = remove_extra_surfaces(solDef, CTable) if type(newSolid.elements) is not bool: newTemp.append(newSolid) - elif newSolid.elements == True: + elif newSolid.elements is True: return None, None voidSolidDef = newTemp @@ -279,13 +304,13 @@ def getVoidComplementary(self, Surfaces, simplify="no"): cellVoid.append(voidSolidDef) voidSolidDef = cellVoid - if voidSolidDef.elements == True: + if voidSolidDef.elements is True: return None, None - elif voidSolidDef.elements == False or voidSolidDef.elements == []: + elif voidSolidDef.elements is False or voidSolidDef.elements == []: return boxDef, None if voidSolidDef.level == 0: - compSeq = voidSolidDef.getComplementary() + compSeq = voidSolidDef.get_complementary() else: if voidSolidDef.level == 1 and voidSolidDef.operator == "AND": @@ -302,16 +327,15 @@ def getVoidComplementary(self, Surfaces, simplify="no"): chk = None # solid in cover full Void cell volume => Void cell doesn't exist - if chk == True: - if opt.verbose: - print("warning void Cell should not exist") + if chk is True: + logger.warning("void Cell should not exist") return None, None # solid cell is not in void cell Void cell volume => doesn't contribute to void definition - elif chk == False: + elif chk is False: continue - pmoc = comp.getComplementary() + pmoc = comp.get_complementary() compSeq.append(pmoc) if simplify == "full": @@ -327,17 +351,18 @@ def getVoidComplementary(self, Surfaces, simplify="no"): complementary.append(compSeq) complementary.clean() - complementary.levelUpdate() + complementary.level_update() if type(complementary.elements) is bool: return None, None else: return complementary, cellIn - def getBoxNumber(self): + # TODO check this is used in the code + def get_box_number(self): return len(self.Objects) - def getNumbers(self): + def get_numbers(self): ns = 0 nb = 0 @@ -347,14 +372,14 @@ def getNumbers(self): return ns, nb - def getBoundPlanes(self): + def get_bound_planes(self): Xmid = 0.5 * (self.BoundBox.XMin + self.BoundBox.XMax) Ymid = 0.5 * (self.BoundBox.YMin + self.BoundBox.YMax) Zmid = 0.5 * (self.BoundBox.ZMin + self.BoundBox.ZMax) LX = self.BoundBox.ZMin + self.BoundBox.XLength LY = self.BoundBox.ZMin + self.BoundBox.YLength LZ = self.BoundBox.ZMin + self.BoundBox.ZLength - PXMin = GEOUNED_Surface( + PXMin = GeounedSurface( ( "Plane", ( @@ -366,7 +391,7 @@ def getBoundPlanes(self): ), self.BoundBox, ) - PXMax = GEOUNED_Surface( + PXMax = GeounedSurface( ( "Plane", ( @@ -378,7 +403,7 @@ def getBoundPlanes(self): ), self.BoundBox, ) - PYMin = GEOUNED_Surface( + PYMin = GeounedSurface( ( "Plane", ( @@ -390,7 +415,7 @@ def getBoundPlanes(self): ), self.BoundBox, ) - PYMax = GEOUNED_Surface( + PYMax = GeounedSurface( ( "Plane", ( @@ -402,7 +427,7 @@ def getBoundPlanes(self): ), self.BoundBox, ) - PZMin = GEOUNED_Surface( + PZMin = GeounedSurface( ( "Plane", ( @@ -414,7 +439,7 @@ def getBoundPlanes(self): ), self.BoundBox, ) - PZMax = GEOUNED_Surface( + PZMax = GeounedSurface( ( "Plane", ( @@ -429,7 +454,7 @@ def getBoundPlanes(self): return (PXMin, PXMax, PYMin, PYMax, PZMin, PZMax) - def __removeExtraComp__(self, Obj, Box, mode="box"): + def __remove_extra_comp__(self, Obj, Box, mode="box"): reducedSol = [] reducedDef = BoolSequence(operator="OR") if not Obj.Solids: @@ -451,16 +476,16 @@ def __removeExtraComp__(self, Obj, Box, mode="box"): reducedDef.append(Obj.Definition.elements[i]) if len(reducedSol) < len(Obj.Solids): - Obj.updateSolids(reducedSol) - Obj.setDefinition(reducedDef) + Obj.update_solids(reducedSol) + Obj.set_definition(reducedDef) return - def __copyMeta__(self, m): + def __copy_meta__(self, m): solidsCopy = m.Solids[:] facesCopy = m.Faces[:] - Meta = GEOUNED_Solid(m.__id__, solidsCopy) - Meta.setDefinition(m.Definition.copy()) - Meta.setFaces(facesCopy) + Meta = GeounedSolid(m.__id__, solidsCopy) + Meta.set_definition(m.Definition.copy()) + Meta.set_faces(facesCopy) if m.IsEnclosure: Meta.IsEnclosure = True Meta.EnclosureID = m.EnclosureID diff --git a/src/geouned/GEOUNED/Void/voidFunctions.py b/src/geouned/GEOUNED/void/void_functions.py similarity index 84% rename from src/geouned/GEOUNED/Void/voidFunctions.py rename to src/geouned/GEOUNED/void/void_functions.py index b64bfaf6..ea552033 100644 --- a/src/geouned/GEOUNED/Void/voidFunctions.py +++ b/src/geouned/GEOUNED/void/void_functions.py @@ -8,7 +8,7 @@ def assignEnclosure(MetaList, NestedEnclosure): outside = True for lev, Level in enumerate(reversed(NestedEnclosure)): for encl in Level: - inter = m.checkIntersection(encl.CADSolid) + inter = m.check_intersection(encl.CADSolid) # CAD solid intersect level i enclosure -> end of nested loops # Current loop continue because solid can intersect various enclosure if inter == 0: @@ -38,7 +38,7 @@ def assignEnclosure(MetaList, NestedEnclosure): return -def selectSolids(MetaList, LowLevelEnclosure, Enclosure): +def select_solids(MetaList, LowLevelEnclosure, Enclosure): """This function selects the CAD solids and nested enclosures that can intersect with the current nested enclosure. For intermediate Levels selected solids are those intersecting i and i+1 level enclosure, @@ -60,19 +60,19 @@ def selectSolids(MetaList, LowLevelEnclosure, Enclosure): return newMetaList -def updateVoidList(offset, voidList, NestedEnclosure, sortEnclosure): +def update_void_list(offset, voidList, NestedEnclosure, sort_enclosure): newVoidList = [] if NestedEnclosure: - updateComment = True + update_comment = True else: - updateComment = False + update_comment = False icount = offset + 1 voids = voidList[0] for m in voids: m.__id__ = icount - if updateComment and not sortEnclosure: + if update_comment and not sort_enclosure: m.Comments = m.Comments + "\nLevel 0 void enclosure" icount += 1 m.ParentEnclosureID = -1 @@ -88,12 +88,10 @@ def updateVoidList(offset, voidList, NestedEnclosure, sortEnclosure): m.__id__ = icount m.ParentEnclosureID = encl.ParentEnclosureID m.EnclosureID = encl.EnclosureID - if sortEnclosure: - m.Comments = m.Comments + "\n{}".format(encl.Comments) + if sort_enclosure: + m.Comments = m.Comments + f"\n{encl.Comments}" else: - m.Comments = m.Comments + "\nVoid Enclosure #{}".format( - encl.EnclosureID - ) + m.Comments = m.Comments + f"\nVoid Enclosure #{encl.EnclosureID}" icount += 1 newVoidList.append(m) ivoid += 1 diff --git a/src/geouned/GEOUNED/write/__init__.py b/src/geouned/GEOUNED/write/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/geouned/GEOUNED/write/additional_files.py b/src/geouned/GEOUNED/write/additional_files.py new file mode 100644 index 00000000..cfdd6839 --- /dev/null +++ b/src/geouned/GEOUNED/write/additional_files.py @@ -0,0 +1,55 @@ +from pathlib import Path + + +def comments_write(name, MetaList): + """Function to write in an independent file the comment strings""" + + Path(name).parent.mkdir(parents=True, exist_ok=True) + with open(file=name + "_comments.txt", mode="w", encoding="utf-8") as outfile: + for m in MetaList: + outfile.write(m.Comments + "\n") + + +def summary_write(name, MetaList): + + Path(name).parent.mkdir(parents=True, exist_ok=True) + with open(file=name + "_summary.txt", mode="w", encoding="utf-8") as outfile: + header = f" Cell Id{'':5s}Mat Id{'':6s}Density{'':7s}Volume{'':5s}Comments\n" + outfile.write(header) + + for m in MetaList: + if m.Void or m.__id__ is None: + continue + index = m.__id__ + Vol = m.Volume * 1e-3 + if m.Material == 0: + line = " {:>8d}{:3s}{:>8d}{:3s}{:11s}{:3s}{:11.4e}{:3s}{}\n".format( + index, "", 0, "", "", "", Vol, "", m.Comments + ) + else: + if abs(m.Density) < 1e-2: + line = " {:>8d}{:3s}{:>8d}{:3s}{:11.4e}{:3s}{:11.4e}{:3s}{}\n".format( + index, + "", + m.Material, + "", + m.Density, + "", + Vol, + "", + m.Comments, + ) + else: + line = " {:>8d}{:3s}{:>8d}{:3s}{:11.7f}{:3s}{:11.4e}{:3s}{}\n".format( + index, + "", + m.Material, + "", + m.Density, + "", + Vol, + "", + m.Comments, + ) + + outfile.write(line) diff --git a/src/geouned/GEOUNED/write/functions.py b/src/geouned/GEOUNED/write/functions.py new file mode 100644 index 00000000..7225e031 --- /dev/null +++ b/src/geouned/GEOUNED/write/functions.py @@ -0,0 +1,1151 @@ +import math +import re + +import FreeCAD + +from ..utils import q_form as q_form +from ..utils.basic_functions_part1 import is_opposite, is_parallel +from .string_functions import remove_redundant + + +class CardLine: + def __init__(self, card, linesize=80, tabspace=6, fmt=""): + self.str = card + " " + self.lineSize = linesize + self.__leftspace__ = linesize - len(self.str) + self.tabspace = tabspace + self.tabstr = " " * tabspace + self.fmt = fmt + + def extend(self, dataList): + line = "" + for data in dataList: + data_str = "{:{fmt}} ".format(data, fmt=self.fmt) + if self.__leftspace__ - len(data_str) > 0: + line += data_str + self.__leftspace__ -= len(data_str) + else: + self.str += line + "\n" + line = f"{self.tabstr}{data_str}" + self.__leftspace__ = self.lineSize - len(line) + + self.str += line + + +class CellString: + def __init__(self, linesize=80, tabspace=6): + self.str = "" + self.lineSize = linesize + self.__leftspace__ = linesize + self.tabspace = tabspace + self.tabstr = " " * tabspace + + def add(self, string): + self.str += string + + # TODO check this is used + def del_ast_char(self): + self.str = self.str[0:-1] + self.__leftspace__ += 1 + + def wrap_line(self, offset=0): + + self.str = self.str.strip() + self.str = re.sub(" +", " ", self.str) + self.str = re.sub(" *\( *", "(", self.str) + self.str = re.sub(" *\) *", ")", self.str) + self.str = re.sub(" *: *", ":", self.str) + self.str = re.sub("\)\(", ") (", self.str) + self.str = re.sub("(?P\d)\(", "\g (", self.str) + self.str = re.sub("\)(?P\d)", ") \g", self.str) + self.str = re.sub("\)-", ") -", self.str) + + if len(self.str) + offset <= self.lineSize: + return + + newline = "" + init = 0 + icount = self.lineSize - 1 - offset + lenleft = len(self.str) + + while True: + while self.str[icount] not in (")", ":", " "): + icount -= 1 + + newline += self.str[init : icount + 1] + lenleft -= icount - init + 1 + init = icount + 1 + icount += self.lineSize - self.tabspace + if lenleft == 0: + break + elif icount < len(self.str): + newline += f"\n{self.tabstr}" + else: + newline += f"\n{self.tabstr}{self.str[init:]}" + break + + self.str = newline + + +def change_surf_sign(surf, Seq): + if Seq.level == 0: + for i, e in enumerate(Seq.elements): + if surf == abs(e): + Seq.elements[i] = -Seq.elements[i] + else: + for i, e in enumerate(Seq.elements): + if type(e) is int: + if surf == abs(e): + Seq.elements[i] = -Seq.elements[i] + else: + change_surf_sign(surf, e) + + +def write_mcnp_cell_def(definition, tabspace=0, offset=0): + sdef = CellString(tabspace=tabspace) + str_def = remove_redundant(write_sequence_mcnp(definition)) + sdef.add(str_def) + sdef.wrap_line(offset) + return sdef.str + + +def write_serpent_cell_def(definition, tabspace=0, offset=0): + sdef = CellString(tabspace=tabspace) + str_def = remove_redundant(write_sequence_serpent(definition)) + sdef.add(str_def) + sdef.wrap_line(offset) + return sdef.str + + +def write_phits_cell_def(definition, tabspace=0, offset=0): + sdef = CellString(tabspace=tabspace) + str_def = remove_redundant(write_sequence_phits(definition)) + sdef.add(str_def) + sdef.wrap_line(offset) + return sdef.str + + +def write_openmc_region(definition, options, w_type="XML"): + if w_type == "XML": + return write_sequence_omc_xml(definition) + if w_type == "PY": + return write_sequence_omc_py(definition, options) + + +def write_sequence_mcnp(Seq): + if Seq.level == 0: + if Seq.operator == "AND": + line = f"({' '.join(map(str, Seq.elements))})" + else: + line = f"({':'.join(map(str, Seq.elements))})" + else: + terms = [] + for e in Seq.elements: + if type(e) is int: + terms.append(str(e)) + else: + terms.append(write_sequence_mcnp(e)) + + if Seq.operator == "AND": + line = f"({' '.join(terms)})" + else: + line = f"({':'.join(terms)})" + + return line + + +def write_sequence_serpent(seq): + if seq.level == 0: + if seq.operator == "AND": + line = f"({' '.join(map(str, seq.elements))})" + else: + line = f"({':'.join(map(str, seq.elements))})" + else: + terms = [] + for e in seq.elements: + if type(e) is int: + terms.append(str(e)) + else: + terms.append(write_sequence_mcnp(e)) + + if seq.operator == "AND": + line = f"({' '.join(terms)})" + else: + line = f"({':'.join(terms)})" + + return line + + +def write_sequence_phits(seq): + if seq.level == 0: + if seq.operator == "AND": + line = f"({' '.join(map(str, seq.elements))})" + else: + line = f"({':'.join(map(str, seq.elements))})" + else: + terms = [] + for e in seq.elements: + if type(e) is int: + terms.append(str(e)) + else: + terms.append(write_sequence_phits(e)) + + if seq.operator == "AND": + line = f"({' '.join(terms)})" + else: + line = f"({':'.join(terms)})" + + return line + + +def write_sequence_omc_xml(seq): + if seq.level == 0: + if seq.operator == "AND": + line = f"({' '.join(map(str, seq.elements))})" + else: + line = f"({' | '.join(map(str, seq.elements))})" + else: + terms = [] + for e in seq.elements: + if type(e) is int: + terms.append(str(e)) + else: + terms.append(write_sequence_omc_xml(e)) + + if seq.operator == "AND": + line = f"({' '.join(terms)})" + else: + line = f"({' | '.join(terms)})" + return line + + +def write_sequence_omc_py(seq, options, prefix="S"): + + strSurf = lambda surf: (f"-{prefix}{-surf}" if surf < 0 else f"+{prefix}{surf}") + + if seq.level == 0: + if seq.operator == "AND": + line = f"({' & '.join(map(strSurf, seq.elements))})" + else: + line = f"({' | '.join(map(strSurf, seq.elements))})" + else: + terms = [] + for e in seq.elements: + if type(e) is int: + terms.append(strSurf(e)) + else: + terms.append(write_sequence_omc_py(e, options)) + + if seq.operator == "AND": + line = f"({' & '.join(terms)})" + else: + line = f"({' | '.join(terms)})" + return line + + +def mcnp_surface(id, Type, surf, options, tolerances, numeric_format): + mcnp_def = "" + + if Type == "Plane": + if surf.pointDef and options.prnt3PPlane: + P1 = surf.Points[0] + P2 = surf.Points[1] + P3 = surf.Points[2] + mcnp_def = """{:<6d} P {P1[0]:{d}} {P1[1]:{d}} {P1[2]:{d}} + {P2[0]:{d}} {P2[1]:{d}} {P2[2]:{d}} + {P3[0]:{d}} {P3[1]:{d}} {P3[2]:{d}}""".format( + id, P1=P1 / 10, P2=P2 / 10, P3=P3 / 10, d=numeric_format.P_d + ) + else: + A = surf.Axis.x + B = surf.Axis.y + C = surf.Axis.z + D = surf.Axis.dot(surf.Position) + if surf.Axis.isEqual(FreeCAD.Vector(1, 0, 0), tolerances.pln_angle): + mcnp_def = "{:<6d} PX {:{x}}".format(id, D / 10.0, x=numeric_format.P_xyz) + elif surf.Axis.isEqual(FreeCAD.Vector(0, 1, 0), tolerances.pln_angle): + mcnp_def = "{:<6d} PY {:{y}}".format(id, D / 10.0, y=numeric_format.P_xyz) + elif surf.Axis.isEqual(FreeCAD.Vector(0, 0, 1), tolerances.pln_angle): + mcnp_def = "{:<6d} PZ {:{z}}".format(id, D / 10.0, z=numeric_format.P_xyz) + else: + mcnp_def = "{:<6d} P {:{abc}} {:{abc}} {:{abc}} {:{d}}".format( + id, + A, + B, + C, + D / 10.0, + abc=numeric_format.P_abc, + d=numeric_format.P_d, + ) + + elif Type == "Cylinder": + Dir = FreeCAD.Vector(surf.Axis) + Dir.normalize() + Pos = surf.Center * 0.1 + rad = surf.Radius * 0.1 + if is_parallel(Dir, FreeCAD.Vector(1, 0, 0), tolerances.angle): + if Pos.y == 0.0 and Pos.z == 0.0: + mcnp_def = "{:<6d} CX {:{r}}".format(id, rad, r=numeric_format.C_r) + else: + mcnp_def = "{:<6d} C/X {:{yz}} {:{yz}} {:{r}}".format( + id, Pos.y, Pos.z, rad, yz=numeric_format.C_xyz, r=numeric_format.C_r + ) + elif is_parallel(Dir, FreeCAD.Vector(0, 1, 0), tolerances.angle): + if Pos.x == 0.0 and Pos.z == 0.0: + mcnp_def = "{:<6d} CY {:{r}}".format(id, rad, r=numeric_format.C_r) + else: + mcnp_def = "{:<6d} C/Y {:{xz}} {:{xz}} {:{r}}".format( + id, Pos.x, Pos.z, rad, xz=numeric_format.C_xyz, r=numeric_format.C_r + ) + elif is_parallel(Dir, FreeCAD.Vector(0, 0, 1), tolerances.angle): + if Pos.y == 0.0 and Pos.x == 0.0: + mcnp_def = "{:<6d} CZ {:{r}}".format(id, rad, r=numeric_format.C_r) + else: + mcnp_def = "{:<6d} C/Z {:{xy}} {:{xy}} {:{r}}".format( + id, Pos.x, Pos.y, rad, xy=numeric_format.C_xyz, r=numeric_format.C_r + ) + else: + # Is not still working fine + Q = q_form.q_form_cyl(Dir, Pos, rad) + mcnp_def = """\ +{:<6d} GQ {v[0]:{aTof}} {v[1]:{aTof}} {v[2]:{aTof}} + {v[3]:{aTof}} {v[4]:{aTof}} {v[5]:{aTof}} + {v[6]:{gToi}} {v[7]:{gToi}} {v[8]:{gToi}} + {v[9]:{j}} """.format( + id, + v=Q, + aTof=numeric_format.GQ_1to6, + gToi=numeric_format.GQ_7to9, + j=numeric_format.GQ_10, + ) + + # Si se quiere rcc en vez de Q form + # pnt = surf.Center.sub(surf.Axis.multiply(1.0e7)) # mas alla de 100 m + # dir = surf.Axis.multiply(1.0e8) + # Vx= pnt.x/10.0 + # Vy= pnt.y/10.0 + # Vz= pnt.z/10.0 + # Hx= dir.x/10.0 + # Hy= dir.y/10.0 + # Hz= dir.z/10.0 + # rad=surf.Radius/10.0 + # mcnp_def='%i RCC %13.7E %13.7E %13.7E %13.7E\n %13.7E %13.7E %13.7E' %(id,Vx,Vy,Vz,Hx,Hy,Hz,rad) + + elif Type == "Cone": + Apex = surf.Apex * 0.1 + Dir = surf.Axis * 0.1 + tan = math.tan(surf.SemiAngle) + X_dir = FreeCAD.Vector(1, 0, 0) + Y_dir = FreeCAD.Vector(0, 1, 0) + Z_dir = FreeCAD.Vector(0, 0, 1) + if is_parallel(Dir, X_dir, tolerances.angle): + sheet = 1 + if is_opposite(Dir, X_dir, tolerances.angle): + sheet = -1 + if Apex.y == 0.0 and Apex.z == 0.0: + mcnp_def = "{:<6d} KX {:{x}} {:{t2}} {}".format( + id, + Apex.x, + tan**2, + sheet, + x=numeric_format.K_xyz, + t2=numeric_format.K_tan2, + ) + else: + mcnp_def = "{:<6d} K/X {:{xyz}} {:{xyz}} {:{xyz}} {:{t2}} {}".format( + id, + Apex.x, + Apex.y, + Apex.z, + tan**2, + sheet, + xyz=numeric_format.K_xyz, + t2=numeric_format.K_tan2, + ) + elif is_parallel(Dir, Y_dir, tolerances.angle): + sheet = 1 + if is_opposite(Dir, Y_dir, tolerances.angle): + sheet = -1 + if Apex.x == 0.0 and Apex.z == 0.0: + mcnp_def = "{:<6d} KY {:{y}} {:{t2}} {}".format( + id, + Apex.y, + tan**2, + sheet, + y=numeric_format.K_xyz, + t2=numeric_format.K_tan2, + ) + else: + mcnp_def = "{:<6d} K/Y {:{xyz}} {:{xyz}} {:{xyz}} {:{t2}} {}".format( + id, + Apex.x, + Apex.y, + Apex.z, + tan**2, + sheet, + xyz=numeric_format.K_xyz, + t2=numeric_format.K_tan2, + ) + elif is_parallel(Dir, Z_dir, tolerances.angle): + sheet = 1 + if is_opposite(Dir, Z_dir, tolerances.angle): + sheet = -1 + if Apex.x == 0.0 and Apex.y == 0.0: + mcnp_def = "{:<6d} KZ {:{z}} {:{t2}} {}".format( + id, + Apex.z, + tan**2, + sheet, + z=numeric_format.K_xyz, + t2=numeric_format.K_tan2, + ) + else: + mcnp_def = "{:<6d} K/Z {:{xyz}} {:{xyz}} {:{xyz}} {:{t2}} {}".format( + id, + Apex.x, + Apex.y, + Apex.z, + tan**2, + sheet, + xyz=numeric_format.K_xyz, + t2=numeric_format.K_tan2, + ) + else: + Q = q_form.q_form_cone(Dir, Apex, tan) + mcnp_def = """\ +{:<6d} GQ {v[0]:{aTof}} {v[1]:{aTof}} {v[2]:{aTof}} + {v[3]:{aTof}} {v[4]:{aTof}} {v[5]:{aTof}} + {v[6]:{gToi}} {v[7]:{gToi}} {v[8]:{gToi}} + {v[9]:{j}} """.format( + id, + v=Q, + aTof=numeric_format.GQ_1to6, + gToi=numeric_format.GQ_7to9, + j=numeric_format.GQ_10, + ) + + elif Type == "Sphere": + # corresponding logic + rad = surf.Radius * 0.1 + pnt = surf.Center * 0.1 + if pnt.isEqual(FreeCAD.Vector(0, 0, 0), tolerances.sph_distance): + mcnp_def = "{:<6d} SO {:{r}}".format(id, rad, r=numeric_format.S_r) + else: + mcnp_def = "{:<6d} S {:{xyz}} {:{xyz}} {:{xyz}} {:{r}}".format( + id, + pnt.x, + pnt.y, + pnt.z, + rad, + xyz=numeric_format.S_xyz, + r=numeric_format.S_r, + ) + + elif Type == "Torus": + Dir = FreeCAD.Vector(surf.Axis) + Dir.normalize() + Pos = surf.Center * 0.1 + radMaj = surf.MajorRadius * 0.1 + radMin = surf.MinorRadius * 0.1 + if is_parallel(Dir, FreeCAD.Vector(1, 0, 0), tolerances.angle): + mcnp_def = """\ +{:<6d} TX {:{xyz}} {:{xyz}} {:{xyz}} + {:{r}} {:{r}} {:{r}}""".format( + id, + Pos.x, + Pos.y, + Pos.z, + radMaj, + radMin, + radMin, + xyz=numeric_format.T_xyz, + r=numeric_format.T_r, + ) + elif is_parallel(Dir, FreeCAD.Vector(0, 1, 0), tolerances.angle): + mcnp_def = """\ +{:<6d} TY {:{xyz}} {:{xyz}} {:{xyz}} + {:{r}} {:{r}} {:{r}}""".format( + id, + Pos.x, + Pos.y, + Pos.z, + radMaj, + radMin, + radMin, + xyz=numeric_format.T_xyz, + r=numeric_format.T_r, + ) + elif is_parallel(Dir, FreeCAD.Vector(0, 0, 1), tolerances.angle): + mcnp_def = """\ +{:<6d} TZ {:{xyz}} {:{xyz}} {:{xyz}} + {:{r}} {:{r}} {:{r}}""".format( + id, + Pos.x, + Pos.y, + Pos.z, + radMaj, + radMin, + radMin, + xyz=numeric_format.T_xyz, + r=numeric_format.T_r, + ) + + return trim(mcnp_def, 80) + + +def open_mc_surface(Type, surf, tolerances, numeric_format, out_xml=True, quadricForm=False): + if Type == "Plane": + A = surf.Axis.x + B = surf.Axis.y + C = surf.Axis.z + D = surf.Axis.dot(surf.Position) * 0.1 + if surf.Axis.isEqual(FreeCAD.Vector(1, 0, 0), tolerances.pln_angle): + if out_xml: + omc_surf = "x-plane" + coeffs = "{:{x}}".format(D, x=numeric_format.P_xyz) + else: + omc_surf = "XPlane" + coeffs = f"x0={D}" + + elif surf.Axis.isEqual(FreeCAD.Vector(0, 1, 0), tolerances.pln_angle): + if out_xml: + omc_surf = "y-plane" + coeffs = "{:{x}}".format(D, x=numeric_format.P_xyz) + else: + omc_surf = "YPlane" + coeffs = f"y0={D}" + + elif surf.Axis.isEqual(FreeCAD.Vector(0, 0, 1), tolerances.pln_angle): + if out_xml: + omc_surf = "z-plane" + coeffs = "{:{x}}".format(D, x=numeric_format.P_xyz) + else: + omc_surf = "ZPlane" + coeffs = f"z0={D}" + + else: + if out_xml: + omc_surf = "plane" + coeffs = "{:{abc}} {:{abc}} {:{abc}} {:{d}}".format(A, B, C, D, abc=numeric_format.P_abc, d=numeric_format.P_d) + else: + omc_surf = "Plane" + coeffs = f"a={A},b={B},c={C},d={D}" + + elif Type == "Cylinder": + pos = surf.Center * 0.1 + Rad = surf.Radius * 0.1 + Dir = FreeCAD.Vector(surf.Axis) + Dir.normalize() + + if is_parallel(Dir, FreeCAD.Vector(1, 0, 0), tolerances.angle): + if out_xml: + omc_surf = "x-cylinder" + coeffs = "{:{xy}} {:{xy}} {:{r}}".format(pos.y, pos.z, Rad, xy=numeric_format.C_xyz, r=numeric_format.C_r) + else: + omc_surf = "XCylinder" + coeffs = f"y0={pos.y},z0={pos.z},r={Rad}" + + elif is_parallel(Dir, FreeCAD.Vector(0, 1, 0), tolerances.angle): + if out_xml: + omc_surf = "y-cylinder" + coeffs = "{:{xy}} {:{xy}} {:{r}}".format(pos.x, pos.z, Rad, xy=numeric_format.C_xyz, r=numeric_format.C_r) + else: + omc_surf = "YCylinder" + coeffs = f"x0={pos.x},z0={pos.z},r={Rad}" + + elif is_parallel(Dir, FreeCAD.Vector(0, 0, 1), tolerances.angle): + if out_xml: + omc_surf = "z-cylinder" + coeffs = "{:{xy}} {:{xy}} {:{r}}".format(pos.x, pos.y, Rad, xy=numeric_format.C_xyz, r=numeric_format.C_r) + else: + omc_surf = "ZCylinder" + coeffs = f"x0={pos.x},y0={pos.y},r={Rad}" + + else: + if out_xml: + omc_surf = "quadric" + Q = q_form.q_form_cyl(Dir, pos, Rad) + coeffs = "{v[0]:{aTof}} {v[1]:{aTof}} {v[2]:{aTof}} {v[3]:{aTof}} {v[4]:{aTof}} {v[5]:{aTof}} {v[6]:{gToi}} {v[7]:{gToi}} {v[8]:{gToi}} {v[9]:{j}}".format( + v=Q, + aTof=numeric_format.GQ_1to6, + gToi=numeric_format.GQ_7to9, + j=numeric_format.GQ_10, + ) + else: + if quadricForm: + omc_surf = "Quadric" + Q = q_form.q_form_cyl(Dir, pos, Rad) + coeffs = "a={v[0]},b={v[1]},c={v[2]},d={v[3]},e={v[4]},f={v[5]},g={v[6]},h={v[7]},j={v[8]},k={v[9]}".format( + v=Q + ) + else: + omc_surf = "Cylinder" + coeffs = "x0={},y0={},z0={},r={},dx={},dy={},dz={}".format(pos.x, pos.y, pos.z, Rad, Dir.x, Dir.y, Dir.z) + + elif Type == "Cone": + Apex = surf.Apex * 0.1 + Dir = FreeCAD.Vector(surf.Axis) + Dir.normalize() + tan = math.tan(surf.SemiAngle) + tan2 = tan * tan + + X_dir = FreeCAD.Vector(1, 0, 0) + Y_dir = FreeCAD.Vector(0, 1, 0) + Z_dir = FreeCAD.Vector(0, 0, 1) + + if is_parallel(Dir, X_dir, tolerances.angle): + if out_xml: + omc_surf = "x-cone" + coeffs = "{:{xyz}} {:{xyz}} {:{xyz}} {:{t2}}".format( + Apex.x, + Apex.y, + Apex.z, + tan2, + xyz=numeric_format.K_xyz, + t2=numeric_format.K_tan2, + ) + else: + omc_surf = "XCone" + coeffs = f"x0={Apex.x},y0={Apex.y},z0={Apex.z},r2={tan2}" + + elif is_parallel(Dir, Y_dir, tolerances.angle): + if out_xml: + omc_surf = "y-cone" + coeffs = "{:{xyz}} {:{xyz}} {:{xyz}} {:{t2}}".format( + Apex.x, + Apex.y, + Apex.z, + tan2, + xyz=numeric_format.K_xyz, + t2=numeric_format.K_tan2, + ) + else: + omc_surf = "YCone" + coeffs = f"x0={Apex.x},y0={Apex.y},z0={Apex.z},r2={tan2}" + + elif is_parallel(Dir, Z_dir, tolerances.angle): + if out_xml: + omc_surf = "z-cone" + coeffs = "{:{xyz}} {:{xyz}} {:{xyz}} {:{t2}}".format( + Apex.x, + Apex.y, + Apex.z, + tan2, + xyz=numeric_format.K_xyz, + t2=numeric_format.K_tan2, + ) + else: + omc_surf = "ZCone" + coeffs = f"x0={Apex.x},y0={Apex.y},z0={Apex.z},r2={tan2}" + + else: + if out_xml: + omc_surf = "quadric" + Q = q_form.q_form_cone(Dir, Apex, tan) + coeffs = "{v[0]:{aTof}} {v[1]:{aTof}} {v[2]:{aTof}} {v[3]:{aTof}} {v[4]:{aTof}} {v[5]:{aTof}} {v[6]:{gToi}} {v[7]:{gToi}} {v[8]:{gToi}} {v[9]:{j}}".format( + v=Q, + aTof=numeric_format.GQ_1to6, + gToi=numeric_format.GQ_7to9, + j=numeric_format.GQ_10, + ) + else: + if quadricForm: + omc_surf = "Quadric" + Q = q_form.q_form_cone(Dir, Apex, tan) + coeffs = "a={v[0]},b={v[1]},c={v[2]},d={v[3]},e={v[4]},f={v[5]},g={v[6]},h={v[7]},j={v[8]},k={v[9]}".format( + v=Q + ) + else: + omc_surf = "Cone" + coeffs = "x0={},y0={},z0={},r2={},dx={},dy={},dz={}".format( + Apex.x, Apex.y, Apex.z, tan2, Dir.x, Dir.y, Dir.z + ) + + elif Type == "Sphere": + Center = surf.Center * 0.1 + Rad = surf.Radius * 0.1 + if out_xml: + omc_surf = "sphere" + coeffs = "{:{xyz}} {:{xyz}} {:{xyz}} {:{r}}".format( + Center.x, + Center.y, + Center.z, + Rad, + xyz=numeric_format.S_xyz, + r=numeric_format.S_r, + ) + else: + omc_surf = "Sphere" + coeffs = f"x0={Center.x},y0={Center.y},z0={Center.z},r={Rad}" + + elif Type == "Torus": + Center = surf.Center * 0.1 + minRad = surf.MinorRadius * 0.1 + majRad = surf.MajorRadius * 0.1 + Dir = FreeCAD.Vector(surf.Axis) + Dir.normalize() + if out_xml: + coeffs = "{:{xyz}} {:{xyz}} {:{xyz}} {:{r}} {:{r}} {:{r}}".format( + Center.x, + Center.y, + Center.z, + majRad, + minRad, + minRad, + xyz=numeric_format.T_xyz, + r=numeric_format.T_r, + ) + else: + coeffs = "x0={},y0={},z0={},r={},r1={},r2={}".format(Center.x, Center.y, Center.z, majRad, minRad, minRad) + + if is_parallel(Dir, FreeCAD.Vector(1, 0, 0), tolerances.angle): + omc_surf = "x-torus" if out_xml else "XTorus" + elif is_parallel(Dir, FreeCAD.Vector(0, 1, 0), tolerances.angle): + omc_surf = "y-torus" if out_xml else "YTorus" + elif is_parallel(Dir, FreeCAD.Vector(0, 0, 1), tolerances.angle): + omc_surf = "z-torus" if out_xml else "ZTorus" + else: + omc_surf = None + + if out_xml: + coeffs = " ".join(coeffs.split()) + return omc_surf, coeffs + + +def serpent_surface(id, Type, surf, options, tolerance, numeric_format): + serpent_def = "" + + if Type == "Plane": + if surf.pointDef and options.prnt3PPlane: + P1 = surf.Points[0] + P2 = surf.Points[1] + P3 = surf.Points[2] + serpent_def = f"surf {id} plane {P1.x/10:{numeric_format.P_d}} {P1.y/10:{numeric_format.P_d}} {P1.z/10:{numeric_format.P_d}}\n" + serpent_def += ( + f" {P2.x/10:{numeric_format.P_d}} {P2.y/10:{numeric_format.P_d}} {P2.z/10:{numeric_format.P_d}}\n" + ) + serpent_def += f" {P3.x/10:{numeric_format.P_d}} {P3.y/10:{numeric_format.P_d}} {P3.z/10:{numeric_format.P_d}}" + + else: + A = surf.Axis.x + B = surf.Axis.y + C = surf.Axis.z + D = surf.Axis.dot(surf.Position) + if surf.Axis.isEqual(FreeCAD.Vector(1, 0, 0), tolerance.pln_angle): + serpent_def = f"surf {id} px {D/10:{numeric_format.P_xyz}}" + elif surf.Axis.isEqual(FreeCAD.Vector(0, 1, 0), tolerance.pln_angle): + serpent_def = f"surf {id} py {D/10:{numeric_format.P_xyz}}" + elif surf.Axis.isEqual(FreeCAD.Vector(0, 0, 1), tolerance.pln_angle): + serpent_def = f"surf {id} pz {D/10:{numeric_format.P_xyz}}" + else: + serpent_def = f"surf {id} plane {A:{numeric_format.P_d}} {B:{numeric_format.P_d}} {C:{numeric_format.P_d}} {D/10:{numeric_format.P_d}}" + + elif Type == "Cylinder": + Dir = surf.Axis + Dir.normalize() + Pos = surf.Center * 0.1 + rad = surf.Radius * 0.1 + if is_parallel(Dir, FreeCAD.Vector(1, 0, 0), tolerance.angle): + serpent_def = ( + f"surf {id} cylx {Pos.y:{numeric_format.C_xyz}} {Pos.z:{numeric_format.C_xyz}} {rad:{numeric_format.C_r}}" + ) + elif is_parallel(Dir, FreeCAD.Vector(0, 1, 0), tolerance.angle): + serpent_def = ( + f"surf {id} cyly {Pos.x:{numeric_format.C_xyz}} {Pos.z:{numeric_format.C_xyz}} {rad:{numeric_format.C_r}}" + ) + elif is_parallel(Dir, FreeCAD.Vector(0, 0, 1), tolerance.angle): + serpent_def = f"surf {id} cylz {rad:{numeric_format.C_r}}" + else: + # Is not still working fine + Q = q_form.q_form_cyl(Dir, Pos, rad) + serpent_def = """\ +surf quadratic {v[0]:{aTof}} {v[1]:{aTof}} {v[2]:{aTof}} + {v[3]:{aTof}} {v[4]:{aTof}} {v[5]:{aTof}} + {v[6]:{gToi}} {v[7]:{gToi}} {v[8]:{gToi}} + {v[9]:{j}} """.format( + id, + v=Q, + aTof=numeric_format.GQ_1to6, + gToi=numeric_format.GQ_7to9, + j=numeric_format.GQ_10, + ) + + elif Type == "Cone": + Apex = surf.Apex * 0.1 + Dir = surf.Axis * 0.1 + tan = math.tan(surf.SemiAngle) + X_dir = FreeCAD.Vector(1, 0, 0) + Y_dir = FreeCAD.Vector(0, 1, 0) + Z_dir = FreeCAD.Vector(0, 0, 1) + + # Need to check this + # Serpent has no specific card for cone at origin, explicit origin only + + if is_parallel(Dir, X_dir, tolerance.angle): + sheet = 1 + if is_opposite(Dir, X_dir, tolerance.angle): + sheet = -1 + serpent_def = "surf ckx {:{xyz}} {:{xyz}} {:{xyz}} {:{t2}} {}".format( + id, + Apex.x, + Apex.y, + Apex.z, + tan**2, + sheet, + xyz=numeric_format.K_xyz, + t2=numeric_format.K_tan2, + ) + elif is_parallel(Dir, Y_dir, tolerance.angle): + sheet = 1 + if is_opposite(Dir, Y_dir, tolerance.angle): + sheet = -1 + serpent_def = "surf cky {:{xyz}} {:{xyz}} {:{xyz}} {:{t2}} {}".format( + id, + Apex.x, + Apex.y, + Apex.z, + tan**2, + sheet, + xyz=numeric_format.K_xyz, + t2=numeric_format.K_tan2, + ) + elif is_parallel(Dir, Z_dir, tolerance.angle): + sheet = 1 + if is_opposite(Dir, Z_dir, tolerance.angle): + sheet = -1 + serpent_def = "surf ckz {:{xyz}} {:{xyz}} {:{xyz}} {:{t2}} {}".format( + id, + Apex.x, + Apex.y, + Apex.z, + tan**2, + sheet, + xyz=numeric_format.K_xyz, + t2=numeric_format.K_tan2, + ) + else: + Q = q_form.q_form_cone(Dir, Apex, tan) + + elif Type == "Sphere": + rad = surf.Radius * 0.1 + pnt = surf.Center * 0.1 + # Serpent has only explicit spheres at the origin + serpent_def = f"surf {id} sph {pnt.x:{numeric_format.S_xyz}} {pnt.y:{numeric_format.S_xyz}} {pnt.z:{numeric_format.S_xyz}} {rad:{numeric_format.S_r}}" + + elif Type == "Torus": + Dir = surf.Axis + Dir.normalize() + Pos = surf.Center * 0.1 + radMaj = surf.MajorRadius * 0.1 + radMin = surf.MinorRadius * 0.1 + if is_parallel(Dir, FreeCAD.Vector(1, 0, 0), tolerance.angle): + serpent_def = ( + f"surf {id} torx {Pos.x:{numeric_format.T_xyz}} {Pos.y:{numeric_format.T_xyz}} {Pos.z:{numeric_format.T_xyz}}\n" + ) + serpent_def += f" {radMaj:{numeric_format.T_r}} {radMin:{numeric_format.T_r}} {radMin:{numeric_format.T_r}}" + elif is_parallel(Dir, FreeCAD.Vector(0, 1, 0), tolerance.angle): + serpent_def = ( + f"surf {id} tory {Pos.x:{numeric_format.T_xyz}} {Pos.y:{numeric_format.T_xyz}} {Pos.z:{numeric_format.T_xyz}}\n" + ) + serpent_def += f" {radMaj:{numeric_format.T_r}} {radMin:{numeric_format.T_r}} {radMin:{numeric_format.T_r}}" + elif is_parallel(Dir, FreeCAD.Vector(0, 0, 1), tolerance.angle): + serpent_def = ( + f"surf {id} torz {Pos.x:{numeric_format.T_xyz}} {Pos.y:{numeric_format.T_xyz}} {Pos.z:{numeric_format.T_xyz}}\n" + ) + serpent_def += f" {radMaj:{numeric_format.T_r}} {radMin:{numeric_format.T_r}} {radMin:{numeric_format.T_r}}" + + return serpent_def + + +def phits_surface(id, Type, surf, options, tolerance, numeric_format): + phits_def = "" + + if Type == "Plane": + if surf.pointDef and options.prnt3PPlane: + P1 = surf.Points[0] + P2 = surf.Points[1] + P3 = surf.Points[2] + phits_def = """{:<6d} P {P1[0]:{d}} {P1[1]:{d}} {P1[2]:{d}} + {P2[0]:{d}} {P2[1]:{d}} {P2[2]:{d}} + {P3[0]:{d}} {P3[1]:{d}} {P3[2]:{d}}""".format( + id, P1=P1 / 10, P2=P2 / 10, P3=P3 / 10, d=numeric_format.P_d + ) + else: + A = surf.Axis.x + B = surf.Axis.y + C = surf.Axis.z + D = surf.Axis.dot(surf.Position) + if surf.Axis.isEqual(FreeCAD.Vector(1, 0, 0), tolerance.pln_angle): + phits_def = "{:<6d} PX {:{x}}".format(id, D / 10.0, x=numeric_format.P_xyz) + elif surf.Axis.isEqual(FreeCAD.Vector(0, 1, 0), tolerance.pln_angle): + phits_def = "{:<6d} PY {:{y}}".format(id, D / 10.0, y=numeric_format.P_xyz) + elif surf.Axis.isEqual(FreeCAD.Vector(0, 0, 1), tolerance.pln_angle): + phits_def = "{:<6d} PZ {:{z}}".format(id, D / 10.0, z=numeric_format.P_xyz) + else: + phits_def = "{:<6d} P {:{abc}} {:{abc}} {:{abc}} {:{d}}".format( + id, + A, + B, + C, + D / 10.0, + abc=numeric_format.P_abc, + d=numeric_format.P_d, + ) + + elif Type == "Cylinder": + Dir = FreeCAD.Vector(surf.Axis) + Dir.normalize() + Pos = surf.Center * 0.1 + rad = surf.Radius * 0.1 + if is_parallel(Dir, FreeCAD.Vector(1, 0, 0), tolerance.angle): + if Pos.y == 0.0 and Pos.z == 0.0: + phits_def = "{:<6d} CX {:{r}}".format(id, rad, r=numeric_format.C_r) + else: + phits_def = "{:<6d} C/X {:{yz}} {:{yz}} {:{r}}".format( + id, Pos.y, Pos.z, rad, yz=numeric_format.C_xyz, r=numeric_format.C_r + ) + elif is_parallel(Dir, FreeCAD.Vector(0, 1, 0), tolerance.angle): + if Pos.x == 0.0 and Pos.z == 0.0: + phits_def = "{:<6d} CY {:{r}}".format(id, rad, r=numeric_format.C_r) + else: + phits_def = "{:<6d} C/Y {:{xz}} {:{xz}} {:{r}}".format( + id, Pos.x, Pos.z, rad, xz=numeric_format.C_xyz, r=numeric_format.C_r + ) + elif is_parallel(Dir, FreeCAD.Vector(0, 0, 1), tolerance.angle): + if Pos.y == 0.0 and Pos.x == 0.0: + phits_def = "{:<6d} CZ {:{r}}".format(id, rad, r=numeric_format.C_r) + else: + phits_def = "{:<6d} C/Z {:{xy}} {:{xy}} {:{r}}".format( + id, Pos.x, Pos.y, rad, xy=numeric_format.C_xyz, r=numeric_format.C_r + ) + else: + # Is not still working fine + Q = q_form.q_form_cyl(Dir, Pos, rad) + phits_def = """\ +{:<6d} GQ {v[0]:{aTof}} {v[1]:{aTof}} {v[2]:{aTof}} + {v[3]:{aTof}} {v[4]:{aTof}} {v[5]:{aTof}} + {v[6]:{gToi}} {v[7]:{gToi}} {v[8]:{gToi}} + {v[9]:{j}} """.format( + id, + v=Q, + aTof=numeric_format.GQ_1to6, + gToi=numeric_format.GQ_7to9, + j=numeric_format.GQ_10, + ) + + # Si se quiere rcc en vez de Q form + # pnt = surf.Center.sub(surf.Axis.multiply(1.0e7)) # mas alla de 100 m + # dir = surf.Axis.multiply(1.0e8) + # Vx= pnt.x/10.0 + # Vy= pnt.y/10.0 + # Vz= pnt.z/10.0 + # Hx= dir.x/10.0 + # Hy= dir.y/10.0 + # Hz= dir.z/10.0 + # rad=surf.Radius/10.0 + # mcnp_def='%i RCC %13.7E %13.7E %13.7E %13.7E\n %13.7E %13.7E %13.7E' %(id,Vx,Vy,Vz,Hx,Hy,Hz,rad) + + elif Type == "Cone": + Apex = surf.Apex * 0.1 + Dir = surf.Axis * 0.1 + tan = math.tan(surf.SemiAngle) + X_dir = FreeCAD.Vector(1, 0, 0) + Y_dir = FreeCAD.Vector(0, 1, 0) + Z_dir = FreeCAD.Vector(0, 0, 1) + if is_parallel(Dir, X_dir, tolerance.angle): + sheet = 1 + if is_opposite(Dir, X_dir, tolerance.angle): + sheet = -1 + if Apex.y == 0.0 and Apex.z == 0.0: + phits_def = "{:<6d} KX {:{x}} {:{t2}} {}".format( + id, + Apex.x, + tan**2, + sheet, + x=numeric_format.K_xyz, + t2=numeric_format.K_tan2, + ) + else: + phits_def = "{:<6d} K/X {:{xyz}} {:{xyz}} {:{xyz}} {:{t2}} {}".format( + id, + Apex.x, + Apex.y, + Apex.z, + tan**2, + sheet, + xyz=numeric_format.K_xyz, + t2=numeric_format.K_tan2, + ) + elif is_parallel(Dir, Y_dir, tolerance.angle): + sheet = 1 + if is_opposite(Dir, Y_dir, tolerance.angle): + sheet = -1 + if Apex.x == 0.0 and Apex.z == 0.0: + phits_def = "{:<6d} KY {:{y}} {:{t2}} {}".format( + id, + Apex.y, + tan**2, + sheet, + y=numeric_format.K_xyz, + t2=numeric_format.K_tan2, + ) + else: + phits_def = "{:<6d} K/Y {:{xyz}} {:{xyz}} {:{xyz}} {:{t2}} {}".format( + id, + Apex.x, + Apex.y, + Apex.z, + tan**2, + sheet, + xyz=numeric_format.K_xyz, + t2=numeric_format.K_tan2, + ) + elif is_parallel(Dir, Z_dir, tolerance.angle): + sheet = 1 + if is_opposite(Dir, Z_dir, tolerance.angle): + sheet = -1 + if Apex.x == 0.0 and Apex.y == 0.0: + phits_def = "{:<6d} KZ {:{z}} {:{t2}} {}".format( + id, + Apex.z, + tan**2, + sheet, + z=numeric_format.K_xyz, + t2=numeric_format.K_tan2, + ) + else: + phits_def = "{:<6d} K/Z {:{xyz}} {:{xyz}} {:{xyz}} {:{t2}} {}".format( + id, + Apex.x, + Apex.y, + Apex.z, + tan**2, + sheet, + xyz=numeric_format.K_xyz, + t2=numeric_format.K_tan2, + ) + else: + Q = q_form.q_form_cone(Dir, Apex, tan) + phits_def = """\ +{:<6d} GQ {v[0]:{aTof}} {v[1]:{aTof}} {v[2]:{aTof}} + {v[3]:{aTof}} {v[4]:{aTof}} {v[5]:{aTof}} + {v[6]:{gToi}} {v[7]:{gToi}} {v[8]:{gToi}} + {v[9]:{j}} """.format( + id, + v=Q, + aTof=numeric_format.GQ_1to6, + gToi=numeric_format.GQ_7to9, + j=numeric_format.GQ_10, + ) + + elif Type == "Sphere": + # corresponding logic + rad = surf.Radius * 0.1 + pnt = surf.Center * 0.1 + if pnt.isEqual(FreeCAD.Vector(0, 0, 0), tolerance.sph_distance): + phits_def = "{:<6d} SO {:{r}}".format(id, rad, r=numeric_format.S_r) + else: + phits_def = "{:<6d} S {:{xyz}} {:{xyz}} {:{xyz}} {:{r}}".format( + id, + pnt.x, + pnt.y, + pnt.z, + rad, + xyz=numeric_format.S_xyz, + r=numeric_format.S_r, + ) + + elif Type == "Torus": + Dir = FreeCAD.Vector(surf.Axis) + Dir.normalize() + Pos = surf.Center * 0.1 + radMaj = surf.MajorRadius * 0.1 + radMin = surf.MinorRadius * 0.1 + if is_parallel(Dir, FreeCAD.Vector(1, 0, 0), tolerance.angle): + phits_def = """\ +{:<6d} TX {:{xyz}} {:{xyz}} {:{xyz}} + {:{r}} {:{r}} {:{r}}""".format( + id, + Pos.x, + Pos.y, + Pos.z, + radMaj, + radMin, + radMin, + xyz=numeric_format.T_xyz, + r=numeric_format.T_r, + ) + elif is_parallel(Dir, FreeCAD.Vector(0, 1, 0), tolerance.angle): + phits_def = """\ +{:<6d} TY {:{xyz}} {:{xyz}} {:{xyz}} + {:{r}} {:{r}} {:{r}}""".format( + id, + Pos.x, + Pos.y, + Pos.z, + radMaj, + radMin, + radMin, + xyz=numeric_format.T_xyz, + r=numeric_format.T_r, + ) + elif is_parallel(Dir, FreeCAD.Vector(0, 0, 1), tolerance.angle): + phits_def = """\ +{:<6d} TZ {:{xyz}} {:{xyz}} {:{xyz}} + {:{r}} {:{r}} {:{r}}""".format( + id, + Pos.x, + Pos.y, + Pos.z, + radMaj, + radMin, + radMin, + xyz=numeric_format.T_xyz, + r=numeric_format.T_r, + ) + + return trim(phits_def, 80) + + +def trim(surfDef, lineLength=80): + + lines = surfDef.split("\n") + if len(lines) == 1 and len(lines[0]) <= lineLength: + return surfDef + + longLine = [] + for i, line in enumerate(lines): + if len(line) > lineLength: + longLine.append(i) + + if len(longLine) == 0: + return surfDef + + newDef = "" + for i, line in enumerate(lines): + if i in longLine: + newLine = cut_line(line, lineLength) + else: + newLine = line + newDef += newLine + "\n" + + return newDef[:-1] + + +def cut_line(line, lineLength): + tabNumber = 10 + while True: + pos = line.rfind(" ") + if pos <= lineLength: + break + + line1 = line[0:pos] + line2 = line[pos + 1 :] + + # second line is only spaces + if len(line2.strip()) == 0: + newLine = line1 + else: + newLine = "{}\n{: <{n}}{}".format(line1, "", line2, n=tabNumber) + + return newLine diff --git a/src/geouned/GEOUNED/Write/MCNPFormat.py b/src/geouned/GEOUNED/write/mcnp_format.py similarity index 57% rename from src/geouned/GEOUNED/Write/MCNPFormat.py rename to src/geouned/GEOUNED/write/mcnp_format.py index a157b16b..a53d020a 100644 --- a/src/geouned/GEOUNED/Write/MCNPFormat.py +++ b/src/geouned/GEOUNED/write/mcnp_format.py @@ -1,26 +1,46 @@ ############################## # Module to write MCNP input # ############################## +import logging import math from datetime import datetime +from pathlib import Path import FreeCAD -from ..CodeVersion import * -from ..Utils.BasicFunctions_part1 import isOposite, pointsToCoeffs -from ..Utils.Functions import Surfaces_dict -from ..Utils.Options.Classes import Options as opt -from .Functions import CardLine, MCNPSurface, changeSurfSign, writeMCNPCellDef - - -class MCNP_input: - def __init__(self, Meta, Surfaces, setting): - self.Title = setting["title"] - self.VolSDEF = setting["volSDEF"] - self.VolCARD = setting["volCARD"] - self.U0CARD = setting["UCARD"] - self.dummyMat = setting["dummyMat"] +from ..code_version import * +from ..utils.basic_functions_part1 import is_opposite, points_to_coeffs +from ..utils.functions import SurfacesDict +from .functions import CardLine, change_surf_sign, mcnp_surface, write_mcnp_cell_def + +logger = logging.getLogger("general_logger") + + +# TODO rename as there are two classes with this name +class McnpInput: + def __init__( + self, + Meta, + Surfaces, + options, + tolerances, + numeric_format, + title, + volSDEF, + volCARD, + UCARD, + dummyMat, + stepFile, + ): + self.Title = title + self.VolSDEF = volSDEF + self.VolCARD = volCARD + self.U0CARD = UCARD + self.dummyMat = dummyMat self.Cells = Meta + self.options = options + self.tolerances = tolerances + self.numeric_format = numeric_format self.Options = { "Volume": self.VolCARD, "Particle": ("n", "p"), @@ -28,30 +48,25 @@ def __init__(self, Meta, Surfaces, setting): } self.part = "P" - self.StepFile = setting["stepFile"] + self.StepFile = stepFile if isinstance(self.StepFile, (tuple, list)): self.StepFile = "; ".join(self.StepFile) - if self.Title == "": - self.Title = self.StepFile + self.__get_surface_table__() + self.__simplify_planes__(Surfaces) - self.__getSurfaceTable__() - self.__simplifyPlanes__(Surfaces) - - self.Surfaces = self.__sortedSurfaces__(Surfaces) + self.Surfaces = self.__sorted_surfaces__(Surfaces) self.Materials = set() return - def setSDEF(self, data): + def set_sdef(self, data): if data[0] is not None: sphereId = data[0][0] radius = data[0][1] wgt = math.pi * radius * radius * 1e-2 - sdef = "SDEF PAR={} NRM=-1 SUR={} WGT={:13.7e} DIR=d1\n".format( - self.part, sphereId, wgt - ) + sdef = "SDEF PAR={} NRM=-1 SUR={} WGT={:13.7e} DIR=d1\n".format(self.part, sphereId, wgt) SI1 = "SI1 0 1\n" SP1 = "SP1 -21 1\n" self.SDEF_sphere = (sdef, SI1, SP1) @@ -59,19 +74,20 @@ def setSDEF(self, data): self.SDEF_sphere = None xmin, xmax, ymin, ymax, zmin, zmax = data[1] - sdef = "SDEF PAR={} X=D1 Y=D2 Z=D3 \n".format(self.part) - SI1 = "SI1 {:13.7e} {:13.7e} \n".format(xmin * 0.1, xmax * 0.1) - SI2 = "SI2 {:13.7e} {:13.7e} \n".format(ymin * 0.1, ymax * 0.1) - SI3 = "SI3 {:13.7e} {:13.7e} \n".format(zmin * 0.1, zmax * 0.1) + sdef = f"SDEF PAR={self.part} X=D1 Y=D2 Z=D3 \n" + SI1 = f"SI1 {xmin * 0.1:13.7e} {xmax * 0.1:13.7e} \n" + SI2 = f"SI2 {ymin * 0.1:13.7e} {ymax * 0.1:13.7e} \n" + SI3 = f"SI3 {zmin * 0.1:13.7e} {zmax * 0.1:13.7e} \n" SP1 = "SP1 0 1 \n" SP2 = "SP2 0 1 \n" SP3 = "SP3 0 1 \n" self.SDEF_box = (sdef, SI1, SI2, SI3, SP1, SP2, SP3) - def writeInput(self, filename): - print("write MCNP file {}".format(filename)) + def write_input(self, filename): + Path(filename).parent.mkdir(parents=True, exist_ok=True) + logger.info(f"write MCNP file {filename}") self.inpfile = open(filename, "w", encoding="utf-8") - self.__write_header__(filename) + self.__write_header__() self.__write_cell_block__() self.inpfile.write(" \n") @@ -89,39 +105,32 @@ def writeInput(self, filename): self.inpfile.close() return - def __write_header__(self, fileout): + def __write_header__(self): version = GEOUNED_Version releaseDate = GEOUNED_ReleaseDate freeCAD_Version = "{V[0]:}.{V[1]:}.{V[2]:}".format(V=FreeCAD.Version()) - Header = """{} + Header = f"""{self.Title} C ______ _______ _____ _ _ __ _ _______ ______ -C | ____ |______ | | ___ | | | \ | |______ | \ -C |_____| |______ |_____| |_____| | \_| |______ |_____/ -C Version : {} {} -C FreeCAD Version : {} \n""".format( - self.Title, version, releaseDate, freeCAD_Version - ) +C | ____ |______ | | ___ | | | \\ | |______ | \\ +C |_____| |______ |_____| |_____| | \\_| |______ |_____/ +C Version : {version} {releaseDate} +C FreeCAD Version : {freeCAD_Version} +""" - Information = """C + Information = f"""C C ************************************************************* -C Original Step file : {} +C Original Step file : {self.StepFile} C -C Creation Date : {} -C Solid Cells : {} -C Total Cells : {} -C Surfaces : {} -C Materials : {} +C Creation Date : {datetime.now()} +C Solid Cells : {self.__solidCells__} +C Total Cells : {self.__cells__} +C Surfaces : {len(self.Surfaces)} +C Materials : {len(self.__materials__)} C -C **************************************************************\n""".format( - self.StepFile, - datetime.now(), - self.__solidCells__, - self.__cells__, - len(self.Surfaces), - len(self.__materials__), - ) +C ************************************************************** +""" self.inpfile.write(Header) self.inpfile.write(Information) return @@ -144,28 +153,24 @@ def __write_cells__(self, cell): # if index is None objet not contain cell definition # but a comment to insert between cells if cell.__id__ is None: - comment = self.__commentLine__(cell.Comments) + comment = self.__comment_line__(cell.Comments) self.inpfile.write(comment) return if cell.Material == 0: - cellHeader = "{:<5d} {:<5d} ".format(index, 0) + cellHeader = f"{index:<5d} {0:<5d} " else: self.Materials.add(cell.Material) if abs(cell.Density) < 1e-2: - cellHeader = "{:<5d} {:<5d} {:11.4e} ".format( - index, cell.Material, cell.Density - ) + cellHeader = f"{index:<5d} {cell.Material:<5d} {cell.Density:11.4e} " else: - cellHeader = "{:<5d} {:<5d} {:11.7f} ".format( - index, cell.Material, cell.Density - ) + cellHeader = f"{index:<5d} {cell.Material:<5d} {cell.Density:11.7f} " mcnpcell = "{}{}\n{}{}".format( cellHeader, - self.__cellFormat__(cell.Definition, offset=len(cellHeader)), - self.__optionFormat__(cell), - self.__commentFormat__(cell.Comments, cell.MatInfo), + self.__cell_format__(cell.Definition, offset=len(cellHeader)), + self.__option_format__(cell), + self.__comment_format__(cell.Comments, cell.MatInfo), ) self.inpfile.write(mcnpcell) return @@ -173,12 +178,19 @@ def __write_cells__(self, cell): def __write_surfaces__(self, surface): """Write the surfaces in MCNP format""" - MCNP_def = MCNPSurface(surface.Index, surface.Type, surface.Surf) + MCNP_def = mcnp_surface( + surface.Index, + surface.Type, + surface.Surf, + self.options, + self.tolerances, + self.numeric_format, + ) if MCNP_def: MCNP_def += "\n" self.inpfile.write(MCNP_def) else: - print("Surface {} cannot be written in MCNP input".format(surface.Type)) + logger.info(f"Surface {surface.Type} cannot be written in MCNP input") return def __write_source_block__(self): @@ -186,13 +198,13 @@ def __write_source_block__(self): if self.SDEF_sphere is None: return - MODE = "MODE {}\nVOID \nNPS 1e6\n".format(self.part) + MODE = f"MODE {self.part}\nVOID \nNPS 1e6\n" if self.dummyMat: mat = list(self.Materials) mat.sort() MATCARD = "" for m in mat: - MATCARD += "M{:<6d} 1001 1\n".format(m) + MATCARD += f"M{m:<6d} 1001 1\n" Block = MATCARD + "C \n" + MODE else: Block = MODE @@ -204,9 +216,9 @@ def __write_source_block__(self): for line in self.SDEF_sphere: Block += line - celList, volList = self.__get_solidCellVolume__() + celList, volList = self.__get_solid_cell_volume__() - F4Tally = CardLine("F4:{} ".format(self.part)) + F4Tally = CardLine(f"F4:{self.part} ") F4Tally.extend(celList) SD4 = CardLine("SD4 ", fmt="13.7e") SD4.extend(volList) @@ -224,59 +236,59 @@ def __write_source_block__(self): self.inpfile.write(Block) - def __cellFormat__(self, Definition, offset=11): - return writeMCNPCellDef(Definition, tabspace=11, offset=offset) + def __cell_format__(self, Definition, offset=11): + return write_mcnp_cell_def(Definition, tabspace=11, offset=offset) - def __optionFormat__(self, cell): + def __option_format__(self, cell): option = "" if self.Options["Volume"]: if not cell.Void: - option = "{:11s}Vol={:e}\n".format("", cell.Volume * 1e-3) + option = f"{'':11s}Vol={cell.Volume * 0.001:e}\n" else: - option = "{:11s}Vol=1.0\n".format("") + option = f"{'':11s}Vol=1.0\n" - option += "{:11s}".format("") + option += f"{'':11s}" for p in self.Options["Particle"]: if cell.MatInfo == "Graveyard": - option += "imp:{}=0 ".format(p) + option += f"imp:{p}=0 " else: - option += "imp:{}=1.0 ".format(p) + option += f"imp:{p}=1.0 " if self.Options["Universe"] is not None: - option += "U={} ".format(self.Options["Universe"]) + option += f"U={self.Options['Universe']} " option += "\n" return option - def __commentFormat__(self, cComment, mComment=None): + def __comment_format__(self, cComment, mComment=None): comment = "" if mComment: mComment = mComment.split("\n") for c in mComment: if c: - comment += "{:11s}${}\n".format("", c) + comment += f"{'':11s}${c}\n" if cComment.strip() != "": cComment = cComment.strip().split("\n") for c in cComment: if c: - comment += "{:11s}${}\n".format("", c) + comment += f"{'':11s}${c}\n" return comment - def __commentLine__(self, lineComment): + def __comment_line__(self, lineComment): lineComment = lineComment.strip().split("\n") comment = "" if lineComment: comment = "C \n" for c in lineComment: if c: - comment += "C {}\n".format(c) + comment += f"C {c}\n" comment += "C \n" return comment - def __getSurfaceTable__(self): + def __get_surface_table__(self): self.surfaceTable = {} self.__solidCells__ = 0 self.__cells__ = 0 @@ -289,7 +301,7 @@ def __getSurfaceTable__(self): if CellObj.Material != 0: self.__materials__.add(CellObj.Material) - surf = CellObj.Definition.getSurfacesNumbers() + surf = CellObj.Definition.get_surfaces_numbers() if not CellObj.Void: self.__solidCells__ += 1 for index in surf: @@ -299,61 +311,55 @@ def __getSurfaceTable__(self): self.surfaceTable[index] = {i} return - def __simplifyPlanes__(self, Surfaces): + def __simplify_planes__(self, Surfaces): for p in Surfaces["PX"]: if p.Surf.Axis[0] < 0: p.Surf.Axis = FreeCAD.Vector(1, 0, 0) - self.__changeSurfSign__(p) + self.__change_surf_sign__(p) for p in Surfaces["PY"]: if p.Surf.Axis[1] < 0: p.Surf.Axis = FreeCAD.Vector(0, 1, 0) - self.__changeSurfSign__(p) + self.__change_surf_sign__(p) for p in Surfaces["PZ"]: if p.Surf.Axis[2] < 0: p.Surf.Axis = FreeCAD.Vector(0, 0, 1) - self.__changeSurfSign__(p) + self.__change_surf_sign__(p) - if opt.prnt3PPlane: + if self.options.prnt3PPlane: for p in Surfaces["P"]: if p.Surf.pointDef: - axis, d = pointsToCoeffs(p.Surf.Points) - if isOposite(axis, p.Surf.Axis): - self.__changeSurfSign__(p) + axis, d = points_to_coeffs(p.Surf.Points) + if is_opposite(axis, p.Surf.Axis): + self.__change_surf_sign__(p) return - def __sortedSurfaces__(self, Surfaces): - temp = Surfaces_dict(Surfaces) + def __sorted_surfaces__(self, Surfaces): + temp = SurfacesDict(Surfaces) surfList = [] - for ind in range( - Surfaces.IndexOffset, Surfaces.surfaceNumber + Surfaces.IndexOffset - ): - s = temp.getSurface(ind + 1) + for ind in range(Surfaces.IndexOffset, Surfaces.surfaceNumber + Surfaces.IndexOffset): + s = temp.get_surface(ind + 1) if s is not None: surfList.append(s) - temp.delSurface(ind + 1) + temp.del_surface(ind + 1) return surfList - def __changeSurfSign__(self, p): + def __change_surf_sign__(self, p): if p.Index not in self.surfaceTable.keys(): - print( - "{} Surface {} not used in cell definition)".format(p.Type, p.Index), - p.Surf.Axis, - p.Surf.Position, - ) + logger.info(f"{p.Type} Surface {p.Index} not used in cell definition) {p.Surf.Axis} {p.Surf.Position}") return for ic in self.surfaceTable[p.Index]: - surf = self.Cells[ic].Definition.getSurfacesNumbers() + surf = self.Cells[ic].Definition.get_surfaces_numbers() for s in surf: if s == p.Index: - changeSurfSign(s, self.Cells[ic].Definition) + change_surf_sign(s, self.Cells[ic].Definition) - def __get_solidCellVolume__(self): + def __get_solid_cell_volume__(self): solidList = [] volumeList = [] diff --git a/src/geouned/GEOUNED/Write/OpenMCFormat.py b/src/geouned/GEOUNED/write/openmc_format.py similarity index 54% rename from src/geouned/GEOUNED/Write/OpenMCFormat.py rename to src/geouned/GEOUNED/write/openmc_format.py index a425665a..af19d458 100644 --- a/src/geouned/GEOUNED/Write/OpenMCFormat.py +++ b/src/geouned/GEOUNED/write/openmc_format.py @@ -2,46 +2,51 @@ # Module to write MCNP input # ############################## +import logging +from pathlib import Path + import FreeCAD -from ..CodeVersion import * -from ..Utils.Functions import Surfaces_dict -from ..Utils.Options.Classes import Options as opt -from .Functions import OpenMCSurface, changeSurfSign, writeOpenMCregion +from ..code_version import * +from ..utils.functions import SurfacesDict +from .functions import change_surf_sign, open_mc_surface, write_openmc_region + +logger = logging.getLogger("general_logger") -class OpenMC_input: - def __init__(self, Meta, Surfaces, setting): +class OpenmcInput: + def __init__(self, Meta, Surfaces, options, tolerances, numeric_format): self.Cells = Meta + self.options = options + self.tolerances = tolerances + self.numeric_format = numeric_format - self.__getSurfaceTable__() - self.__simplifyPlanes__(Surfaces) + self.__get_surface_table__() + self.__simplify_planes__(Surfaces) - self.Surfaces = self.__sortedSurfaces__(Surfaces) + self.Surfaces = self.__sorted_surfaces__(Surfaces) self.Materials = set() - def writeXML(self, filename): - print("write OpenMC xml file {}".format(filename)) - self.inpfile = open(filename, "w", encoding="utf-8") - self.__write_xml_header__(filename) - - self.inpfile.write("\n") - self.__write_xml_cell_block__() - self.inpfile.write(" \n") - self.__write_xml_surface_block__() - self.inpfile.write("\n") - - self.inpfile.close() + def write_xml(self, filename): + logger.info(f"write OpenMC xml file {filename}") + Path(filename).parent.mkdir(parents=True, exist_ok=True) + with open(file=filename, mode="w", encoding="utf-8") as self.inpfile: + self.__write_xml_header__() + self.inpfile.write("\n") + self.__write_xml_cell_block__() + self.inpfile.write(" \n") + self.__write_xml_surface_block__() + self.inpfile.write("\n") return - def __write_xml_header__(self, fileout): + def __write_xml_header__(self): Header = "\n" self.inpfile.write(Header) return def __write_xml_cell_block__(self): - for i, cell in enumerate(self.Cells): + for _, cell in enumerate(self.Cells): if cell.MatInfo == "Graveyard": continue self.__write_xml_cells__(cell) @@ -62,10 +67,13 @@ def __write_xml_cells__(self, cell): if cell.Material == 0: matName = "void" else: - matName = "{}".format(cell.Material) + matName = f"{cell.Material}" OMCcell = ' \n'.format( - index, matName, cellName, writeOpenMCregion(cell.Definition, "XML") + index, + matName, + cellName, + write_openmc_region(cell.Definition, self.options, "XML"), ) self.inpfile.write(OMCcell) return @@ -73,47 +81,42 @@ def __write_xml_cells__(self, cell): def __write_xml_surfaces__(self, surface, boundary=False): """Write the surfaces in xml OpenMC format""" - surfType, coeffs = OpenMCSurface(surface.Type, surface.Surf) + surfType, coeffs = open_mc_surface(surface.Type, surface.Surf, self.tolerances, self.numeric_format) if not boundary: - OMCsurf = ' \n'.format( - surface.Index, surfType, coeffs - ) + OMCsurf = ' \n'.format(surface.Index, surfType, coeffs) else: - OMCsurf = ' \n'.format( - surface.Index, surfType, coeffs - ) + OMCsurf = ' \n'.format(surface.Index, surfType, coeffs) self.inpfile.write(OMCsurf) return - def writePY(self, filename): - print("write OpenMC python script {}".format(filename)) + def write_py(self, filename): + logger.info(f"write OpenMC python script {filename}") # get all the materials present in the model for cell in self.Cells: if cell.Material != 0: self.Materials.add(cell.Material) - self.inpfile = open(filename, "w", encoding="utf-8") - self.__write_py_header__(filename) - - if len(self.Materials) > 0: - self.inpfile.write("# Materials setup\n") - self.__write_py_materials__() - self.inpfile.write("\n") + Path(filename).parent.mkdir(parents=True, exist_ok=True) + with open(file=filename, mode="w", encoding="utf-8") as self.inpfile: + self.__write_py_header__() - self.inpfile.write("# Surface setup\n") - self.__write_py_surface_block__() - self.inpfile.write("\n") + if len(self.Materials) > 0: + self.inpfile.write("# Materials setup\n") + self.__write_py_materials__() + self.inpfile.write("\n") - self.inpfile.write("# Cell definition \n") - self.__write_py_cell_block__() + self.inpfile.write("# Surface setup\n") + self.__write_py_surface_block__() + self.inpfile.write("\n") - self.inpfile.close() + self.inpfile.write("# Cell definition \n") + self.__write_py_cell_block__() return - def __write_py_header__(self, fileout): + def __write_py_header__(self): Header = """\ # openMC geometry script generated by GEOUNED @@ -131,11 +134,11 @@ def __write_py_materials__(self): matList = tuple(sorted(self.Materials)) strMat = [] for m in matList: - material = "M{} = openmc.Material(name='M{}')\n".format(m, m) + material = f"M{m} = openmc.Material(name='M{m}')\n" self.inpfile.write(material) - strMat.append("M{}".format(m)) + strMat.append(f"M{m}") - collect = "materials = openmc.Materials([{}])\n".format(", ".join(strMat)) + collect = f"materials = openmc.Materials([{', '.join(strMat)}])\n" self.inpfile.write(collect) self.inpfile.write("materials.export_to_xml()\n") @@ -148,16 +151,19 @@ def __write_py_surface_block__(self): def __write_py_surfaces__(self, surface, boundary=False): """Write the surfaces in python OpenMC format""" - surfType, coeffs = OpenMCSurface( - surface.Type, surface.Surf, outXML=False, quadricForm=opt.quadricPY + surfType, coeffs = open_mc_surface( + surface.Type, + surface.Surf, + self.tolerances, + self.numeric_format, + out_xml=False, + quadricForm=self.options.quadricPY, ) if not boundary: - OMCsurf = "S{} = openmc.{}({})\n".format(surface.Index, surfType, coeffs) + OMCsurf = f"S{surface.Index} = openmc.{surfType}({coeffs})\n" else: - OMCsurf = 'S{} = openmc.{}({}, boundary_type="vacuum")\n'.format( - surface.Index, surfType, coeffs - ) + OMCsurf = 'S{} = openmc.{}({}, boundary_type="vacuum")\n'.format(surface.Index, surfType, coeffs) self.inpfile.write(OMCsurf) return @@ -171,13 +177,9 @@ def __write_py_cell_block__(self): self.__write_py_cells__(cell) if cell.__id__ is None: continue - cellNames.append("C{}".format(cell.label)) + cellNames.append(f"C{cell.label}") - geometry = ( - "\ngeometry = openmc.Geometry([{}])\ngeometry.export_to_xml()\n".format( - ", ".join(cellNames) - ) - ) + geometry = "\ngeometry = openmc.Geometry([{}])\ngeometry.export_to_xml()\n".format(", ".join(cellNames)) self.inpfile.write(geometry) return @@ -191,17 +193,22 @@ def __write_py_cells__(self, cell): if cell.Material == 0: OMCcell = 'C{} = openmc.Cell(name="{}", region={})\n'.format( - index, cellName, writeOpenMCregion(cell.Definition, "PY") + index, + cellName, + write_openmc_region(cell.Definition, self.options, "PY"), ) else: - matName = "M{}".format(cell.Material) + matName = f"M{cell.Material}" OMCcell = 'C{} = openmc.Cell(name="{}", fill={}, region={})\n'.format( - index, cellName, matName, writeOpenMCregion(cell.Definition, "PY") + index, + cellName, + matName, + write_openmc_region(cell.Definition, self.options, "PY"), ) self.inpfile.write(OMCcell) return - def __getSurfaceTable__(self): + def __get_surface_table__(self): self.surfaceTable = {} self.__solidCells__ = 0 self.__cells__ = 0 @@ -214,7 +221,7 @@ def __getSurfaceTable__(self): if CellObj.Material != 0: self.__materials__.add(CellObj.Material) - surf = CellObj.Definition.getSurfacesNumbers() + surf = CellObj.Definition.get_surfaces_numbers() if not CellObj.Void: self.__solidCells__ += 1 for index in surf: @@ -224,48 +231,42 @@ def __getSurfaceTable__(self): self.surfaceTable[index] = {i} return - def __simplifyPlanes__(self, Surfaces): + def __simplify_planes__(self, Surfaces): for p in Surfaces["PX"]: if p.Surf.Axis[0] < 0: p.Surf.Axis = FreeCAD.Vector(1, 0, 0) - self.__changeSurfSign__(p) + self.__change_surf_sign__(p) for p in Surfaces["PY"]: if p.Surf.Axis[1] < 0: p.Surf.Axis = FreeCAD.Vector(0, 1, 0) - self.__changeSurfSign__(p) + self.__change_surf_sign__(p) for p in Surfaces["PZ"]: if p.Surf.Axis[2] < 0: p.Surf.Axis = FreeCAD.Vector(0, 0, 1) - self.__changeSurfSign__(p) + self.__change_surf_sign__(p) return - def __sortedSurfaces__(self, Surfaces): - temp = Surfaces_dict(Surfaces) + def __sorted_surfaces__(self, Surfaces): + temp = SurfacesDict(Surfaces) surfList = [] - for ind in range( - Surfaces.IndexOffset, Surfaces.surfaceNumber + Surfaces.IndexOffset - ): - s = temp.getSurface(ind + 1) + for ind in range(Surfaces.IndexOffset, Surfaces.surfaceNumber + Surfaces.IndexOffset): + s = temp.get_surface(ind + 1) if s is not None: surfList.append(s) - temp.delSurface(ind + 1) + temp.del_surface(ind + 1) return surfList - def __changeSurfSign__(self, p): + def __change_surf_sign__(self, p): if p.Index not in self.surfaceTable.keys(): - print( - "{} Surface {} not used in cell definition)".format(p.Type, p.Index), - p.Surf.Axis, - p.Surf.Position, - ) + logger.info(f"{p.Type} Surface {p.Index} not used in cell definition {p.Surf.Axis} {p.Surf.Position}") return for ic in self.surfaceTable[p.Index]: - surf = self.Cells[ic].Definition.getSurfacesNumbers() + surf = self.Cells[ic].Definition.get_surfaces_numbers() for s in surf: if s == p.Index: - changeSurfSign(s, self.Cells[ic].Definition) + change_surf_sign(s, self.Cells[ic].Definition) diff --git a/src/geouned/GEOUNED/Write/PHITSFormat.py b/src/geouned/GEOUNED/write/phits_format.py similarity index 58% rename from src/geouned/GEOUNED/Write/PHITSFormat.py rename to src/geouned/GEOUNED/write/phits_format.py index 62c60511..34e3077f 100644 --- a/src/geouned/GEOUNED/Write/PHITSFormat.py +++ b/src/geouned/GEOUNED/write/phits_format.py @@ -11,57 +11,80 @@ # 5. Eliminated the only MCNP related parts # 6. Added some comments to remind +import logging import re from datetime import datetime +from pathlib import Path import FreeCAD -from ..CodeVersion import * -from ..Utils.BasicFunctions_part1 import isOposite, pointsToCoeffs -from ..Utils.Functions import Surfaces_dict -from ..Utils.Options.Classes import Options as opt -from ..Write.Functions import ( +from ..code_version import * +from ..utils.basic_functions_part1 import is_opposite, points_to_coeffs +from ..utils.functions import SurfacesDict +from .functions import ( CellString, - PHITSSurface, - changeSurfSign, - writePHITSCellDef, + change_surf_sign, + phits_surface, + write_phits_cell_def, ) - -class PHITS_input: - def __init__(self, Meta, Surfaces, setting): - self.Title = setting["title"] - self.VolSDEF = setting["volSDEF"] - self.VolCARD = setting["volCARD"] - self.U0CARD = setting["UCARD"] - self.DummyMat = setting["dummyMat"] - self.Matfile = setting["matFile"] - self.voidMat = setting["voidMat"] - self.startCell = setting["startCell"] +logger = logging.getLogger("general_logger") + + +class PhitsInput: + def __init__( + self, + Meta, + Surfaces, + options, + tolerances, + numeric_format, + title, + volSDEF, + volCARD, + UCARD, + dummyMat, + stepFile, + matFile, + voidMat, + startCell, + ): + self.Title = title + self.VolSDEF = volSDEF + self.VolCARD = volCARD + self.U0CARD = UCARD + self.DummyMat = dummyMat + self.Matfile = matFile + self.voidMat = voidMat + self.startCell = startCell self.Cells = Meta + self.tolerances = tolerances + self.numeric_format = numeric_format + self.options = options self.Options = {"Volume": self.VolCARD, "Universe": self.U0CARD} - self.StepFile = setting["stepFile"] + self.StepFile = stepFile if isinstance(self.StepFile, (tuple, list)): self.StepFile = "; ".join(self.StepFile) if self.Title == "": self.Title = self.StepFile - self.__getSurfaceTable__() - self.__simplifyPlanes__(Surfaces) + self.__get_surface_table__() + self.__simplify_planes__(Surfaces) - self.Surfaces = self.__sortedSurfaces__(Surfaces) + self.Surfaces = self.__sorted_surfaces__(Surfaces) self.Materials = set() return - def writePHITS(self, filename): - print("write PHITS file {}".format(filename)) - self.inpfile = open(filename, "w", encoding="utf-8") - self.__write_PHITS_header__(filename) + def write_phits(self, filename): + logger.info(f"write PHITS file {filename}") + Path(filename).parent.mkdir(parents=True, exist_ok=True) + with open(file=filename, mode="w", encoding="utf-8") as self.inpfile: + self.__write_phits_header__() - cHeader = """\ + cHeader = """\ $ $ ########################################################## $ CELL DEFINITION @@ -69,11 +92,11 @@ def writePHITS(self, filename): $ [CELL]\n""" - self.inpfile.write(cHeader) - self.__write_PHITS_cell_block__() - self.inpfile.write(" \n") + self.inpfile.write(cHeader) + self.__write_phits_cell_block__() + self.inpfile.write(" \n") - surfaceHeader = """\ + surfaceHeader = """\ $ $ ########################################################## $ SURFACE DEFINITION @@ -81,11 +104,11 @@ def writePHITS(self, filename): $ [SURFACE]\n""" - self.inpfile.write(surfaceHeader) - self.__write_PHITS_surface_block__() - self.inpfile.write(" \n") + self.inpfile.write(surfaceHeader) + self.__write_phits_surface_block__() + self.inpfile.write(" \n") - materialHeader = """\ + materialHeader = """\ $ $ ########################################################## $ MATERIAL DEFINITION @@ -95,12 +118,12 @@ def writePHITS(self, filename): $ [MATERIAL]\n""" - if self.DummyMat: - self.inpfile.write(materialHeader) - self.__write_PHITS_source_block__() - self.inpfile.write(" \n") + if self.DummyMat: + self.inpfile.write(materialHeader) + self.__write_phits_source_block__() + self.inpfile.write(" \n") - volHeader = """\ + volHeader = """\ $ $ ########################################################## $ VOLUME DEFINITION @@ -111,15 +134,14 @@ def writePHITS(self, filename): $ [VOLUME] off\n""" - if self.Options["Volume"]: - self.inpfile.write(volHeader) - self.__write_PHITS__Volume_block__() - self.inpfile.write(" \n") + if self.Options["Volume"]: + self.inpfile.write(volHeader) + self.__write_phits_volume_block__() + self.inpfile.write(" \n") - self.inpfile.close() - return + return - def __write_PHITS_header__(self, fileout): + def __write_phits_header__(self): version = GEOUNED_Version releaseDate = GEOUNED_ReleaseDate @@ -138,44 +160,38 @@ def __write_PHITS_header__(self, fileout): ) ) - Information = """$ + Information = f"""$ $ ************************************************************* -$ Original Step file : {} +$ Original Step file : {self.StepFile} $ -$ Creation Date : {} -$ Solid Cells : {} -$ Total Cells : {} -$ Surfaces : {} -$ Materials : {} +$ Creation Date : {datetime.now()} +$ Solid Cells : {self.__solidCells__} +$ Total Cells : {self.__cells__} +$ Surfaces : {len(self.Surfaces)} +$ Materials : {len(self.__materials__)} $ $ ************************************************************** -\n""".format( - self.StepFile, - datetime.now(), - self.__solidCells__, - self.__cells__, - len(self.Surfaces), - len(self.__materials__), - ) + +""" self.inpfile.write(Header) self.inpfile.write(Information) return - def __write_PHITS_cell_block__(self): + def __write_phits_cell_block__(self): enclenvChk = [] enclenvChk = self.__stepfile_label_chk__(self.StepFile) if enclenvChk: - print("Unified the inner void cell(s) definition") + logger.info("Unified the inner void cell(s) definition") for i, cell in enumerate(self.Cells): - self.__write_PHITS_cells_uniVoidDef__(cell) + self.__write_phits_cells_uni_void_def__(cell) return else: for i, cell in enumerate(self.Cells): - self.__write_PHITS_cells__(cell) + self.__write_phits_cells__(cell) return def __stepfile_label_chk__(self, filename): @@ -183,88 +199,74 @@ def __stepfile_label_chk__(self, filename): enclenvList = [] with open(filename) as f: enclenvList = f.readlines() - enclLabel = re.search( - "enclosure(?P[0-9]+)_(?P[0-9]+)_", str(enclenvList) - ) - envelLabel = re.search( - "envelope(?P[0-9]+)_(?P[0-9]+)_", str(enclenvList) - ) + enclLabel = re.search("enclosure(?P[0-9]+)_(?P[0-9]+)_", str(enclenvList)) + envelLabel = re.search("envelope(?P[0-9]+)_(?P[0-9]+)_", str(enclenvList)) cond1 = enclLabel == None cond2 = envelLabel == None return cond1 and cond2 - def __write_PHITS_surface_block__(self): + def __write_phits_surface_block__(self): for surf in self.Surfaces: - self.__write_PHITS_surfaces__(surf) + self.__write_phits_surfaces__(surf) - def __write_PHITS_cells__(self, cell): + def __write_phits_cells__(self, cell): index = cell.label # if index is None objet not contain cell definition # but a comment to insert between cells if cell.__id__ is None: - comment = self.__commentLine__(cell.Comments) + comment = self.__comment_line__(cell.Comments) self.inpfile.write(comment) return if cell.Material == 0: if cell.MatInfo == "Graveyard": cell.MatInfo = "Outer void" - cellHeader = "{:<5d} {:<5d} ".format(index, -1) + cellHeader = f"{index:<5d} {-1:<5d} " elif cell.MatInfo == "Graveyard_in": cell.MatInfo = "Inner void" if self.voidMat != []: self.Materials.add(self.voidMat[0]) if abs(self.voidMat[1]) < 1e-2: - cellHeader = "{:<5d} {:<5d} {:11.4e} ".format( - index, self.voidMat[0], self.voidMat[1] - ) + cellHeader = "{:<5d} {:<5d} {:11.4e} ".format(index, self.voidMat[0], self.voidMat[1]) else: - cellHeader = "{:<5d} {:<5d} {:11.7f} ".format( - index, self.voidMat[0], self.voidMat[1] - ) + cellHeader = "{:<5d} {:<5d} {:11.7f} ".format(index, self.voidMat[0], self.voidMat[1]) else: - cellHeader = "{:<5d} {:<5d} ".format(index, 0) + cellHeader = f"{index:<5d} {0:<5d} " else: - cellHeader = "{:<5d} {:<5d} ".format(index, 0) + cellHeader = f"{index:<5d} {0:<5d} " else: self.Materials.add(cell.Material) if self.Matfile == "" and cell.EnclosureID != 0: - cellHeader = "{:<5d} {:<5d} c{:<5d} ".format( - index, cell.Material, cell.Material - ) + cellHeader = f"{index:<5d} {cell.Material:<5d} c{cell.Material:<5d} " else: if abs(cell.Density) < 1e-2: - cellHeader = "{:<5d} {:<5d} {:11.4e} ".format( - index, cell.Material, cell.Density - ) + cellHeader = "{:<5d} {:<5d} {:11.4e} ".format(index, cell.Material, cell.Density) else: - cellHeader = "{:<5d} {:<5d} {:11.7f} ".format( - index, cell.Material, cell.Density - ) + cellHeader = "{:<5d} {:<5d} {:11.7f} ".format(index, cell.Material, cell.Density) phitscell = "{}{}\n{}{}".format( cellHeader, - self.__cellFormat__(cell.Definition, offset=len(cellHeader)), - self.__optionFormat__(cell), - self.__commentFormat__(cell.Comments, cell.MatInfo), + self.__cell_format__(cell.Definition, offset=len(cellHeader)), + self.__option_format__(cell), + self.__comment_format__(cell.Comments, cell.MatInfo), ) self.inpfile.write(phitscell) return - def __write_PHITS_cells_uniVoidDef__(self, cell): + def __write_phits_cells_uni_void_def__(self, cell): index = cell.label # if index is None objet not contain cell definition # but a comment to insert between cells if cell.__id__ is None: - comment = self.__commentLine__(cell.Comments) + comment = self.__comment_line__(cell.Comments) self.inpfile.write(comment) return """ @@ -275,7 +277,7 @@ def __write_PHITS_cells_uniVoidDef__(self, cell): # To exclude solid cell(s) from the inner void's defenition, # a string of '#(Solid Cell No.)', or inclsolidCells, # is appended to the new inner void cell definition - # after self.__cellFormat__(cell.Definition) process. + # after self.__cell_format__(cell.Definition) process. """ if cell.Void: @@ -291,45 +293,37 @@ def __write_PHITS_cells_uniVoidDef__(self, cell): eliminated_endVoidIndex = self.__cells__ + self.startCell - 3 if self.startCell == startVoidIndex - 1: - inclSolidCells = "{:1s}#{}".format("", self.startCell) + inclSolidCells = f"{'':1s}#{self.startCell}" else: for i in range(self.startCell, startVoidIndex): - inclSolidCells += "{:1s}#{}".format("", i) + inclSolidCells += f"{'':1s}#{i}" if startVoidIndex == eliminated_endVoidIndex: one_mervoid_str = "VOID CELL {} merged, so the auto-genarated void definition is eliminated\n" cell.Comments = one_mervoid_str.format(startVoidIndex) else: some_mervoid_str = "VOID CELLs {}-{} merged, so the auto-genarated void definitions are eliminated\n" - cell.Comments = some_mervoid_str.format( - startVoidIndex, eliminated_endVoidIndex - ) + cell.Comments = some_mervoid_str.format(startVoidIndex, eliminated_endVoidIndex) if self.voidMat != []: self.Materials.add(self.voidMat[0]) if abs(self.voidMat[1]) < 1e-2: - cellHeader = "{:<5d} {:<5d} {:11.4e} ".format( - index, self.voidMat[0], self.voidMat[1] - ) + cellHeader = "{:<5d} {:<5d} {:11.4e} ".format(index, self.voidMat[0], self.voidMat[1]) else: - cellHeader = "{:<5d} {:<5d} {:11.7f} ".format( - index, self.voidMat[0], self.voidMat[1] - ) + cellHeader = "{:<5d} {:<5d} {:11.7f} ".format(index, self.voidMat[0], self.voidMat[1]) else: - cellHeader = "{:<5d} {:<5d} ".format(index, 0) + cellHeader = f"{index:<5d} {0:<5d} " phitscell = "{}{}\n{}{}\n".format( cellHeader, - self.__new_InnerVoid_Def__( - inclSolidCells, cell.Definition, offset=len(cellHeader) - ), - self.__optionFormat__(cell), - self.__commentFormat__(cell.Comments, cell.MatInfo), + self.__new_inner_void_def__(inclSolidCells, cell.Definition, offset=len(cellHeader)), + self.__option_format__(cell), + self.__comment_format__(cell.Comments, cell.MatInfo), ) self.inpfile.write(phitscell) return elif cell.MatInfo == "Graveyard": - cellHeader = "{:<5d} {:<5d} ".format(index, -1) + cellHeader = f"{index:<5d} {-1:<5d} " cell.MatInfo = "Outer void" else: @@ -339,9 +333,9 @@ def __write_PHITS_cells_uniVoidDef__(self, cell): # To check auto-generated voids, apply this commented out section instead # and comment out above from "if cell.Void:..." to "... else: return" # In addition, if you set volCARD = True and want for all void regions to come apperes in [VOLUME], - # comment out some part in the def __write_PHITS__Volume_block__() section also. + # comment out some part in the def __write_phits_volume_block__() section also. if cell.Material == 0: - print(cell.IsEnclosure) + logger.info(cell.IsEnclosure) if cell.MatInfo == 'Graveyard': cellHeader = '{:<5d} {:<5d} '.format(index,-1) else: @@ -351,40 +345,41 @@ def __write_PHITS_cells_uniVoidDef__(self, cell): else: self.Materials.add(cell.Material) if self.Matfile == "" and cell.EnclosureID != 0: - cellHeader = "{:<5d} {:<5d} c{:<5d} ".format( - index, cell.Material, cell.Material - ) + cellHeader = f"{index:<5d} {cell.Material:<5d} c{cell.Material:<5d} " else: if abs(cell.Density) < 1e-2: - cellHeader = "{:<5d} {:<5d} {:11.4e} ".format( - index, cell.Material, cell.Density - ) + cellHeader = "{:<5d} {:<5d} {:11.4e} ".format(index, cell.Material, cell.Density) else: - cellHeader = "{:<5d} {:<5d} {:11.7f} ".format( - index, cell.Material, cell.Density - ) + cellHeader = "{:<5d} {:<5d} {:11.7f} ".format(index, cell.Material, cell.Density) phitscell = "{}{}\n{}{}".format( cellHeader, - self.__cellFormat__(cell.Definition, offset=len(cellHeader)), - self.__optionFormat__(cell), - self.__commentFormat__(cell.Comments, cell.MatInfo), + self.__cell_format__(cell.Definition, offset=len(cellHeader)), + self.__option_format__(cell), + self.__comment_format__(cell.Comments, cell.MatInfo), ) self.inpfile.write(phitscell) return - def __write_PHITS_surfaces__(self, surface): + def __write_phits_surfaces__(self, surface): """Write the surfaces in PHITS format""" - PHITS_def = PHITSSurface(surface.Index, surface.Type, surface.Surf) + PHITS_def = phits_surface( + surface.Index, + surface.Type, + surface.Surf, + self.options, + self.tolerances, + self.numeric_format, + ) if PHITS_def: PHITS_def += "\n" self.inpfile.write(PHITS_def) else: - print("Surface {} cannot be written in PHITS input".format(surface.Type)) + logger.info(f"Surface {surface.Type} cannot be written in PHITS input") return - def __write_PHITS_source_block__(self): + def __write_phits_source_block__(self): if self.DummyMat: mat = list(self.Materials) @@ -396,9 +391,7 @@ def __write_PHITS_source_block__(self): MATID.append(cell.Material) if self.Matfile == "" and cell.EnclosureID != 0: mismat_comment = "$ Change dummyMat M{}, {} c{} g/cm3 is assigned\n M{:<6d} H 2 O 1\n" - MATCARD += mismat_comment.format( - cell.Material, cell.MatInfo, cell.Material, cell.Material - ) + MATCARD += mismat_comment.format(cell.Material, cell.MatInfo, cell.Material, cell.Material) else: mat_comment = "$ Change dummyMat M{} to {}, Density = {}g/cm3\n M{:<6d} H 2 O 1\n" MATCARD += mat_comment.format( @@ -411,9 +404,9 @@ def __write_PHITS_source_block__(self): self.inpfile.write(Block) - def __write_PHITS__Volume_block__(self): + def __write_phits_volume_block__(self): - vol = "{:5s}reg{:5s}vol\n".format("", "") + vol = f"{'':5s}reg{'':5s}vol\n" startVoidIndex = self.__solidCells__ + self.startCell eliminated_endVoidIndex = self.__cells__ + self.startCell - 3 @@ -426,38 +419,24 @@ def __write_PHITS__Volume_block__(self): if cell.__id__ is not None: if cell.Void and startVoidIndex == eliminated_endVoidIndex: if cell.label == startVoidIndex: - print( - "Eliminated the merged void cell {} from [VOLUME] section".format( - cell.label - ) - ) + logger.info(f"Eliminated the merged void cell {cell.label} from [VOLUME] section") else: - vol += "{:6s}{}{:6s}1.0\n".format("", cell.label, "") + vol += f"{'':6s}{cell.label}{'':6s}1.0\n" elif cell.Void: - if cell.label in range( - startVoidIndex, eliminated_endVoidIndex + 1 - ): - print( - "Eliminated the merged void cell {} from [VOLUME] section".format( - cell.label - ) - ) + if cell.label in range(startVoidIndex, eliminated_endVoidIndex + 1): + logger.info(f"Eliminated the merged void cell {cell.label} from [VOLUME] section") else: - vol += "{:6s}{}{:6s}1.0\n".format("", cell.label, "") + vol += f"{'':6s}{cell.label}{'':6s}1.0\n" else: - vol += "{:6s}{}{:6s}{:6e}\n".format( - "", cell.label, "", cell.Volume * 1e-3 - ) + vol += f"{'':6s}{cell.label}{'':6s}{cell.Volume * 0.001:6e}\n" else: if self.Options["Volume"]: for i, cell in enumerate(self.Cells): if cell.__id__ is not None: if cell.Void: - vol += "{:6s}{}{:6s}1.0\n".format("", cell.label, "") + vol += f"{'':6s}{cell.label}{'':6s}1.0\n" else: - vol += "{:6s}{}{:6s}{:6e}\n".format( - "", cell.label, "", cell.Volume * 1e-3 - ) + vol += "{:6s}{}{:6s}{:6e}\n".format("", cell.label, "", cell.Volume * 1e-3) self.inpfile.write(vol) @@ -476,58 +455,58 @@ def __write_PHITS__Volume_block__(self): self.inpfile.write(vol) """ - def __cellFormat__(self, Definition, offset=11): - return writePHITSCellDef(Definition, tabspace=11, offset=offset) + def __cell_format__(self, Definition, offset=11): + return write_phits_cell_def(Definition, tabspace=11, offset=offset) - def __new_InnerVoid_Def__(self, innerSolidCells, Definition, offset=11): - newInnerVoidDef = self.__cellFormat__(Definition, offset) + def __new_inner_void_def__(self, innerSolidCells, Definition, offset=11): + newInnerVoidDef = self.__cell_format__(Definition, offset) strdef = CellString(tabspace=11) strdef.add(newInnerVoidDef + innerSolidCells) - strdef.wrapLine(offset) + strdef.wrap_line(offset) return strdef.str - def __optionFormat__(self, cell): + def __option_format__(self, cell): option = "" if self.Options["Volume"]: if not cell.Void: - option = "${:11s}Vol={:e} cm3\n".format("", cell.Volume * 1e-3) + option = f"${'':11s}Vol={cell.Volume * 0.001:e} cm3\n" else: - option = "${:11s}Vol=1.0 cm3\n".format("") + option = f"${'':11s}Vol=1.0 cm3\n" if self.Options["Universe"] is not None: - option += "{:11s}U={}\n".format("", self.Options["Universe"]) + option += f"{'':11s}U={self.Options['Universe']}\n" return option - def __commentFormat__(self, cComment, mComment=None): + def __comment_format__(self, cComment, mComment=None): comment = "" if mComment: mComment = mComment.split("\n") for c in mComment: if c: - comment += "{:11s}${}\n".format("", c) + comment += f"{'':11s}${c}\n" if cComment.strip() != "": cComment = cComment.strip().split("\n") for c in cComment: if c: - comment += "{:11s}${}\n".format("", c) + comment += f"{'':11s}${c}\n" return comment - def __commentLine__(self, lineComment): + def __comment_line__(self, lineComment): lineComment = lineComment.strip().split("\n") comment = "" if lineComment: comment = "$ \n" for c in lineComment: if c: - comment += "$ {}\n".format(c) + comment += f"$ {c}\n" comment += "$ \n" return comment - def __getSurfaceTable__(self): + def __get_surface_table__(self): self.surfaceTable = {} self.__solidCells__ = 0 self.__cells__ = 0 @@ -540,7 +519,7 @@ def __getSurfaceTable__(self): if CellObj.Material != 0: self.__materials__.add(CellObj.Material) - surf = CellObj.Definition.getSurfacesNumbers() + surf = CellObj.Definition.get_surfaces_numbers() if not CellObj.Void: self.__solidCells__ += 1 for index in surf: @@ -550,60 +529,54 @@ def __getSurfaceTable__(self): self.surfaceTable[index] = {i} return - def __simplifyPlanes__(self, Surfaces): + def __simplify_planes__(self, Surfaces): for p in Surfaces["PX"]: if p.Surf.Axis[0] < 0: p.Surf.Axis = FreeCAD.Vector(1, 0, 0) - self.__changeSurfSign__(p) + self.__change_surf_sign__(p) for p in Surfaces["PY"]: if p.Surf.Axis[1] < 0: p.Surf.Axis = FreeCAD.Vector(0, 1, 0) - self.__changeSurfSign__(p) + self.__change_surf_sign__(p) for p in Surfaces["PZ"]: if p.Surf.Axis[2] < 0: p.Surf.Axis = FreeCAD.Vector(0, 0, 1) - self.__changeSurfSign__(p) + self.__change_surf_sign__(p) - if opt.prnt3PPlane: + if self.options.prnt3PPlane: for p in Surfaces["P"]: if p.Surf.pointDef: - axis, d = pointsToCoeffs(p.Surf.Points) - if isOposite(axis, p.Surf.Axis): - self.__changeSurfSign__(p) + axis, d = points_to_coeffs(p.Surf.Points) + if is_opposite(axis, p.Surf.Axis): + self.__change_surf_sign__(p) return - def __sortedSurfaces__(self, Surfaces): - temp = Surfaces_dict(Surfaces) + def __sorted_surfaces__(self, Surfaces): + temp = SurfacesDict(Surfaces) surfList = [] - for ind in range( - Surfaces.IndexOffset, Surfaces.surfaceNumber + Surfaces.IndexOffset - ): - s = temp.getSurface(ind + 1) + for ind in range(Surfaces.IndexOffset, Surfaces.surfaceNumber + Surfaces.IndexOffset): + s = temp.get_surface(ind + 1) if s is not None: surfList.append(s) - temp.delSurface(ind + 1) + temp.del_surface(ind + 1) return surfList - def __changeSurfSign__(self, p): + def __change_surf_sign__(self, p): if p.Index not in self.surfaceTable.keys(): - print( - "{} Surface {} not used in cell definition)".format(p.Type, p.Index), - p.Surf.Axis, - p.Surf.Position, - ) + logger.info(f"{p.Type} Surface {p.Index} not used in cell definition) {p.Surf.Axis} {p.Surf.Position}") return for ic in self.surfaceTable[p.Index]: - surf = self.Cells[ic].Definition.getSurfacesNumbers() + surf = self.Cells[ic].Definition.get_surfaces_numbers() for s in surf: if s == p.Index: - changeSurfSign(s, self.Cells[ic].Definition) + change_surf_sign(s, self.Cells[ic].Definition) - def __get_solidCellVolume__(self): + def __get_solid_cell_volume__(self): solidList = [] volumeList = [] diff --git a/src/geouned/GEOUNED/Write/SerpentFormat.py b/src/geouned/GEOUNED/write/serpent_format.py similarity index 64% rename from src/geouned/GEOUNED/Write/SerpentFormat.py rename to src/geouned/GEOUNED/write/serpent_format.py index 82f87132..edd14e6d 100644 --- a/src/geouned/GEOUNED/Write/SerpentFormat.py +++ b/src/geouned/GEOUNED/write/serpent_format.py @@ -1,24 +1,43 @@ ################################# # Module to write Serpent input # ################################# +import logging from datetime import datetime +from pathlib import Path import FreeCAD -from ..CodeVersion import * -from ..Utils.BasicFunctions_part1 import isOposite, pointsToCoeffs -from ..Utils.Functions import Surfaces_dict -from ..Utils.Options.Classes import Options as opt -from .Functions import SerpentSurface, changeSurfSign, writeSerpentCellDef - - -class Serpent_input: - def __init__(self, Meta, Surfaces, setting): - self.Title = setting["title"] - self.VolSDEF = setting["volSDEF"] - self.VolCARD = setting["volCARD"] - self.U0CARD = setting["UCARD"] - self.dummyMat = setting["dummyMat"] +from ..code_version import * +from ..utils.basic_functions_part1 import is_opposite, points_to_coeffs +from ..utils.functions import SurfacesDict +from .functions import change_surf_sign, serpent_surface, write_serpent_cell_def + +logger = logging.getLogger("general_logger") + + +class SerpentInput: + def __init__( + self, + Meta, + Surfaces, + options, + tolerances, + numeric_format, + title, + volSDEF, + volCARD, + UCARD, + dummyMat, + stepFile, + ): + self.options = options + self.tolerances = tolerances + self.numeric_format = numeric_format + self.Title = title + self.VolSDEF = volSDEF + self.VolCARD = volCARD + self.U0CARD = UCARD + self.dummyMat = dummyMat self.Cells = Meta self.Options = { "Volume": self.VolCARD, @@ -27,17 +46,14 @@ def __init__(self, Meta, Surfaces, setting): } self.part = "p" - self.StepFile = setting["stepFile"] + self.StepFile = stepFile if isinstance(self.StepFile, (tuple, list)): self.StepFile = "; ".join(self.StepFile) - if self.Title == "": - self.Title = self.StepFile - - self.__getSurfaceTable__() - self.__simplifyPlanes__(Surfaces) + self.__get_surface_table__() + self.__simplify_planes__(Surfaces) - self.Surfaces = self.__sortedSurfaces__(Surfaces) + self.Surfaces = self.__sorted_surfaces__(Surfaces) self.Materials = set() return @@ -60,62 +76,55 @@ def __init__(self, Meta, Surfaces, setting): # SP3 = 'SP3 0 1 \n' # self.SDEF_box = (sdef,SI1,SI2,SI3,SP1,SP2,SP3) - def writeInput(self, filename): - print(f"write Serpent file {filename}") - self.inpfile = open(filename, "w", encoding="utf-8") - self.__write_header__(filename) - cellblockHeader = """\ + def write_input(self, filename): + logger.info(f"write Serpent file {filename}") + Path(filename).parent.mkdir(parents=True, exist_ok=True) + with open(file=filename, mode="w", encoding="utf-8") as self.inpfile: + self.__write_header__() + cellblockHeader = """\ % --- CELL DEFINITIONS """ - self.inpfile.write(cellblockHeader) - self.__write_cell_block__() - self.inpfile.write(" \n") + self.inpfile.write(cellblockHeader) + self.__write_cell_block__() + self.inpfile.write(" \n") - surfaceHeader = """\ + surfaceHeader = """\ % --- SURFACE DEFINITIONS """ - self.inpfile.write(surfaceHeader) - self.__write_surface_block__() - self.inpfile.write(" \n") + self.inpfile.write(surfaceHeader) + self.__write_surface_block__() + self.inpfile.write(" \n") - self.__write_source_block__() + self.__write_source_block__() - self.inpfile.close() return - def __write_header__(self, fileout): + def __write_header__(self): version = GEOUNED_Version releaseDate = GEOUNED_ReleaseDate freeCAD_Version = "{V[0]:}.{V[1]:}.{V[2]:}".format(V=FreeCAD.Version()) - Header = """{} + Header = f"""{self.Title} % ______ _______ _____ _ _ __ _ _______ ______ -% | ____ |______ | | ___ | | | \ | |______ | \ -% |_____| |______ |_____| |_____| | \_| |______ |_____/ -% Version : {} {} -% FreeCAD Version : {} \n""".format( - self.Title, version, releaseDate, freeCAD_Version - ) +% | ____ |______ | | ___ | | | \\ | |______ | \\ +% |_____| |______ |_____| |_____| | \\_| |______ |_____/ +% Version : {version} {releaseDate} +% FreeCAD Version : {freeCAD_Version} +""" - Information = """% + Information = f"""% % ************************************************************* -% Original Step file : {} +% Original Step file : {self.StepFile} % -% Creation Date : {} -% Solid Cells : {} -% Total Cells : {} -% Surfaces : {} -% Materials : {} +% Creation Date : {datetime.now()} +% Solid Cells : {self.__solidCells__} +% Total Cells : {self.__cells__} +% Surfaces : {len(self.Surfaces)} +% Materials : {len(self.__materials__)} % -% **************************************************************\n""".format( - self.StepFile, - datetime.now(), - self.__solidCells__, - self.__cells__, - len(self.Surfaces), - len(self.__materials__), - ) +% ************************************************************** +""" self.inpfile.write(Header) self.inpfile.write(Information) return @@ -145,16 +154,16 @@ def __write_cells__(self, cell): if self.Options["Universe"] is not None: if cell.Material == 0: cellHeader = ( - f'cell {index:<5d} {self.Options["Universe"]} {"void":<5d} ' + # {"void":<5d} has been removed from the end of the line below + # see issue /~https://github.com/GEOUNED-org/GEOUNED/issues/151 for details + f'cell {index:<5d} {self.Options["Universe"]} ' ) else: self.Materials.add(cell.Material) - cellHeader = ( - f'cell {index:<5d} {self.Options["Universe"]} {cell.Material:<5d} ' - ) + cellHeader = f'cell {index:<5d} {self.Options["Universe"]} {cell.Material:<5d} ' serpent_cell = ( - f"{cellHeader}{self.__cellFormat__(cell.Definition, offset=len(cellHeader))}" + f"{cellHeader}{self.__cell_format__(cell.Definition, offset=len(cellHeader))}" f"{self.comment_format(cell.Comments, cell.MatInfo)}" ) self.inpfile.write(serpent_cell) @@ -168,7 +177,7 @@ def __write_cells__(self, cell): cellHeader = f"cell {index:<5d} 0 {cell.Material:<5d} " serpent_cell = ( - f"{cellHeader}{self.__cellFormat__(cell.Definition, offset=len(cellHeader))}" + f"{cellHeader}{self.__cell_format__(cell.Definition, offset=len(cellHeader))}" f"{self.comment_format(cell.Comments, cell.MatInfo)}" ) self.inpfile.write(serpent_cell) @@ -178,12 +187,19 @@ def __write_cells__(self, cell): def __write_surfaces__(self, surface): """Write the surfaces in Serpent format""" - Serpent_def = SerpentSurface(surface.Index, surface.Type, surface.Surf) + Serpent_def = serpent_surface( + surface.Index, + surface.Type, + surface.Surf, + self.options, + self.tolerances, + self.numeric_format, + ) if Serpent_def: Serpent_def += "\n" self.inpfile.write(Serpent_def) else: - print("Surface {} cannot be written in Serpent input".format(surface.Type)) + logger.info(f"Surface {surface.Type} cannot be written in Serpent input") return # No void all option in Serpent. For now remove addition of source. @@ -197,9 +213,7 @@ def __write_source_block__(self): mat.sort() MATCARD = "" for m in mat: - MATCARD += "mat {:<6d} {:11.4e} \n1001 1 \n".format( - m, self.cell.Density - ) + MATCARD += f"mat {m:<6d} {self.cell.Density:11.4e} \n1001 1 \n" Block = MATCARD + "% \n" + MODE else: Block = MODE @@ -212,7 +226,7 @@ def __write_source_block__(self): # for line in self.SDEF_sphere: # Block += line - # celList,volList = self.__get_solidCellVolume__() + # celList,volList = self.__get_solid_cell_volume__() # F4Tally = CardLine('F4:{} '.format(self.part)) # F4Tally.extend(celList) @@ -232,12 +246,12 @@ def __write_source_block__(self): self.inpfile.write(Block) - def __cellFormat__(self, Definition, offset=11): - return writeSerpentCellDef(Definition, tabspace=11, offset=offset) + def __cell_format__(self, Definition, offset=11): + return write_serpent_cell_def(Definition, tabspace=11, offset=offset) # Function not relevant for Serpent : No importance setting, universes assigned elsewhere. # Volumes only defined on tally cards. - # def __optionFormat__(self,cell): + # def __option_format__(self,cell): # option = '' # if self.Options['Volume']: @@ -265,13 +279,13 @@ def comment_format(self, cComment, mComment=None): mComment = mComment.split("\n") for c in mComment: if c: - comment += "{:11s}%{}\n".format("", c) + comment += f"{'':11s}%{c}\n" if cComment.strip() != "": cComment = cComment.strip().split("\n") for c in cComment: if c: - comment += "{:11s}%{}\n".format("", c) + comment += f"{'':11s}%{c}\n" return comment def comment_line(self, lineComment): @@ -281,11 +295,11 @@ def comment_line(self, lineComment): comment = "% \n" for c in lineComment: if c: - comment += "% {}\n".format(c) + comment += f"% {c}\n" comment += "% \n" return comment - def __getSurfaceTable__(self): + def __get_surface_table__(self): self.surfaceTable = {} self.__solidCells__ = 0 self.__cells__ = 0 @@ -298,7 +312,7 @@ def __getSurfaceTable__(self): if CellObj.Material != 0: self.__materials__.add(CellObj.Material) - surf = CellObj.Definition.getSurfacesNumbers() + surf = CellObj.Definition.get_surfaces_numbers() if not CellObj.Void: self.__solidCells__ += 1 for index in surf: @@ -308,61 +322,55 @@ def __getSurfaceTable__(self): self.surfaceTable[index] = {i} return - def __simplifyPlanes__(self, Surfaces): + def __simplify_planes__(self, Surfaces): for p in Surfaces["PX"]: if p.Surf.Axis[0] < 0: p.Surf.Axis = FreeCAD.Vector(1, 0, 0) - self.__changeSurfSign__(p) + self.__change_surf_sign__(p) for p in Surfaces["PY"]: if p.Surf.Axis[1] < 0: p.Surf.Axis = FreeCAD.Vector(0, 1, 0) - self.__changeSurfSign__(p) + self.__change_surf_sign__(p) for p in Surfaces["PZ"]: if p.Surf.Axis[2] < 0: p.Surf.Axis = FreeCAD.Vector(0, 0, 1) - self.__changeSurfSign__(p) + self.__change_surf_sign__(p) - if opt.prnt3PPlane: + if self.options.prnt3PPlane: for p in Surfaces["P"]: if p.Surf.pointDef: - axis, d = pointsToCoeffs(p.Surf.Points) - if isOposite(axis, p.Surf.Axis): - self.__changeSurfSign__(p) + axis, d = points_to_coeffs(p.Surf.Points) + if is_opposite(axis, p.Surf.Axis): + self.__change_surf_sign__(p) return - def __sortedSurfaces__(self, Surfaces): - temp = Surfaces_dict(Surfaces) + def __sorted_surfaces__(self, Surfaces): + temp = SurfacesDict(Surfaces) surfList = [] - for ind in range( - Surfaces.IndexOffset, Surfaces.surfaceNumber + Surfaces.IndexOffset - ): - s = temp.getSurface(ind + 1) + for ind in range(Surfaces.IndexOffset, Surfaces.surfaceNumber + Surfaces.IndexOffset): + s = temp.get_surface(ind + 1) if s is not None: surfList.append(s) - temp.delSurface(ind + 1) + temp.del_surface(ind + 1) return surfList - def __changeSurfSign__(self, p): + def __change_surf_sign__(self, p): if p.Index not in self.surfaceTable.keys(): - print( - "{} Surface {} not used in cell definition)".format(p.Type, p.Index), - p.Surf.Axis, - p.Surf.Position, - ) + logger.info(f"{p.Type} Surface {p.Index} not used in cell definition) {p.Surf.Axis} {p.Surf.Position}") return for ic in self.surfaceTable[p.Index]: - surf = self.Cells[ic].Definition.getSurfacesNumbers() + surf = self.Cells[ic].Definition.get_surfaces_numbers() for s in surf: if s == p.Index: - changeSurfSign(s, self.Cells[ic].Definition) + change_surf_sign(s, self.Cells[ic].Definition) - def __get_solidCellVolume__(self): + def __get_solid_cell_volume__(self): solidList = [] volumeList = [] diff --git a/src/geouned/GEOUNED/Write/StringFunctions.py b/src/geouned/GEOUNED/write/string_functions.py similarity index 85% rename from src/geouned/GEOUNED/Write/StringFunctions.py rename to src/geouned/GEOUNED/write/string_functions.py index 24c7287d..c16777ad 100644 --- a/src/geouned/GEOUNED/Write/StringFunctions.py +++ b/src/geouned/GEOUNED/write/string_functions.py @@ -37,9 +37,7 @@ intercls = re.compile( r"(?P\))(?P(( *| *((\n *)?\$|\nC)*\n *)[-+]?\d))" ) # closed parenthesis followed by number -interopn = re.compile( - r"(?P\d)(?P(( *| *((\n *)?\$|\nC)*\n *)\())" -) # number followed by opened parenthesis +interopn = re.compile(r"(?P\d)(?P(( *| *((\n *)?\$|\nC)*\n *)\())") # number followed by opened parenthesis intercop = re.compile( r"(?P\))(?P(( *| *((\n *)?\$|\nC)*\n *)\())" ) # closed parenthesis followed by opened parenthesis @@ -49,29 +47,19 @@ mostinner = re.compile(r"\([^\(^\)]*\)") # identify most inner parentheses bracketsemi = re.compile(r"[\]\[;]") # square bracket or semicolon blnkline = re.compile(r"^ *\n", re.M) # identify blank line -contline = re.compile( - r"\n {0,4}(?P[^c^ ])", re.I -) # identify character other than 'C' in fisrt 5 columns +contline = re.compile(r"\n {0,4}(?P[^c^ ])", re.I) # identify character other than 'C' in fisrt 5 columns comdollar = re.compile(r"\n(?P *)\$") # identify dollar on 'blank line' -startgeom = re.compile( - r"(?P^ *)(?P[\-\+\d])" -) # identify beginning of the geomtric part -endgeom = re.compile( - r"(?P\d)(?P *((\n *)?\$|\nc)?(\n *)?$)", re.I -) # identify end of the geomtric part +startgeom = re.compile(r"(?P^ *)(?P[\-\+\d])") # identify beginning of the geomtric part +endgeom = re.compile(r"(?P\d)(?P *((\n *)?\$|\nc)?(\n *)?$)", re.I) # identify end of the geomtric part # endgeom=re.compile(r"(?P\d)(?P *(\$|\nc)?(\n *)?$)",re.I) # identify end of the geomtric part # other -rehash = re.compile( - r"# *(\d+|\()" -) # find beginning of complementary operator (both cell and surf) +rehash = re.compile(r"# *(\d+|\()") # find beginning of complementary operator (both cell and surf) parent = re.compile(r"[\(|\)]") # position of open and close parenthesis (get_hashcell) gline = re.compile( r"(^ ?[\(\):\-\+\d+\.\# ]+|\n {5}[\(\):\-\+\d+\.\# ]+)", re.I ) # valid geometric part of the line (remove/restore_comments) -comments = re.compile( - r"((\n *)?\$|\n *c)", re.I -) # begining of comment part (remove/restore_comments) +comments = re.compile(r"((\n *)?\$|\n *c)", re.I) # begining of comment part (remove/restore_comments) # comments=re.compile(r"\$|\n *c",re.I) # begining of comment part (remove/restore_comments) lastCR = re.compile(r"\n *$") # last newline "\n" on line string @@ -150,13 +138,7 @@ def remove_redundant(geom): cont = True if redundant(m, geom): # remove redundant parentheses - geom = ( - geom[: m.start()] - + " " - + geom[m.start() + 1 : m.end() - 1] - + " " - + geom[m.end() :] - ) + geom = geom[: m.start()] + " " + geom[m.start() + 1 : m.end() - 1] + " " + geom[m.end() :] else: # replace no redundant parentheses by [] and : by ; term = geom[m.start() + 1 : m.end() - 1].replace(":", ";") diff --git a/src/geouned/GEOUNED/write/write_files.py b/src/geouned/GEOUNED/write/write_files.py new file mode 100644 index 00000000..995efcd9 --- /dev/null +++ b/src/geouned/GEOUNED/write/write_files.py @@ -0,0 +1,148 @@ +from . import additional_files as OutFiles +from .mcnp_format import McnpInput +from .openmc_format import OpenmcInput +from .phits_format import PhitsInput +from .serpent_format import SerpentInput + + +def write_geometry( + UniverseBox, + MetaList, + Surfaces, + settings, + options, + tolerances, + numeric_format, + geometryName, + outFormat, + cellCommentFile, + cellSummaryFile, + title, + volSDEF, + volCARD, + UCARD, + dummyMat, + stepFile, +): + + supported_mc_codes = ("mcnp", "openmc_xml", "openmc_py", "serpent", "phits") + for out_format in outFormat: + if out_format not in supported_mc_codes: + msg = f"outFormat {out_format} not in supported MC codes ({supported_mc_codes})" + raise ValueError(msg) + + # write cells comments in file + if cellCommentFile: + OutFiles.comments_write(geometryName, MetaList) + if cellSummaryFile: + OutFiles.summary_write(geometryName, MetaList) + + if "mcnp" in outFormat: + mcnpFilename = geometryName + ".mcnp" + outBox = ( + UniverseBox.XMin, + UniverseBox.XMax, + UniverseBox.YMin, + UniverseBox.YMax, + UniverseBox.ZMin, + UniverseBox.ZMax, + ) + if settings.voidGen: + outSphere = (Surfaces["Sph"][-1].Index, Surfaces["Sph"][-1].Surf.Radius) + else: + outSphere = None + + MCNPfile = McnpInput( + MetaList, + Surfaces, + options, + tolerances, + numeric_format, + title, + volSDEF, + volCARD, + UCARD, + dummyMat, + stepFile, + ) + MCNPfile.set_sdef((outSphere, outBox)) + MCNPfile.write_input(mcnpFilename) + + if "openmc_xml" in outFormat or "openmc_py" in outFormat: + OMCFile = OpenmcInput(MetaList, Surfaces, options, tolerances, numeric_format) + + if "openmc_xml" in outFormat: + omcFilename = geometryName + ".xml" + OMCFile.write_xml(omcFilename) + + if "openmc_py" in outFormat: + omcFilename = geometryName + ".py" + OMCFile.write_py(omcFilename) + + if "serpent" in outFormat: + serpentFilename = geometryName + ".serp" + outBox = ( + UniverseBox.XMin, + UniverseBox.XMax, + UniverseBox.YMin, + UniverseBox.YMax, + UniverseBox.ZMin, + UniverseBox.ZMax, + ) + if settings.voidGen: + outSphere = (Surfaces["Sph"][-1].Index, Surfaces["Sph"][-1].Surf.Radius) + else: + outSphere = None + + Serpentfile = SerpentInput( + MetaList, + Surfaces, + options, + tolerances, + numeric_format, + title, + volSDEF, + volCARD, + UCARD, + dummyMat, + stepFile, + ) + # Serpentfile.set_sdef((outSphere,outBox)) + Serpentfile.write_input(serpentFilename) + + if "phits" in outFormat: + phitsFilename = geometryName + ".inp" + PHITS_outBox = ( + UniverseBox.XMin, + UniverseBox.XMax, + UniverseBox.YMin, + UniverseBox.YMax, + UniverseBox.ZMin, + UniverseBox.ZMax, + ) + if settings.voidGen: + PHITS_outSphere = ( + Surfaces["Sph"][-1].Index, + Surfaces["Sph"][-1].Surf.Radius, + ) + else: + PHITS_outSphere = None + + PHITSfile = PhitsInput( + MetaList, + Surfaces, + options, + tolerances, + numeric_format, + title, + volSDEF, + volCARD, + UCARD, + dummyMat, + stepFile, + matFile=settings.matFile, + voidMat=settings.voidMat, + startCell=settings.startCell, + ) + # PHITSfile.setSDEF_PHITS((PHITS_outSphere,PHITS_outBox)) + PHITSfile.write_phits(phitsFilename) diff --git a/src/geouned/__init__.py b/src/geouned/__init__.py index 897aa4f0..296fc72a 100644 --- a/src/geouned/__init__.py +++ b/src/geouned/__init__.py @@ -1,11 +1,18 @@ +import logging + # this try except attempts to import freecad (lowercase) which is the conda # package name for FreeCAD (mixed case) upon import the conda package appends # the sys path for Conda installed FreeCAD, consequently FreeCAD can then be # found by subsequent import statements through out the code base try: import freecad -except: +except ImportError: pass -from .GEOUNED import * from .GEOReverse import * +from .GEOUNED import * +from .GEOUNED.utils.log_utils import setup_logger + +setup_logger("general_logger", "geouned_general_log.log") +setup_logger("fuzzy_logger", "geouned_fuzzy_log.log") +setup_logger("solids_logger", "geouned_solids_log.log") diff --git a/testing/test.py b/testing/test.py index 63999d94..672bb63d 100644 --- a/testing/test.py +++ b/testing/test.py @@ -1,12 +1,12 @@ -import sys import os import subprocess +import sys from pathlib import Path sys.path.append(str(Path(__file__).parents[1] / "src")) sys.path.append("/usr/lib64/freecad/lib64/") -from geouned import GEOUNED +from geouned import CadToCsg def setInput(inName, inpDir, outDir): @@ -23,8 +23,8 @@ def setInput(inName, inpDir, outDir): if outDir == "": outDir = "." - inName = "{}/{}".format(inpDir, inName) - outName = "{}/{}".format(outDir, filename) + inName = f"{inpDir}/{inName}" + outName = f"{outDir}/{filename}" template = """[Files] title = Input Test @@ -52,9 +52,8 @@ def setInput(inName, inpDir, outDir): inName, outName ) - file = open("config.ini", "w") - file.write(template) - file.close() + with open(file="config.ini", mode="w", encoding="utf-8") as outfile: + outfile.write(template) def getInputList(folder, ext=None): @@ -91,9 +90,7 @@ def runMCNP(path, inpFile): inp = inpFile out = inpFile[0:-1] + "o" mctal = inpFile[0:-1] + "m" - cmd = "cd {} && {} i={} o={} mctal={} xsdir={}".format( - path, code, inp, out, mctal, xsdir - ) + cmd = "cd {} && {} i={} o={} mctal={} xsdir={}".format(path, code, inp, out, mctal, xsdir) os.system(cmd) @@ -157,8 +154,8 @@ def printResults(f, res, lost): def mkGEOInp(inpDir, outDir): for f in getInputList(inpDir, ("stp", "step")): setInput(f, inpDir, outDir) - GEO = GEOUNED(inifile) - GEO.SetOptions() + GEO = CadToCsg() + GEO.set_configuration(inifile) GEO.Start() del GEO diff --git a/tests/config_complete_defaults.json b/tests/config_complete_defaults.json new file mode 100644 index 00000000..ef931ae8 --- /dev/null +++ b/tests/config_complete_defaults.json @@ -0,0 +1,76 @@ +{ + "stepFile": "testing/inputSTEP/BC.stp", + "Options": { + "forceCylinder": false, + "newSplitPlane": true, + "delLastNumber": false, + "enlargeBox": 2.0, + "nPlaneReverse": 0, + "splitTolerance": 0.0, + "scaleUp": true, + "quadricPY": false, + "Facets": false, + "prnt3PPlane": false, + "forceNoOverlap": false + }, + "Tolerances": { + "relativeTol": false, + "relativePrecision": 1e-06, + "value": 1e-06, + "distance": 0.0001, + "angle": 0.0001, + "pln_distance": 0.0001, + "pln_angle": 0.0001, + "cyl_distance": 0.0001, + "cyl_angle": 0.0001, + "sph_distance": 0.0001, + "kne_distance": 0.0001, + "kne_angle": 0.0001, + "tor_distance": 0.0001, + "tor_angle": 0.0001, + "min_area": 0.01 + }, + "NumericFormat": { + "P_abc": "14.7e", + "P_d": "14.7e", + "P_xyz": "14.7e", + "S_r": "14.7e", + "S_xyz": "14.7e", + "C_r": "12f", + "C_xyz": "12f", + "K_xyz": "13.6e", + "K_tan2": "12f", + "T_r": "14.7e", + "T_xyz": "14.7e", + "GQ_1to6": "18.15f", + "GQ_7to9": "18.15f", + "GQ_10": "18.15f" + }, + "Settings": { + "matFile": "", + "voidGen": true, + "debug": false, + "compSolids": true, + "simplify": "no", + "cellRange": [], + "exportSolids": "", + "minVoidSize": 200.0, + "maxSurf": 50, + "maxBracket": 30, + "voidMat": [], + "voidExclude": [], + "startCell": 1, + "startSurf": 1, + "sort_enclosure": false + }, + "export_csg":{ + "title": "Converted with GEOUNED", + "geometryName": "csg", + "outFormat": ["openmc_xml", "openmc_py", "serpent", "phits", "mcnp"], + "volSDEF": false, + "volCARD": true, + "dummyMat": false, + "cellCommentFile": false, + "cellSummaryFile": true + } +} \ No newline at end of file diff --git a/tests/config_minimal.json b/tests/config_minimal.json new file mode 100644 index 00000000..04761d79 --- /dev/null +++ b/tests/config_minimal.json @@ -0,0 +1,3 @@ +{ + "stepFile": "testing/inputSTEP/BC.stp" +} \ No newline at end of file diff --git a/tests/config_non_defaults.json b/tests/config_non_defaults.json new file mode 100644 index 00000000..b8ff5cec --- /dev/null +++ b/tests/config_non_defaults.json @@ -0,0 +1,15 @@ +{ + "stepFile": "testing/inputSTEP/BC.stp", + "Options": { + "forceCylinder": true + }, + "Tolerances": { + "relativePrecision": 2e-6 + }, + "NumericFormat": { + "P_abc": "15.7e" + }, + "Settings": { + "matFile": "non default" + } +} \ No newline at end of file diff --git a/tests/test_convert.py b/tests/test_convert.py index 45e1dc19..2998c565 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -1,11 +1,18 @@ +import os from pathlib import Path import pytest -from geouned import GEOUNED +import geouned path_to_cad = Path("testing/inputSTEP") step_files = list(path_to_cad.rglob("*.stp")) + list(path_to_cad.rglob("*.step")) +# removing two geometries that are particularly slow to convert from CI testing +# these two geometries remain in the test suite for locally testing +if os.getenv("GITHUB_ACTIONS"): + step_files.remove(Path("testing/inputSTEP/large/SCDR.stp")) + step_files.remove(Path("testing/inputSTEP/large/Triangle.stp")) +suffixes = (".mcnp", ".xml", ".inp", ".py", ".serp") @pytest.mark.parametrize("input_step_file", step_files) @@ -17,43 +24,204 @@ def test_conversion(input_step_file): output_dir.mkdir(parents=True, exist_ok=True) output_filename_stem = output_dir / input_step_file.stem - # creates the config file contents - template = ( - "[Files]\n" - "title = 'Input Test'\n" - f"stepFile = {input_step_file.resolve()}\n" - f"geometryName = {output_filename_stem.resolve()}\n" - "outFormat = ('mcnp', 'openMC_XML')\n" - "[Parameters]\n" - "compSolids = False\n" - "volCARD = False\n" - "volSDEF = True\n" - "voidGen = True\n" - "dummyMat = True\n" - "minVoidSize = 100\n" - "cellSummaryFile = False\n" - "cellCommentFile = False\n" - "debug = False\n" - "simplify = no\n" - "[Options]\n" - "forceCylinder = False\n" - "splitTolerance = 0\n" - "newSplitPlane = True\n" - "nPlaneReverse = 0\n" - ) - - with open(output_dir / "config.ini", mode="w") as file: - file.write(template) - - # deletes the output openmc and mcnp output files if it already exists - output_filename_stem.with_suffix(".mcnp").unlink(missing_ok=True) - output_filename_stem.with_suffix(".xml").unlink(missing_ok=True) - - inifile = f"{output_dir/'config.ini'}" - GEO = GEOUNED(inifile) - GEO.SetOptions() - GEO.outFormat = ("mcnp", "openMC_XML") - GEO.Start() - - assert output_filename_stem.with_suffix(".mcnp").exists() - assert output_filename_stem.with_suffix(".xml").exists() + # deletes the output MC files if they already exists + for suffix in suffixes: + output_filename_stem.with_suffix(suffix).unlink(missing_ok=True) + + my_options = geouned.Options( + forceCylinder=False, + newSplitPlane=True, + delLastNumber=False, + enlargeBox=2, + nPlaneReverse=0, + splitTolerance=0, + scaleUp=True, + quadricPY=False, + Facets=False, + prnt3PPlane=False, + forceNoOverlap=False, + ) + + my_tolerances = geouned.Tolerances( + relativeTol=False, + relativePrecision=0.000001, + value=0.000001, + distance=0.0001, + angle=0.0001, + pln_distance=0.0001, + pln_angle=0.0001, + cyl_distance=0.0001, + cyl_angle=0.0001, + sph_distance=0.0001, + kne_distance=0.0001, + kne_angle=0.0001, + tor_distance=0.0001, + tor_angle=0.0001, + min_area=0.01, + ) + + my_numeric_format = geouned.NumericFormat( + P_abc="14.7e", + P_d="14.7e", + P_xyz="14.7e", + S_r="14.7e", + S_xyz="14.7e", + C_r="12f", + C_xyz="12f", + K_xyz="13.6e", + K_tan2="12f", + T_r="14.7e", + T_xyz="14.7e", + GQ_1to6="18.15f", + GQ_7to9="18.15f", + GQ_10="18.15f", + ) + + my_settings = geouned.Settings( + matFile="", + voidGen=True, + debug=False, + compSolids=True, + simplify="no", + cellRange=[], + exportSolids="", + minVoidSize=200.0, # units mm + maxSurf=50, + maxBracket=30, + voidMat=[], + voidExclude=[], + startCell=1, + startSurf=1, + sort_enclosure=False, + ) + + geo = geouned.CadToCsg( + stepFile=f"{input_step_file.resolve()}", + options=my_options, + settings=my_settings, + tolerances=my_tolerances, + numeric_format=my_numeric_format, + ) + + geo.start() + + geo.export_csg( + title="Converted with GEOUNED", + geometryName=f"{output_filename_stem.resolve()}", + outFormat=( + "openmc_xml", + "openmc_py", + "serpent", + "phits", + "mcnp", + ), + volSDEF=True, # changed from the default + volCARD=False, # changed from the default + UCARD=None, + dummyMat=True, # changed from the default + cellCommentFile=False, + cellSummaryFile=False, # changed from the default + ) + + for suffix in suffixes: + assert output_filename_stem.with_suffix(suffix).exists() + + +@pytest.mark.parametrize( + "input_json_file", + ["tests/config_complete_defaults.json", "tests/config_minimal.json"], +) +def test_cad_to_csg_from_json_with_defaults(input_json_file): + + # deletes the output MC files if they already exists + for suffix in suffixes: + Path("csg").with_suffix(suffix).unlink(missing_ok=True) + + my_cad_to_csg = geouned.CadToCsg.from_json(input_json_file) + assert isinstance(my_cad_to_csg, geouned.CadToCsg) + + assert my_cad_to_csg.stepFile == "testing/inputSTEP/BC.stp" + assert my_cad_to_csg.options.forceCylinder == False + assert my_cad_to_csg.tolerances.relativeTol == False + assert my_cad_to_csg.numeric_format.P_abc == "14.7e" + assert my_cad_to_csg.settings.matFile == "" + + for suffix in suffixes: + assert Path("csg").with_suffix(suffix).exists() + + # deletes the output MC files if they already exists + for suffix in suffixes: + Path("csg").with_suffix(suffix).unlink(missing_ok=True) + + my_cad_to_csg.start() + my_cad_to_csg.export_csg() + + +def test_cad_to_csg_from_json_with_non_defaults(): + + # deletes the output MC files if they already exists + for suffix in suffixes: + Path("csg").with_suffix(suffix).unlink(missing_ok=True) + + my_cad_to_csg = geouned.CadToCsg.from_json("tests/config_non_defaults.json") + assert isinstance(my_cad_to_csg, geouned.CadToCsg) + + assert my_cad_to_csg.stepFile == "testing/inputSTEP/BC.stp" + assert my_cad_to_csg.options.forceCylinder == True + assert my_cad_to_csg.tolerances.relativePrecision == 2e-6 + assert my_cad_to_csg.numeric_format.P_abc == "15.7e" + assert my_cad_to_csg.settings.matFile == "non default" + + for suffix in suffixes: + assert Path("csg").with_suffix(suffix).exists() + + # deletes the output MC files if they already exists + for suffix in suffixes: + Path("csg").with_suffix(suffix).unlink(missing_ok=True) + + my_cad_to_csg.start() + my_cad_to_csg.export_csg() + + +def test_writing_to_new_folders(): + """Checks that a folder is created prior to writing output files""" + + geo = geouned.CadToCsg(stepFile="testing/inputSTEP/BC.stp") + geo.start() + + for outformat in ["mcnp", "phits", "serpent", "openmc_xml", "openmc_py"]: + geo.export_csg( + geometryName=f"tests_outputs/new_folder_for_testing_{outformat}/csg", + cellCommentFile=False, + cellSummaryFile=False, + outFormat=[outformat], + ) + geo.export_csg( + geometryName=f"tests_outputs/new_folder_for_testing_{outformat}_cell_comment/csg", + cellCommentFile=True, + cellSummaryFile=False, + outFormat=[outformat], + ) + geo.export_csg( + geometryName=f"tests_outputs/new_folder_for_testing_{outformat}_cell_summary/csg", + cellCommentFile=False, + cellSummaryFile=True, + outFormat=[outformat], + ) + + +def test_with_relative_tol_true(): + + # test to protect against incorrect attribute usage in FreeCAD + # more details /~https://github.com/GEOUNED-org/GEOUNED/issues/154 + + geo = geouned.CadToCsg( + stepFile=f"{step_files[1].resolve()}", + tolerances=geouned.Tolerances(relativeTol=False), + ) + geo.start() + geo = geouned.CadToCsg( + stepFile=f"{step_files[1].resolve()}", + tolerances=geouned.Tolerances(relativeTol=True), + ) + geo.start() diff --git a/tests/test_transport.py b/tests/test_transport.py index f9280040..97c6857c 100644 --- a/tests/test_transport.py +++ b/tests/test_transport.py @@ -12,11 +12,10 @@ import freecad # importing conda package if present except: pass +import openmc import Part import pytest from FreeCAD import Import -import openmc -import openmc openmc.config["cross_sections"] = Path("tests/cross_sections.xml").resolve() @@ -35,6 +34,11 @@ step_files.remove(Path("testing/inputSTEP/DoubleCylinder/placa.stp")) # this face2.stp crashes when loading the geometry.xml +# removing geometries that are particularly slow to convert from CI testing +# these geometries remain in the test suite for locally testing +if os.getenv("GITHUB_ACTIONS"): + step_files.remove(Path("testing/inputSTEP/large/Triangle.stp")) + @pytest.mark.skipif( sys.platform in ["win32", "darwin"], @@ -67,9 +71,7 @@ def test_transport(input_step_file): ) source = openmc.IndependentSource() - source.space = openmc.stats.Box( - lower_left=(llx, lly, llz), upper_right=(urx, ury, urz) - ) + source.space = openmc.stats.Box(lower_left=(llx, lly, llz), upper_right=(urx, ury, urz)) source.energy = openmc.stats.Discrete([14e6], [1]) materials = openmc.Materials() @@ -84,7 +86,7 @@ def test_transport(input_step_file): if os.getenv("GITHUB_ACTIONS"): settings.particles = 100_000 else: - settings.particles = 10_000_000 + settings.particles = 1_000_000 settings.run_mode = "fixed source" settings.source = source model = openmc.Model(geometry, materials, settings) diff --git a/tests/test_volumes.py b/tests/test_volumes.py index 96c856b6..94a65483 100644 --- a/tests/test_volumes.py +++ b/tests/test_volumes.py @@ -4,19 +4,19 @@ that xml files exist in the tests_outputs folder. """ -import sys import math import os +import sys from pathlib import Path try: import freecad # importing conda package if present except: pass +import openmc import Part import pytest from FreeCAD import Import -import openmc with open("cross_sections.xml", "w") as file: file.write( @@ -31,19 +31,24 @@ path_to_cad = Path("testing/inputSTEP") step_files = list(path_to_cad.rglob("*.stp")) + list(path_to_cad.rglob("*.step")) +# this file is removed as it fails the test. An issue has been raised to track step_files.remove(Path("testing/inputSTEP/Misc/rails.stp")) -# # this checks if the tests are being run a github action runner or not +# this checks if the tests are being run a github action runner or not if os.getenv("GITHUB_ACTIONS"): # reduced samples as github action runners have 2 threads and more samples # is not needed for the smaller models tested. This allows the CI to # quickly test the smaller models samples = 4_000_000 rel_tol = 0.05 + # removing two geometries that are particularly slow to convert from CI testing + # these two geometries remain in the test suite for locally testing + step_files.remove(Path("testing/inputSTEP/large/SCDR.stp")) + step_files.remove(Path("testing/inputSTEP/large/Triangle.stp")) else: # samples for local run can be larger as threads is likely to be larger - samples = 400_000_000 + samples = 40_000_000 # acceptable tolerance can also be smaller - rel_tol = 0.01 + rel_tol = 0.04 @pytest.mark.skipif(