From 73ea5ca009e109020a6568cf469acfb644e98226 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Tue, 21 May 2024 11:49:17 +0100 Subject: [PATCH 01/14] moved two methods out of start --- src/geouned/GEOUNED/core.py | 101 ++++++++++++++-------- src/geouned/GEOUNED/utils/data_classes.py | 7 +- 2 files changed, 72 insertions(+), 36 deletions(-) diff --git a/src/geouned/GEOUNED/core.py b/src/geouned/GEOUNED/core.py index abf0f3eb..12d2f687 100644 --- a/src/geouned/GEOUNED/core.py +++ b/src/geouned/GEOUNED/core.py @@ -25,6 +25,9 @@ from .write.write_files import write_geometry logger = logging.getLogger("general_logger") +logger.info("start") +logger.info(f"GEOUNED version {version('geouned')}") +logger.info(f"FreeCAD version {'.'.join(FreeCAD.Version()[:3])}") class CadToCsg: @@ -65,6 +68,8 @@ def __init__( self.numeric_format = numeric_format self.settings = settings + self.meta_list = [] + @property def stepFile(self): return self._stepFile @@ -434,29 +439,37 @@ def set(self, kwrd, value): else: self.__dict__["geometryName"] == value[:-4] - def start(self): + def _load_step_file( + self, + step_file: str, + # TODO consider having discrete indexes (1,5,7) instead of range (1,7) as this offers more flexibility to the user + cell_range: typing.Union[type(None), typing.Tuple[int, int]] = None, + ): + """ + Load STEP file(s) and extract solid volumes and enclosure volumes. - logger.info("start") - freecad_version = ".".join(FreeCAD.Version()[:3]) - logger.info(f"GEOUNED version {version('geouned')} \nFreeCAD version {freecad_version}") + Args: + step_file (str): The path to the STEP file or a list of paths to multiple STEP files. + cell_range (tuple[int, int], optional): A tuple representing the range of solids to select from the original STEP solids. Defaults to None. + + Returns: + tuple: A tuple containing the solid volumes list and enclosure volumes list extracted from the STEP files. + """ - if self.stepFile == "": - raise ValueError("Cannot run the code. Step file name is missing") + logger.info("Start of step file loading phase") - if isinstance(self.stepFile, (tuple, list)): - for stp in self.stepFile: + if isinstance(step_file, (tuple, list)): + for stp in step_file: 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.") + if not path.isfile(step_file): + raise FileNotFoundError(f"Step file {step_file} not found.\nStop.") - startTime = datetime.now() - - if isinstance(self.stepFile, (list, tuple)): - step_files = self.stepFile + if isinstance(step_file, (list, tuple)): + step_files = step_file else: - step_files = [self.stepFile] + step_files = [step_file] MetaChunk = [] EnclosureChunk = [] for stp in tqdm(step_files, desc="Loading CAD files"): @@ -464,30 +477,50 @@ def start(self): Meta, Enclosure = Load.load_cad(stp, self.settings, self.options) MetaChunk.append(Meta) EnclosureChunk.append(Enclosure) - self.meta_list: typing.List[UF.GeounedSolid] = join_meta_lists(MetaChunk) - self.enclosure_list: typing.List[UF.GeounedSolid] = join_meta_lists(EnclosureChunk) + self.meta_list = join_meta_lists(MetaChunk) + self.enclosure_list = join_meta_lists(EnclosureChunk) + + # Select a specific solid range from original STEP solids + if cell_range: + self.meta_list = self.meta_list[cell_range[0] : cell_range[1]] + + logger.info("End of step file loading phase") + + return self.meta_list, self.enclosure_list + + def _export_solids(self, filename: str): + """Export all the solid volumes from the loaded geometry to a STEP file. + + Args: + filename (str): filepath of the output STEP file. + """ + # export in STEP format solids read from input file + if self.meta_list == []: + raise ValueError( + "No solids in CadToCsg.meta_list to export. Try loading the STEP file first with CadToCsg._load_step_file" + ) + solids = [] + for m in self.meta_list: + if m.IsEnclosure: + continue + solids.extend(m.Solids) + Part.makeCompound(solids).exportStep(filename) + + def start(self): + + startTime = datetime.now() + + # sets the self.meta_list and self.enclosure_list + self._load_step_file(step_file=self.stepFile, cell_range=self.settings.cellRange) + + if self.settings.exportSolids: + self._export_solids(filename=self.settings.exportSolids) 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.meta_list = self.meta_list[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.meta_list: - 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 self.enclosure_list: self.UniverseBox = get_universe(self.meta_list + self.enclosure_list) @@ -851,7 +884,7 @@ def print_warning_solids(warnSolids, warnEnclosures): solids_logger.info(lines) -def join_meta_lists(MList): +def join_meta_lists(MList) -> typing.List[UF.GeounedSolid]: newMetaList = MList[0] if MList[0]: diff --git a/src/geouned/GEOUNED/utils/data_classes.py b/src/geouned/GEOUNED/utils/data_classes.py index b29de9a2..ceabfda0 100644 --- a/src/geouned/GEOUNED/utils/data_classes.py +++ b/src/geouned/GEOUNED/utils/data_classes.py @@ -1,3 +1,4 @@ +import typing from numbers import Real @@ -648,7 +649,7 @@ def __init__( compSolids: bool = True, simplify: str = "no", cellRange: list = [], - exportSolids: str = "", + exportSolids: typing.Optional[str] = None, minVoidSize: float = 200.0, # units mm maxSurf: int = 50, maxBracket: int = 30, @@ -744,7 +745,9 @@ def exportSolids(self): @exportSolids.setter def exportSolids(self, exportSolids: str): - if not isinstance(exportSolids, str): + if exportSolids == None: + pass + elif not isinstance(exportSolids, str): raise TypeError(f"geouned.Tolerances.exportSolids should be a str, not a {type(exportSolids)}") self._exportSolids = exportSolids From 5b153529ad35a82afeac62e3d24bd2f5004713ea Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Tue, 21 May 2024 12:27:48 +0100 Subject: [PATCH 02/14] removed unnecessary logger line --- src/geouned/GEOUNED/core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/geouned/GEOUNED/core.py b/src/geouned/GEOUNED/core.py index 12d2f687..a18f0967 100644 --- a/src/geouned/GEOUNED/core.py +++ b/src/geouned/GEOUNED/core.py @@ -25,7 +25,6 @@ from .write.write_files import write_geometry logger = logging.getLogger("general_logger") -logger.info("start") logger.info(f"GEOUNED version {version('geouned')}") logger.info(f"FreeCAD version {'.'.join(FreeCAD.Version()[:3])}") From 0becb4eeaca1399b356a97434a09ea4c857d1c33 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Wed, 22 May 2024 17:25:56 +0100 Subject: [PATCH 03/14] started CsgToCad class definition --- src/geouned/GEOReverse/core.py | 68 ++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/geouned/GEOReverse/core.py diff --git a/src/geouned/GEOReverse/core.py b/src/geouned/GEOReverse/core.py new file mode 100644 index 00000000..c0fba8e4 --- /dev/null +++ b/src/geouned/GEOReverse/core.py @@ -0,0 +1,68 @@ +import typing +import FreeCAD +import Import + +from pathlib import Path + +from .Modules.buildCAD import buildCAD, makeTree +from .Modules.MCNPinput import McnpInput +from .Modules.Objects import CadCell +from .Modules.XMLinput import XmlInput + + +def CsgToCad(): + + def __init__( + self, + ): + pass + + def export_cad( + self, + input_filename, + output_filename, + csg_format: str, + out_box: typing.Tuple[int, int, int, int, int, int] = (-100, -100, -100, 100, 100, 100), + universe_start: int = 0, + level_max: str = "all", + # rangeType + # range + # splitTolerance in the Options class + # mat = this is in the CADselection dictionary but not in the docs https://github.com/GEOUNED-org/GEOUNED/blob/76ef697c7dca6a828c7498996ff3313859c872f2/docs/User_Guide_GEOUNED_v2.0.pdf + ): + + if Path(output_filename).suffix not in ['.stp', '.step']: + raise ValueError(f"output file must have a .stp or .step extension, not {universe_start.suffix}") + + # get geometry definition from OpenMC XML or MCNP input + if csg_format == "mcnp": + geo = McnpInput(input_filename) + elif csg_format == "openmc_xml": + geo = XmlInput(input_filename) + else: + msg = f"input format type {csg_format} is not supported. Supported options are 'openmc_xml' or 'mcnp'" + raise ValueError(msg) + + UnivCell = CadCell() + UnivCell.shape = UnivCell.makeBox(FreeCAD.BoundBox(*out_box)) + + # TODO make these variable names lower case in the downstream code + CADselection = { + "Ustart": universe_start, + "levelMax": level_max, + # "cell": cell + # # "mat": mat + "format": csg_format, + } + + # TODO don't return fails varible, just fail in the method and raise the error there + CADCells, fails = buildCAD(UnivCell, geo, CADselection) + + if fails: + print("failed in conversion", fails) + + CADdoc = FreeCAD.newDocument("converted_with_geouned") + + makeTree(CADdoc, CADCells) + Import.export(CADdoc.Objects[0:1], universe_start) + CADdoc.saveAs(universe_start + ".FCStd") \ No newline at end of file From 0901ac1666310012580d86c3e71dca52b323894d Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Fri, 14 Jun 2024 10:01:41 +0100 Subject: [PATCH 04/14] added class --- src/geouned/GEOReverse/__init__.py | 1 + src/geouned/GEOReverse/core.py | 34 +++++++++++++++++------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/geouned/GEOReverse/__init__.py b/src/geouned/GEOReverse/__init__.py index e69de29b..ba85ca31 100644 --- a/src/geouned/GEOReverse/__init__.py +++ b/src/geouned/GEOReverse/__init__.py @@ -0,0 +1 @@ +from .core import CsgToCad \ No newline at end of file diff --git a/src/geouned/GEOReverse/core.py b/src/geouned/GEOReverse/core.py index c0fba8e4..89ec92d4 100644 --- a/src/geouned/GEOReverse/core.py +++ b/src/geouned/GEOReverse/core.py @@ -11,17 +11,30 @@ def CsgToCad(): - - def __init__( + + def __init__(self): + pass + + def load_csg_file( self, + input_filename: str, + csg_format: str, ): - pass + + # get geometry definition from OpenMC XML or MCNP input + if csg_format == "mcnp": + geo = McnpInput(input_filename) + elif csg_format == "openmc_xml": + geo = XmlInput(input_filename) + else: + msg = f"input format type {csg_format} is not supported. Supported options are 'openmc_xml' or 'mcnp'" + raise ValueError(msg) + self.geo = geo + return geo def export_cad( self, - input_filename, output_filename, - csg_format: str, out_box: typing.Tuple[int, int, int, int, int, int] = (-100, -100, -100, 100, 100, 100), universe_start: int = 0, level_max: str = "all", @@ -34,15 +47,6 @@ def export_cad( if Path(output_filename).suffix not in ['.stp', '.step']: raise ValueError(f"output file must have a .stp or .step extension, not {universe_start.suffix}") - # get geometry definition from OpenMC XML or MCNP input - if csg_format == "mcnp": - geo = McnpInput(input_filename) - elif csg_format == "openmc_xml": - geo = XmlInput(input_filename) - else: - msg = f"input format type {csg_format} is not supported. Supported options are 'openmc_xml' or 'mcnp'" - raise ValueError(msg) - UnivCell = CadCell() UnivCell.shape = UnivCell.makeBox(FreeCAD.BoundBox(*out_box)) @@ -55,7 +59,7 @@ def export_cad( "format": csg_format, } - # TODO don't return fails varible, just fail in the method and raise the error there + # TODO don't return fails variable, just fail in the method and raise the error there CADCells, fails = buildCAD(UnivCell, geo, CADselection) if fails: From 6fcfbb17e9170030b69a78ae7f6c43a65af5ca70 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Fri, 14 Jun 2024 12:11:49 +0100 Subject: [PATCH 05/14] added cli for csgtocad method --- .github/workflows/ci.yml | 14 ++- docs/python_api.rst | 4 + pyproject.toml | 1 + src/geouned/GEOReverse/core.py | 89 +++++++++++++------ .../GEOReverse/scripts/geouned_csgtocad.py | 38 ++++++++ ...=> config_cadtocsg_complete_defaults.json} | 0 ...imal.json => config_cadtocsg_minimal.json} | 0 ...json => config_cadtocsg_non_defaults.json} | 0 tests/config_csgtocad_complete.json | 14 +++ tests/config_csgtocad_minimal.json | 6 ++ tests/csg_files/cylinder_box.mcnp | 87 ++++++++++++++++++ tests/csg_files/cylinder_box.xml | 30 +++++++ tests/{test_convert.py => test_cadtocsg.py} | 0 tests/test_csgtocad.py | 19 ++++ 14 files changed, 272 insertions(+), 30 deletions(-) create mode 100644 src/geouned/GEOReverse/scripts/geouned_csgtocad.py rename tests/{config_complete_defaults.json => config_cadtocsg_complete_defaults.json} (100%) rename tests/{config_minimal.json => config_cadtocsg_minimal.json} (100%) rename tests/{config_non_defaults.json => config_cadtocsg_non_defaults.json} (100%) create mode 100644 tests/config_csgtocad_complete.json create mode 100644 tests/config_csgtocad_minimal.json create mode 100644 tests/csg_files/cylinder_box.mcnp create mode 100644 tests/csg_files/cylinder_box.xml rename tests/{test_convert.py => test_cadtocsg.py} (100%) create mode 100644 tests/test_csgtocad.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e9bf6e1..f45b4de9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,11 +60,17 @@ jobs: run: | python -m pytest -v tests/test_version.py - - name: testing GEOUNED functionality + - name: testing GEOUNED CadToCsg functionality 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 + python -m pytest -v tests/test_cadtocsg.py + geouned_cadtocsg -i tests/config_cadtocsg_complete_defaults.json + geouned_cadtocsg -i tests/config_cadtocsg_non_defaults.json + + - name: testing GEOUNED CsgToCad functionality + run: | + python -m pytest -v tests/test_csgtocad.py + geouned_csgtocad -i tests/config_csgtocad_complete_defaults.json + geouned_csgtocad -i tests/config_csgtocad_non_defaults.json - name: install openmc if: ${{ matrix.os == 'ubuntu-latest'}} diff --git a/docs/python_api.rst b/docs/python_api.rst index 12fa2c9e..73923bea 100644 --- a/docs/python_api.rst +++ b/docs/python_api.rst @@ -23,3 +23,7 @@ Python API reference .. autoclass:: Tolerances :members: :show-inheritance: + +.. autoclass:: CsgToCad + :members: + :show-inheritance: \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index f8fcb9f1..949329ff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,7 @@ docs = [ [project.scripts] geouned_cadtocsg = "geouned.GEOUNED.scripts.geouned_cadtocsg:main" +geouned_csgtocad = "geouned.GEOReverse.scripts.geouned_csgtocad:main" [tool.black] line-length = 128 diff --git a/src/geouned/GEOReverse/core.py b/src/geouned/GEOReverse/core.py index 89ec92d4..db08b1b5 100644 --- a/src/geouned/GEOReverse/core.py +++ b/src/geouned/GEOReverse/core.py @@ -10,17 +10,67 @@ from .Modules.XMLinput import XmlInput -def CsgToCad(): +class CsgToCad(): + """Base class for the conversion of CSG to CAD models + Args: + input_filename (str): The filename and path of the input CSG text file. + csg_format (str): The format of the CSG input file, options are 'openmc_xml' or 'mcnp' + output_filename (str, optional): The filename stem and path of the output file created. + Two files will be created with the '.step' suffix and one with the 'FCStd' suffix. + Defaults to 'cad_from_csg'. + bounding_box (typing.Tuple[int, int, int, int, int, int], optional): The bounding box + coordinates of the CSG geometry. This should encompass the entire CSG geometry. + Format is (xmin, ymin, zmin, xmax, ymax, zmax) Defaults to (-1000, -1000, -1000, + 1000, 1000, 1000). + universe_start (int, optional): The Universe ID to be converted to CAD. If universe_start + is left as 0 then all the universes and any nested universes are converted. If set + then the universe and all its nested universes are converted. Defaults to 0. + level_max (str, optional): Level maximum of nested universe to be translated. If + level_max < highest nested universe level, cells inside the container cell whose + level is level_max will not be translated. This container cell will be the CAD + solid written in the CAD file. Defaults to "all". + cell_range_type (str, optional): Define how to consider the range values. + setting to 'all' results in all the cells with any cell ID being converted (range a no effect). + setting to 'include' results in only cells defined in 'range' being converted. + setting to 'exclude' results in exclude all cells defined in range. Defaults to "all". + cell_range (typing.Tuple[int], optional): list of cells to be included/excluded for the conversion. + Defaults to (). + mat_range_type (str, optional): Define how to consider the range values. + setting to 'all' results in all the materials with any cell ID being converted (range a no effect). + setting to 'include' results in only materials defined in 'range' being converted. + setting to 'exclude' results in exclude all materials defined in range. Defaults to "all". + mat_range (typing.Tuple[int], optional): list of materials to be included/excluded for the conversion. + Defaults to (). + + Raises: + ValueError: If the csg_format is not 'openmc_xml' or 'mcnp' then a ValueError is raised. + """ + def __init__(self): pass - def load_csg_file( + def export_cad( self, input_filename: str, - csg_format: str, + csg_format: str, + output_filename: str = 'cad_from_csg', + bounding_box: typing.Tuple[int, int, int, int, int, int] = (-1000, -1000, -1000, 1000, 1000, 1000), + universe_start: int = 0, + level_max: str = "all", + cell_range_type:str = "all", + cell_range:typing.Tuple[int]=(), + mat_range_type: str = 'all', + mat_range:typing.Tuple[int]=(), + # TODO add these to the method signature + # splitTolerance in the Options class + # mat = this is in the CADselection dictionary but not in the docs https://github.com/GEOUNED-org/GEOUNED/blob/76ef697c7dca6a828c7498996ff3313859c872f2/docs/User_Guide_GEOUNED_v2.0.pdf ): + # TODO check file extensions are correct + # if Path(output_filename).suffix not in ['.stp', '.step']: + # raise ValueError(f"output file must have a .stp or .step extension, not {universe_start.suffix}") + # get geometry definition from OpenMC XML or MCNP input if csg_format == "mcnp": geo = McnpInput(input_filename) @@ -29,34 +79,21 @@ def load_csg_file( else: msg = f"input format type {csg_format} is not supported. Supported options are 'openmc_xml' or 'mcnp'" raise ValueError(msg) - self.geo = geo - return geo - - def export_cad( - self, - output_filename, - out_box: typing.Tuple[int, int, int, int, int, int] = (-100, -100, -100, 100, 100, 100), - universe_start: int = 0, - level_max: str = "all", - # rangeType - # range - # splitTolerance in the Options class - # mat = this is in the CADselection dictionary but not in the docs https://github.com/GEOUNED-org/GEOUNED/blob/76ef697c7dca6a828c7498996ff3313859c872f2/docs/User_Guide_GEOUNED_v2.0.pdf - ): - - if Path(output_filename).suffix not in ['.stp', '.step']: - raise ValueError(f"output file must have a .stp or .step extension, not {universe_start.suffix}") - + UnivCell = CadCell() - UnivCell.shape = UnivCell.makeBox(FreeCAD.BoundBox(*out_box)) + UnivCell.shape = UnivCell.makeBox(FreeCAD.BoundBox(*bounding_box)) # TODO make these variable names lower case in the downstream code CADselection = { "Ustart": universe_start, "levelMax": level_max, - # "cell": cell - # # "mat": mat + "cell": (cell_range_type, cell_range), + "mat": (mat_range_type, mat_range), "format": csg_format, + cell_range_type:cell_range_type, + cell_range:cell_range, + mat_range_type:mat_range_type, + mat_range:mat_range, } # TODO don't return fails variable, just fail in the method and raise the error there @@ -68,5 +105,5 @@ def export_cad( CADdoc = FreeCAD.newDocument("converted_with_geouned") makeTree(CADdoc, CADCells) - Import.export(CADdoc.Objects[0:1], universe_start) - CADdoc.saveAs(universe_start + ".FCStd") \ No newline at end of file + Import.export(CADdoc.Objects[0:1], f'{output_filename}.step') + CADdoc.saveAs(f"{output_filename}.FCStd") \ No newline at end of file diff --git a/src/geouned/GEOReverse/scripts/geouned_csgtocad.py b/src/geouned/GEOReverse/scripts/geouned_csgtocad.py new file mode 100644 index 00000000..a3229686 --- /dev/null +++ b/src/geouned/GEOReverse/scripts/geouned_csgtocad.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +""" +Convert Cad geometry to CSG format for use in Monte Carlo Codes +""" + +import argparse +import json + +from pathlib import Path + +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(): + + if not Path(args.input).exists(): + raise FileNotFoundError(f"config file {args.input} not found") + + with open(args.input) as f: + config = json.load(f) + + geo = geouned.CsgToCad() + geo.export_cad(**config) + + +if __name__ == "__main__": + main() diff --git a/tests/config_complete_defaults.json b/tests/config_cadtocsg_complete_defaults.json similarity index 100% rename from tests/config_complete_defaults.json rename to tests/config_cadtocsg_complete_defaults.json diff --git a/tests/config_minimal.json b/tests/config_cadtocsg_minimal.json similarity index 100% rename from tests/config_minimal.json rename to tests/config_cadtocsg_minimal.json diff --git a/tests/config_non_defaults.json b/tests/config_cadtocsg_non_defaults.json similarity index 100% rename from tests/config_non_defaults.json rename to tests/config_cadtocsg_non_defaults.json diff --git a/tests/config_csgtocad_complete.json b/tests/config_csgtocad_complete.json new file mode 100644 index 00000000..483bf1f7 --- /dev/null +++ b/tests/config_csgtocad_complete.json @@ -0,0 +1,14 @@ +{ + "export_cad":{ + "input_filename": "tests/csg_files/cylinder_box.xml", + "csg_format": "openmc_xml", + "output_filename" : "cad_from_csg", + "bounding_box": [-1000, -1000, -1000, 1000, 1000, 1000], + "universe_start": 0, + "level_max": "all", + "cell_range_type": "all", + "cell_range": [], + "mat_range_type": "all", + "mat_range": [] + } +} \ No newline at end of file diff --git a/tests/config_csgtocad_minimal.json b/tests/config_csgtocad_minimal.json new file mode 100644 index 00000000..7b195bcb --- /dev/null +++ b/tests/config_csgtocad_minimal.json @@ -0,0 +1,6 @@ +{ + "export_cad":{ + "input_filename": "tests/csg_files/cylinder_box.xml", + "csg_format": "openmc_xml" + } +} \ No newline at end of file diff --git a/tests/csg_files/cylinder_box.mcnp b/tests/csg_files/cylinder_box.mcnp new file mode 100644 index 00000000..098038a3 --- /dev/null +++ b/tests/csg_files/cylinder_box.mcnp @@ -0,0 +1,87 @@ +Converted with GEOUNED +C ______ _______ _____ _ _ __ _ _______ ______ +C | ____ |______ | | ___ | | | \ | |______ | \ +C |_____| |______ |_____| |_____| | \_| |______ |_____/ +C Version : 1.0.2.dev38+ga3fd78f.d20240613 +C FreeCAD Version : 0.21.2 +C +C ************************************************************* +C Original Step file : /home/j/GEOUNED/testing/inputSTEP/cylBox.stp +C +C Creation Date : 2024-06-13 14:01:29.607259 +C Solid Cells : 1 +C Total Cells : 4 +C Surfaces : 23 +C Materials : 0 +C +C ************************************************************** +1 0 -14 -6 5:2 6 7 8 9 -1:3 10 11 12 15 16 -4:-15 -13 -10:-15 10 11: + -15 -11 -9 + imp:n=1.0 imp:p=1.0 +C +C ########################################################## +C VOID CELLS +C ########################################################## +C +2 0 17 -18 19 -20 21 -22 (15:-10:-11) (15:11:9) (-8:-7:-2:1:-6:-9) (13: + 15:10) (14:-5:6) (-16:-12:4:-3:-10:-11:-15) + imp:n=1.0 imp:p=1.0 + $Automatic Generated Void Cell. Enclosure(-76.966, -47.115, -22.500, -11.500, -56.992, -40.592) + $Enclosed cells : (1) +3 0 -23 (-17:18:-19:20:-21:22) + imp:n=1.0 imp:p=1.0 + $Graveyard_in +4 0 23 + imp:n=0 imp:p=0 + $Graveyard + +C ########################################################## +C SURFACE DEFINITION +C ########################################################## +1 PY -1.2500035e+01 +2 PY -2.1500035e+01 +3 PY -1.9500035e+01 +4 PY -1.4500035e+01 +5 P 9.4664926e-01 9.5825876e-09 3.2226569e-01 -8.8839591e+01 +6 P 9.4664926e-01 9.5825876e-09 3.2226569e-01 -8.1689591e+01 +7 P -3.2226569e-01 -5.6149002e-11 9.4664926e-01 -3.1338638e+01 +8 P 3.2226569e-01 5.6149002e-11 -9.4664926e-01 2.2338638e+01 +9 P -9.4664926e-01 -9.5825876e-09 -3.2226569e-01 7.0239591e+01 +10 P -9.4664926e-01 -9.5825876e-09 -3.2226569e-01 6.3039591e+01 +11 P 9.4664926e-01 9.5825876e-09 3.2226569e-01 -7.0039591e+01 +12 P -3.2226569e-01 -5.6149002e-11 9.4664926e-01 -3.4238638e+01 +13 P 9.4664926e-01 9.5825876e-09 3.2226569e-01 -6.1239591e+01 +14 GQ 0.103855177195422 0.999999999999999 0.896144822804578 + -0.000000018142699 -0.000000006176279 -0.610145160974434 + -17.298344774043834 34.000069238333026 50.813553217939962 + 1006.753657691386934 +15 GQ 0.103855177195422 0.999999999999999 0.896144822804578 + -0.000000018142699 -0.000000006176279 -0.610145160974434 + -17.298344774043830 34.000069238333033 50.813553217939955 + 995.251157691386766 +16 P 3.2226569e-01 5.6276909e-11 -9.4664926e-01 2.9633723e+01 +17 PX -7.6966386e+01 +18 PX -4.7114745e+01 +19 PY -2.2500035e+01 +20 PY -1.1500035e+01 +21 PZ -5.6992451e+01 +22 PZ -4.0592261e+01 +23 S -6.2040565e+01 -1.7000035e+01 -4.8792356e+01 1.8254058e+01 + +C +MODE P +VOID +NPS 1e6 +PRDMP 2J -1 +C SDEF PAR=P X=D1 Y=D2 Z=D3 +C SI1 -7.6966386e+01 -4.7114745e+01 +C SI2 -2.2500035e+01 -1.1500035e+01 +C SI3 -5.6992451e+01 -4.0592261e+01 +C SP1 0 1 +C SP2 0 1 +C SP3 0 1 +SDEF PAR=P NRM=-1 SUR=23 WGT=1.0468121e+03 DIR=d1 +SI1 0 1 +SP1 -21 1 +F4:P 1 +SD4 1.5208150e+03 \ No newline at end of file diff --git a/tests/csg_files/cylinder_box.xml b/tests/csg_files/cylinder_box.xml new file mode 100644 index 00000000..f62c11f0 --- /dev/null +++ b/tests/csg_files/cylinder_box.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/test_convert.py b/tests/test_cadtocsg.py similarity index 100% rename from tests/test_convert.py rename to tests/test_cadtocsg.py diff --git a/tests/test_csgtocad.py b/tests/test_csgtocad.py new file mode 100644 index 00000000..49db9d55 --- /dev/null +++ b/tests/test_csgtocad.py @@ -0,0 +1,19 @@ +import geouned + +from pathlib import Path + +@pytest.mark.parametrize("csg_format", ['mcnp', 'openmc_xml']) +def test_cylbox_convertion(csg_format): + geo = geouned.CsgToCad() + + # testing/inputSTEP/cylBox.stp has a rough bounding box of + # -1000.0, -500.0, -1000.0, 0,0,0.0 in mm + geo.export_cad( + input_filename='tests_outputs/testing/inputSTEP/cylBox/cylBox.mcnp', + csg_format=csg_format, + bounding_box=[-1000.0, -500.0, -1000.0, 0,0,0.0 ], + # cell_range_type='exclude', + # cell_range=(2,3,4), + output_filename=f'tests_outputs/csgtocad/cylBox_{csg_format}.stp' + ) + assert Path('tests_outputs/csgtocad/cylBox.stp').exists() From c3031ddd28fe07b68ceeeb14fed86201df50b3c5 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Fri, 14 Jun 2024 12:31:44 +0100 Subject: [PATCH 06/14] restructure docs --- .github/workflows/ci.yml | 2 +- ...sage.rst => python_cadtocsg_api_usage.rst} | 0 ...sage.rst => python_cadtocsg_cli_usage.rst} | 0 src/geouned/GEOReverse/core.py | 2 ++ src/geouned/GEOReverse/scripts/__init__.py | 0 .../GEOReverse/scripts/geouned_csgtocad.py | 2 +- tests/test_csgtocad.py | 20 ++++++++++++------- 7 files changed, 17 insertions(+), 9 deletions(-) rename docs/usage/{python_api_usage.rst => python_cadtocsg_api_usage.rst} (100%) rename docs/usage/{python_cli_usage.rst => python_cadtocsg_cli_usage.rst} (100%) create mode 100644 src/geouned/GEOReverse/scripts/__init__.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f45b4de9..6d73a4e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,7 +53,7 @@ jobs: geouned_cadtocsg --help python -c 'import geouned' python -c 'from geouned import CadToCsg' - python -c 'from geouned.GEOReverse import reverse' + python -c 'from geouned import CsgToCad' python -m pip install .[tests] - name: testing package version diff --git a/docs/usage/python_api_usage.rst b/docs/usage/python_cadtocsg_api_usage.rst similarity index 100% rename from docs/usage/python_api_usage.rst rename to docs/usage/python_cadtocsg_api_usage.rst diff --git a/docs/usage/python_cli_usage.rst b/docs/usage/python_cadtocsg_cli_usage.rst similarity index 100% rename from docs/usage/python_cli_usage.rst rename to docs/usage/python_cadtocsg_cli_usage.rst diff --git a/src/geouned/GEOReverse/core.py b/src/geouned/GEOReverse/core.py index db08b1b5..2c94bf3a 100644 --- a/src/geouned/GEOReverse/core.py +++ b/src/geouned/GEOReverse/core.py @@ -79,6 +79,8 @@ def export_cad( else: msg = f"input format type {csg_format} is not supported. Supported options are 'openmc_xml' or 'mcnp'" raise ValueError(msg) + + Path(output_filename).parent.mkdir(parents=True, exist_ok=True) UnivCell = CadCell() UnivCell.shape = UnivCell.makeBox(FreeCAD.BoundBox(*bounding_box)) diff --git a/src/geouned/GEOReverse/scripts/__init__.py b/src/geouned/GEOReverse/scripts/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/geouned/GEOReverse/scripts/geouned_csgtocad.py b/src/geouned/GEOReverse/scripts/geouned_csgtocad.py index a3229686..48bab364 100644 --- a/src/geouned/GEOReverse/scripts/geouned_csgtocad.py +++ b/src/geouned/GEOReverse/scripts/geouned_csgtocad.py @@ -31,7 +31,7 @@ def main(): config = json.load(f) geo = geouned.CsgToCad() - geo.export_cad(**config) + geo.export_cad(**config['export_cad']) if __name__ == "__main__": diff --git a/tests/test_csgtocad.py b/tests/test_csgtocad.py index 49db9d55..020c08db 100644 --- a/tests/test_csgtocad.py +++ b/tests/test_csgtocad.py @@ -1,19 +1,25 @@ -import geouned - from pathlib import Path +import pytest +import geouned @pytest.mark.parametrize("csg_format", ['mcnp', 'openmc_xml']) def test_cylbox_convertion(csg_format): geo = geouned.CsgToCad() - # testing/inputSTEP/cylBox.stp has a rough bounding box of - # -1000.0, -500.0, -1000.0, 0,0,0.0 in mm + if csg_format == 'openmc_xml': + suffix = '.xml' + elif csg_format == 'mcnp': + suffix = '.mcnp' geo.export_cad( - input_filename='tests_outputs/testing/inputSTEP/cylBox/cylBox.mcnp', + # csg file was made from testing/inputSTEP/cylBox.stp + input_filename=f'tests/csg_files/cylinder_box{suffix}', csg_format=csg_format, bounding_box=[-1000.0, -500.0, -1000.0, 0,0,0.0 ], + # TODO add tests for these args that counts volumes in cad file # cell_range_type='exclude', # cell_range=(2,3,4), - output_filename=f'tests_outputs/csgtocad/cylBox_{csg_format}.stp' + output_filename=f'tests_outputs/csgtocad/{csg_format}' ) - assert Path('tests_outputs/csgtocad/cylBox.stp').exists() + + assert Path(f'tests_outputs/csgtocad/{csg_format}.step').exists() + assert Path(f'tests_outputs/csgtocad/{csg_format}.FCStd').exists() From 6e215a99c5305a97eca8684b3b8b6ff5bfa897a2 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Fri, 14 Jun 2024 13:03:11 +0100 Subject: [PATCH 07/14] adding csgtocad to docs --- docs/developer_guide.rst | 3 + docs/usage/index.rst | 6 +- docs/usage/python_cadtocsg_api_usage.rst | 4 +- docs/usage/python_cadtocsg_cli_usage.rst | 6 +- docs/usage/python_csgtocad_api_usage.rst | 22 +++++++ docs/usage/python_csgtocad_cli_usage.rst | 78 ++++++++++++++++++++++++ 6 files changed, 112 insertions(+), 7 deletions(-) create mode 100644 docs/usage/python_csgtocad_api_usage.rst create mode 100644 docs/usage/python_csgtocad_cli_usage.rst diff --git a/docs/developer_guide.rst b/docs/developer_guide.rst index eeea924e..4442c148 100644 --- a/docs/developer_guide.rst +++ b/docs/developer_guide.rst @@ -138,16 +138,19 @@ However we need one more dependency to run the tests. Then we can run the tests with the following command from the root of the repository. .. code-block:: sh + python -m pytest We can run individual test files by specifying the file path .. code-block:: sh + python -m pytest tests/test_convert.py We can run individual test functions by specifying the file path and function name .. code-block:: sh + python -m pytest tests/test_convert.py -k 'test_conversion' Additional pytest options that might be useful are including -s for standard output and -vv for very verbose output. diff --git a/docs/usage/index.rst b/docs/usage/index.rst index 97282c13..157a09ab 100644 --- a/docs/usage/index.rst +++ b/docs/usage/index.rst @@ -10,5 +10,7 @@ GEOUNED can be used as a Python package with the API or as a command line tool w :numbered: :maxdepth: 1 - python_api_usage - python_cli_usage \ No newline at end of file + python_cadtocsg_api_usage + python_cadtocsg_cli_usage + python_csgtocad_api_usage + python_csgtocad_cli_usage diff --git a/docs/usage/python_cadtocsg_api_usage.rst b/docs/usage/python_cadtocsg_api_usage.rst index 43462eaf..eefc973e 100644 --- a/docs/usage/python_cadtocsg_api_usage.rst +++ b/docs/usage/python_cadtocsg_api_usage.rst @@ -1,5 +1,5 @@ -Python Package Usage -==================== +Python Package Usage, CAD to CSG conversion +=========================================== The 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. diff --git a/docs/usage/python_cadtocsg_cli_usage.rst b/docs/usage/python_cadtocsg_cli_usage.rst index 1cf983f0..37d9aa98 100644 --- a/docs/usage/python_cadtocsg_cli_usage.rst +++ b/docs/usage/python_cadtocsg_cli_usage.rst @@ -1,7 +1,7 @@ -Command Line Tool Usage -======================= +Command Line Tool Usage, CAD to CSG conversion +============================================== -GEOUNED can be used in the command line. +GEOUNED CAD to CSG conversion can be performed in the command line. These examples assumes you have a CAD STEP file in the current working directory of the terminal called "cuboid.stp" diff --git a/docs/usage/python_csgtocad_api_usage.rst b/docs/usage/python_csgtocad_api_usage.rst new file mode 100644 index 00000000..f7bf4246 --- /dev/null +++ b/docs/usage/python_csgtocad_api_usage.rst @@ -0,0 +1,22 @@ +Python Package Usage, CSG to CAD conversion +=========================================== + +The main class is ``CsgToCad()`` which converts Constructive Solid Geometry (CSG) to CAD. +There are a few arguments that can be passed into the ``CsgToCad()`` 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 CsgToCad being used to convert a CSG geometry into a STEP CAD file. +The example makes use of default attributes. + +.. code-block:: python + + import geouned + + +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 diff --git a/docs/usage/python_csgtocad_cli_usage.rst b/docs/usage/python_csgtocad_cli_usage.rst new file mode 100644 index 00000000..a55f49dc --- /dev/null +++ b/docs/usage/python_csgtocad_cli_usage.rst @@ -0,0 +1,78 @@ +Command Line Tool Usage, CAD to CSG conversion +============================================== + +GEOUNED CSG to CAD conversion can be performed in the command line. + +Both OpenMC XML CSG and MCNP CSG formats are supported. + +The first example assumes you have an OpenMC XML CSG file in the current working directory of the terminal called "cylinder_box.xml". + +The most minimal use case below shows a minimal config_openmc.json file being used. + +First create a JSON file called "config_openmc.json" containing the following. + +.. code-block:: json + + { + "export_cad":{ + "input_filename": "cylinder_box.xml", + "csg_format": "openmc_xml" + } + } + + +Then execute the command line interface tool to convert your OpenMC XML CSG file to a STEP CAD file with the default configuration. + +.. code-block:: bash + + geouned_csgtocad -i config_openmc.json + +MCNP CSG files can also be converted, this example assumes you have a MCNP CSG file in te current working directory called "cylinder_box.mcnp". + +For MCNP CSG files, the JSON config file should be as follows and is assumed to be named "config_mcnp.json". + + +.. code-block:: json + + { + "export_cad":{ + "input_filename": "cylinder_box.mcnp", + "csg_format": "mcnp" + } + } + +Then execute the command line interface tool to convert your MCNP CSG file to a STEP CAD file with the default configuration. + +.. code-block:: bash + + geouned_csgtocad -i config_mcnp.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 + + { + "export_cad":{ + "input_filename": "tests/csg_files/cylinder_box.xml", + "csg_format": "openmc_xml", + "output_filename" : "cad_from_csg", + "bounding_box": [-1000, -1000, -1000, 1000, 1000, 1000], + "universe_start": 0, + "level_max": "all", + "cell_range_type": "all", + "cell_range": [], + "mat_range_type": "all", + "mat_range": [] + } +} + +.. code-block:: bash + + geouned_csgtocad -i config.json From 5e5450b7b5dea49278965b989df73fce6cb6079c Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Fri, 14 Jun 2024 15:05:28 +0100 Subject: [PATCH 08/14] improved csg to cad examples --- docs/usage/python_cadtocsg_cli_usage.rst | 1 + docs/usage/python_csgtocad_api_usage.rst | 23 +++++++++++++++++++++-- docs/usage/python_csgtocad_cli_usage.rst | 2 +- tests/test_csgtocad.py | 4 +++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/docs/usage/python_cadtocsg_cli_usage.rst b/docs/usage/python_cadtocsg_cli_usage.rst index 37d9aa98..ffdd2c83 100644 --- a/docs/usage/python_cadtocsg_cli_usage.rst +++ b/docs/usage/python_cadtocsg_cli_usage.rst @@ -112,6 +112,7 @@ Here is a complete JSON file specification "cellSummaryFile": true } } + Note that JSON requires ```null``` to be passed in which gets translated to ```None``` in Python. This is converted in the same way as the minimal JSON config file diff --git a/docs/usage/python_csgtocad_api_usage.rst b/docs/usage/python_csgtocad_api_usage.rst index f7bf4246..1c9b2987 100644 --- a/docs/usage/python_csgtocad_api_usage.rst +++ b/docs/usage/python_csgtocad_api_usage.rst @@ -2,7 +2,7 @@ Python Package Usage, CSG to CAD conversion =========================================== The main class is ``CsgToCad()`` which converts Constructive Solid Geometry (CSG) to CAD. -There are a few arguments that can be passed into the ``CsgToCad()`` class which are documented in the `Python API reference section <../python_api.html>`_ of the documentation. +There are a few arguments that can be passed into the ``CsgToCad().export_cad()`` method 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. @@ -13,10 +13,29 @@ The example makes use of default attributes. import geouned + geo = geouned.CsgToCad() -Users can change :meth:`geouned.Options`, :meth:`geouned.Settings`, :meth:`geouned.Tolerances` and :meth:`geouned.NumericFormat` to suit the conversion desired. + geo.export_cad( + csg_format='openmc_xml', + input_filename='cylinder_box.xml', + ) + + +Users can change the default arguments to suit the conversion desired. The following example shows a usage with every attributes specified. +Remember that the arguments are described in the `Python API reference section <../python_api.html>`_ of the documentation. .. code-block:: python import geouned + + geo = geouned.CsgToCad() + + geo.export_cad( + input_filename='cylinder_box.xml', + csg_format='openmc_xml', + bounding_box=[-1000.0, -500.0, -1000.0, 0,0,0.0 ], + cell_range_type='exclude', + cell_range=(2,3,4), + output_filename='openmc_xml', + ) diff --git a/docs/usage/python_csgtocad_cli_usage.rst b/docs/usage/python_csgtocad_cli_usage.rst index a55f49dc..f9ebc98b 100644 --- a/docs/usage/python_csgtocad_cli_usage.rst +++ b/docs/usage/python_csgtocad_cli_usage.rst @@ -70,8 +70,8 @@ Here is a complete JSON file specification "cell_range": [], "mat_range_type": "all", "mat_range": [] + } } -} .. code-block:: bash diff --git a/tests/test_csgtocad.py b/tests/test_csgtocad.py index 020c08db..44de67f5 100644 --- a/tests/test_csgtocad.py +++ b/tests/test_csgtocad.py @@ -4,12 +4,14 @@ @pytest.mark.parametrize("csg_format", ['mcnp', 'openmc_xml']) def test_cylbox_convertion(csg_format): - geo = geouned.CsgToCad() if csg_format == 'openmc_xml': suffix = '.xml' elif csg_format == 'mcnp': suffix = '.mcnp' + + geo = geouned.CsgToCad() + geo.export_cad( # csg file was made from testing/inputSTEP/cylBox.stp input_filename=f'tests/csg_files/cylinder_box{suffix}', From 0295c9505b96bb840fe1fbcbfc68774e7e849b11 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Fri, 14 Jun 2024 15:08:37 +0100 Subject: [PATCH 09/14] format --- src/geouned/GEOReverse/__init__.py | 2 +- src/geouned/GEOReverse/core.py | 30 +++++++++---------- .../GEOReverse/scripts/geouned_csgtocad.py | 4 +-- tests/test_csgtocad.py | 21 ++++++------- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/geouned/GEOReverse/__init__.py b/src/geouned/GEOReverse/__init__.py index ba85ca31..09671254 100644 --- a/src/geouned/GEOReverse/__init__.py +++ b/src/geouned/GEOReverse/__init__.py @@ -1 +1 @@ -from .core import CsgToCad \ No newline at end of file +from .core import CsgToCad diff --git a/src/geouned/GEOReverse/core.py b/src/geouned/GEOReverse/core.py index 2c94bf3a..3388bffc 100644 --- a/src/geouned/GEOReverse/core.py +++ b/src/geouned/GEOReverse/core.py @@ -10,7 +10,7 @@ from .Modules.XMLinput import XmlInput -class CsgToCad(): +class CsgToCad: """Base class for the conversion of CSG to CAD models Args: @@ -46,7 +46,7 @@ class CsgToCad(): Raises: ValueError: If the csg_format is not 'openmc_xml' or 'mcnp' then a ValueError is raised. """ - + def __init__(self): pass @@ -54,14 +54,14 @@ def export_cad( self, input_filename: str, csg_format: str, - output_filename: str = 'cad_from_csg', + output_filename: str = "cad_from_csg", bounding_box: typing.Tuple[int, int, int, int, int, int] = (-1000, -1000, -1000, 1000, 1000, 1000), universe_start: int = 0, level_max: str = "all", - cell_range_type:str = "all", - cell_range:typing.Tuple[int]=(), - mat_range_type: str = 'all', - mat_range:typing.Tuple[int]=(), + cell_range_type: str = "all", + cell_range: typing.Tuple[int] = (), + mat_range_type: str = "all", + mat_range: typing.Tuple[int] = (), # TODO add these to the method signature # splitTolerance in the Options class # mat = this is in the CADselection dictionary but not in the docs https://github.com/GEOUNED-org/GEOUNED/blob/76ef697c7dca6a828c7498996ff3313859c872f2/docs/User_Guide_GEOUNED_v2.0.pdf @@ -79,9 +79,9 @@ def export_cad( else: msg = f"input format type {csg_format} is not supported. Supported options are 'openmc_xml' or 'mcnp'" raise ValueError(msg) - + Path(output_filename).parent.mkdir(parents=True, exist_ok=True) - + UnivCell = CadCell() UnivCell.shape = UnivCell.makeBox(FreeCAD.BoundBox(*bounding_box)) @@ -92,10 +92,10 @@ def export_cad( "cell": (cell_range_type, cell_range), "mat": (mat_range_type, mat_range), "format": csg_format, - cell_range_type:cell_range_type, - cell_range:cell_range, - mat_range_type:mat_range_type, - mat_range:mat_range, + cell_range_type: cell_range_type, + cell_range: cell_range, + mat_range_type: mat_range_type, + mat_range: mat_range, } # TODO don't return fails variable, just fail in the method and raise the error there @@ -107,5 +107,5 @@ def export_cad( CADdoc = FreeCAD.newDocument("converted_with_geouned") makeTree(CADdoc, CADCells) - Import.export(CADdoc.Objects[0:1], f'{output_filename}.step') - CADdoc.saveAs(f"{output_filename}.FCStd") \ No newline at end of file + Import.export(CADdoc.Objects[0:1], f"{output_filename}.step") + CADdoc.saveAs(f"{output_filename}.FCStd") diff --git a/src/geouned/GEOReverse/scripts/geouned_csgtocad.py b/src/geouned/GEOReverse/scripts/geouned_csgtocad.py index 48bab364..1288d9f6 100644 --- a/src/geouned/GEOReverse/scripts/geouned_csgtocad.py +++ b/src/geouned/GEOReverse/scripts/geouned_csgtocad.py @@ -29,9 +29,9 @@ def main(): with open(args.input) as f: config = json.load(f) - + geo = geouned.CsgToCad() - geo.export_cad(**config['export_cad']) + geo.export_cad(**config["export_cad"]) if __name__ == "__main__": diff --git a/tests/test_csgtocad.py b/tests/test_csgtocad.py index 44de67f5..79ae59d1 100644 --- a/tests/test_csgtocad.py +++ b/tests/test_csgtocad.py @@ -2,26 +2,27 @@ import pytest import geouned -@pytest.mark.parametrize("csg_format", ['mcnp', 'openmc_xml']) + +@pytest.mark.parametrize("csg_format", ["mcnp", "openmc_xml"]) def test_cylbox_convertion(csg_format): - if csg_format == 'openmc_xml': - suffix = '.xml' - elif csg_format == 'mcnp': - suffix = '.mcnp' + if csg_format == "openmc_xml": + suffix = ".xml" + elif csg_format == "mcnp": + suffix = ".mcnp" geo = geouned.CsgToCad() geo.export_cad( # csg file was made from testing/inputSTEP/cylBox.stp - input_filename=f'tests/csg_files/cylinder_box{suffix}', + input_filename=f"tests/csg_files/cylinder_box{suffix}", csg_format=csg_format, - bounding_box=[-1000.0, -500.0, -1000.0, 0,0,0.0 ], + bounding_box=[-1000.0, -500.0, -1000.0, 0, 0, 0.0], # TODO add tests for these args that counts volumes in cad file # cell_range_type='exclude', # cell_range=(2,3,4), - output_filename=f'tests_outputs/csgtocad/{csg_format}' + output_filename=f"tests_outputs/csgtocad/{csg_format}", ) - assert Path(f'tests_outputs/csgtocad/{csg_format}.step').exists() - assert Path(f'tests_outputs/csgtocad/{csg_format}.FCStd').exists() + assert Path(f"tests_outputs/csgtocad/{csg_format}.step").exists() + assert Path(f"tests_outputs/csgtocad/{csg_format}.FCStd").exists() From 42f2e7b24b4b1b07857f988aee2e15ab52b5a667 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Fri, 14 Jun 2024 15:16:07 +0100 Subject: [PATCH 10/14] corrected json path --- docs/python_api.rst | 8 ++++---- tests/test_cadtocsg.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/python_api.rst b/docs/python_api.rst index 73923bea..72e5f834 100644 --- a/docs/python_api.rst +++ b/docs/python_api.rst @@ -4,6 +4,10 @@ Python API reference .. currentmodule:: geouned +.. autoclass:: CsgToCad + :members: + :show-inheritance: + .. autoclass:: CadToCsg :members: :show-inheritance: @@ -21,9 +25,5 @@ Python API reference :show-inheritance: .. autoclass:: Tolerances - :members: - :show-inheritance: - -.. autoclass:: CsgToCad :members: :show-inheritance: \ No newline at end of file diff --git a/tests/test_cadtocsg.py b/tests/test_cadtocsg.py index a84be0ad..26137237 100644 --- a/tests/test_cadtocsg.py +++ b/tests/test_cadtocsg.py @@ -129,7 +129,7 @@ def test_conversion(input_step_file): @pytest.mark.parametrize( "input_json_file", - ["tests/config_complete_defaults.json", "tests/config_minimal.json"], + ["tests/config_cadtocsg_complete_defaults.json", "tests/config_cadtocsg_minimal.json"], ) def test_cad_to_csg_from_json_with_defaults(input_json_file): @@ -156,7 +156,7 @@ def test_cad_to_csg_from_json_with_non_defaults(): 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") + my_cad_to_csg = geouned.CadToCsg.from_json("tests/config_cadtocsg_non_defaults.json") assert isinstance(my_cad_to_csg, geouned.CadToCsg) assert my_cad_to_csg.filename == "testing/inputSTEP/BC.stp" From 96ca8a38f327e8a75fc8caed3ecf299e5400cd16 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Fri, 14 Jun 2024 15:25:56 +0100 Subject: [PATCH 11/14] moved doc string to method --- src/geouned/GEOReverse/core.py | 71 +++++++++++++++++----------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/src/geouned/GEOReverse/core.py b/src/geouned/GEOReverse/core.py index 3388bffc..ac587a72 100644 --- a/src/geouned/GEOReverse/core.py +++ b/src/geouned/GEOReverse/core.py @@ -11,41 +11,7 @@ class CsgToCad: - """Base class for the conversion of CSG to CAD models - - Args: - input_filename (str): The filename and path of the input CSG text file. - csg_format (str): The format of the CSG input file, options are 'openmc_xml' or 'mcnp' - output_filename (str, optional): The filename stem and path of the output file created. - Two files will be created with the '.step' suffix and one with the 'FCStd' suffix. - Defaults to 'cad_from_csg'. - bounding_box (typing.Tuple[int, int, int, int, int, int], optional): The bounding box - coordinates of the CSG geometry. This should encompass the entire CSG geometry. - Format is (xmin, ymin, zmin, xmax, ymax, zmax) Defaults to (-1000, -1000, -1000, - 1000, 1000, 1000). - universe_start (int, optional): The Universe ID to be converted to CAD. If universe_start - is left as 0 then all the universes and any nested universes are converted. If set - then the universe and all its nested universes are converted. Defaults to 0. - level_max (str, optional): Level maximum of nested universe to be translated. If - level_max < highest nested universe level, cells inside the container cell whose - level is level_max will not be translated. This container cell will be the CAD - solid written in the CAD file. Defaults to "all". - cell_range_type (str, optional): Define how to consider the range values. - setting to 'all' results in all the cells with any cell ID being converted (range a no effect). - setting to 'include' results in only cells defined in 'range' being converted. - setting to 'exclude' results in exclude all cells defined in range. Defaults to "all". - cell_range (typing.Tuple[int], optional): list of cells to be included/excluded for the conversion. - Defaults to (). - mat_range_type (str, optional): Define how to consider the range values. - setting to 'all' results in all the materials with any cell ID being converted (range a no effect). - setting to 'include' results in only materials defined in 'range' being converted. - setting to 'exclude' results in exclude all materials defined in range. Defaults to "all". - mat_range (typing.Tuple[int], optional): list of materials to be included/excluded for the conversion. - Defaults to (). - - Raises: - ValueError: If the csg_format is not 'openmc_xml' or 'mcnp' then a ValueError is raised. - """ + """Base class for the conversion of CSG to CAD models""" def __init__(self): pass @@ -66,6 +32,41 @@ def export_cad( # splitTolerance in the Options class # mat = this is in the CADselection dictionary but not in the docs https://github.com/GEOUNED-org/GEOUNED/blob/76ef697c7dca6a828c7498996ff3313859c872f2/docs/User_Guide_GEOUNED_v2.0.pdf ): + """export the CSG geometry in OpenMC or MCNP format to a CAD model. + + Args: + input_filename (str): The filename and path of the input CSG text file. + csg_format (str): The format of the CSG input file, options are 'openmc_xml' or 'mcnp' + output_filename (str, optional): The filename stem and path of the output file created. + Two files will be created with the '.step' suffix and one with the 'FCStd' suffix. + Defaults to 'cad_from_csg'. + bounding_box (typing.Tuple[int, int, int, int, int, int], optional): The bounding box + coordinates of the CSG geometry. This should encompass the entire CSG geometry. + Format is (xmin, ymin, zmin, xmax, ymax, zmax) Defaults to (-1000, -1000, -1000, + 1000, 1000, 1000). + universe_start (int, optional): The Universe ID to be converted to CAD. If universe_start + is left as 0 then all the universes and any nested universes are converted. If set + then the universe and all its nested universes are converted. Defaults to 0. + level_max (str, optional): Level maximum of nested universe to be translated. If + level_max < highest nested universe level, cells inside the container cell whose + level is level_max will not be translated. This container cell will be the CAD + solid written in the CAD file. Defaults to "all". + cell_range_type (str, optional): Define how to consider the range values. + setting to 'all' results in all the cells with any cell ID being converted (range a no effect). + setting to 'include' results in only cells defined in 'range' being converted. + setting to 'exclude' results in exclude all cells defined in range. Defaults to "all". + cell_range (typing.Tuple[int], optional): list of cells to be included/excluded for the conversion. + Defaults to (). + mat_range_type (str, optional): Define how to consider the range values. + setting to 'all' results in all the materials with any cell ID being converted (range a no effect). + setting to 'include' results in only materials defined in 'range' being converted. + setting to 'exclude' results in exclude all materials defined in range. Defaults to "all". + mat_range (typing.Tuple[int], optional): list of materials to be included/excluded for the conversion. + Defaults to (). + + Raises: + ValueError: If the csg_format is not 'openmc_xml' or 'mcnp' then a ValueError is raised. + """ # TODO check file extensions are correct # if Path(output_filename).suffix not in ['.stp', '.step']: From 131618b3a75d275ba5e5e6ae31b0a0a087fbca79 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Fri, 14 Jun 2024 15:29:21 +0100 Subject: [PATCH 12/14] corrected file path --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6d73a4e9..fab46788 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,8 +69,8 @@ jobs: - name: testing GEOUNED CsgToCad functionality run: | python -m pytest -v tests/test_csgtocad.py - geouned_csgtocad -i tests/config_csgtocad_complete_defaults.json - geouned_csgtocad -i tests/config_csgtocad_non_defaults.json + geouned_csgtocad -i tests/config_csgtocad_complete.json + geouned_csgtocad -i tests/config_csgtocad_minimal.json - name: install openmc if: ${{ matrix.os == 'ubuntu-latest'}} From ba8ab2317f0b3d6c9cc139f825e164129e86b376 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Fri, 14 Jun 2024 16:22:22 +0100 Subject: [PATCH 13/14] quotes used in dict --- docs/usage/python_csgtocad_cli_usage.rst | 2 +- src/geouned/GEOReverse/core.py | 13 +++++++------ tests/config_csgtocad_complete.json | 9 ++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/usage/python_csgtocad_cli_usage.rst b/docs/usage/python_csgtocad_cli_usage.rst index f9ebc98b..83d955e7 100644 --- a/docs/usage/python_csgtocad_cli_usage.rst +++ b/docs/usage/python_csgtocad_cli_usage.rst @@ -1,4 +1,4 @@ -Command Line Tool Usage, CAD to CSG conversion +Command Line Tool Usage, CSG to CAD conversion ============================================== GEOUNED CSG to CAD conversion can be performed in the command line. diff --git a/src/geouned/GEOReverse/core.py b/src/geouned/GEOReverse/core.py index ac587a72..6bf59b62 100644 --- a/src/geouned/GEOReverse/core.py +++ b/src/geouned/GEOReverse/core.py @@ -87,16 +87,17 @@ def export_cad( UnivCell.shape = UnivCell.makeBox(FreeCAD.BoundBox(*bounding_box)) # TODO make these variable names lower case in the downstream code + CADselection = { "Ustart": universe_start, "levelMax": level_max, - "cell": (cell_range_type, cell_range), - "mat": (mat_range_type, mat_range), + "cell": [cell_range_type, cell_range], + "mat": [mat_range_type, mat_range], "format": csg_format, - cell_range_type: cell_range_type, - cell_range: cell_range, - mat_range_type: mat_range_type, - mat_range: mat_range, + "cell_range_type": cell_range_type, + "cell_range": cell_range, + "mat_range_type": mat_range_type, + "mat_range": mat_range, } # TODO don't return fails variable, just fail in the method and raise the error there diff --git a/tests/config_csgtocad_complete.json b/tests/config_csgtocad_complete.json index 483bf1f7..33798df9 100644 --- a/tests/config_csgtocad_complete.json +++ b/tests/config_csgtocad_complete.json @@ -1,14 +1,13 @@ { - "export_cad":{ + "export_cad": { "input_filename": "tests/csg_files/cylinder_box.xml", "csg_format": "openmc_xml", - "output_filename" : "cad_from_csg", + "output_filename": "cad_from_csg", "bounding_box": [-1000, -1000, -1000, 1000, 1000, 1000], "universe_start": 0, "level_max": "all", "cell_range_type": "all", - "cell_range": [], - "mat_range_type": "all", - "mat_range": [] + "cell_range": [1,2], + "mat_range_type": "all" } } \ No newline at end of file From ab3fb32839439fa5e734d7fb50cdab628d35d1e4 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Sat, 15 Jun 2024 11:24:13 +0100 Subject: [PATCH 14/14] removed old code --- src/geouned/GEOReverse/reverse.py | 71 ------------------------------- 1 file changed, 71 deletions(-) delete mode 100644 src/geouned/GEOReverse/reverse.py diff --git a/src/geouned/GEOReverse/reverse.py b/src/geouned/GEOReverse/reverse.py deleted file mode 100644 index faaf432a..00000000 --- a/src/geouned/GEOReverse/reverse.py +++ /dev/null @@ -1,71 +0,0 @@ -import FreeCAD -import Import - -from .CodeVersion import * -from .Modules.buildCAD import buildCAD, makeTree -from .Modules.MCNPinput import McnpInput -from .Modules.Objects import CadCell -from .Modules.processInp import setOptions -from .Modules.XMLinput import XmlInput - - -def reverse(optFile="configRevese.ini"): - - printCodeVersion() - - setting = setOptions(optFile) - - geomfile = setting["fileIn"] - outname = setting["fileOut"] - outBox = setting["outBox"] - inFormat = setting["inFormat"] - - CADselection = { - "Ustart": setting["UStart"], - "levelMax": setting["levelMax"], - "cell": setting["cell"], - "mat": setting["mat"], - "format": setting["inFormat"], - } - - 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) - else: - 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) - - if fails: - print("failed in conversion", fails) - - CADdoc = FreeCAD.newDocument("WorkingDoc") - - makeTree(CADdoc, CADCells) - Import.export(CADdoc.Objects[0:1], outname + ".stp") - CADdoc.saveAs(outname + ".FCStd") - - -def printCodeVersion(): - - FreeCAD_Version = "{V[0]:}.{V[1]:}.{V[2]:}".format(V=FreeCAD.Version()) - title = """\ -######################################################################### -# # -# GEOReverse version {:<11}{}{:>26} -# FreeCAD version {:<11}{:>36} -# # -#########################################################################""".format( - GEOReverse_Version, GEOReverse_ReleaseDate, "#", FreeCAD_Version, "#" - ) - print(title) - - -if __name__ == "__main__": - reverse()