-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Split magnet set object #173
base: main
Are you sure you want to change the base?
Changes from all commits
d6fff25
6bf66f5
0cb692b
7de5585
cb8b7ea
9e89233
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -36,7 +36,7 @@ def import_step_cubit(filename, import_dir): | |||||
"""Imports STEP file into Coreform Cubit. | ||||||
|
||||||
Arguments: | ||||||
filename (str): name of STEP input file, excluding '.step' extension. | ||||||
filename (str): name of STEP input file. | ||||||
import_dir (str): directory from which to import STEP file. | ||||||
|
||||||
Returns: | ||||||
|
@@ -55,7 +55,7 @@ def export_step_cubit(filename, export_dir=""): | |||||
"""Export CAD solid as a STEP file via Coreform Cubit. | ||||||
|
||||||
Arguments: | ||||||
filename (str): name of STEP output file, excluding '.step' extension. | ||||||
filename (str): name of STEP output file. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above comment. |
||||||
export_dir (str): directory to which to export the STEP output file | ||||||
(defaults to empty string). | ||||||
""" | ||||||
|
@@ -65,11 +65,28 @@ def export_step_cubit(filename, export_dir=""): | |||||
cubit.cmd(f'export step "{export_path}" overwrite') | ||||||
|
||||||
|
||||||
def import_cub5_cubit(filename, import_dir): | ||||||
"""Imports cub5 file with Coreform Cubit with default import settings. | ||||||
Arguments: | ||||||
filename (str): name of cub5 input file. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be best to specify that the '.cub5' extension should be excluded from the filename |
||||||
import_dir (str): directory from which to import cub5 file. | ||||||
Returns: | ||||||
vol_id (int): Cubit volume ID of imported CAD solid. | ||||||
""" | ||||||
init_cubit() | ||||||
import_path = Path(import_dir) / Path(filename).with_suffix(".cub5") | ||||||
cubit.cmd( | ||||||
f'import cubit "{import_path}" nofreesurfaces attributes_on separate_bodies' | ||||||
) | ||||||
vol_id = cubit.get_last_id("volume") | ||||||
return vol_id | ||||||
|
||||||
|
||||||
def export_cub5(filename, export_dir=""): | ||||||
"""Export cub5 representation of model (native Cubit format). | ||||||
|
||||||
Arguments: | ||||||
filename (str): name of cub5 output file, excluding '.cub5' extension. | ||||||
filename (str): name of cub5 output file. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above comments for STEP file extensions |
||||||
export_dir (str): directory to which to export the cub5 output file | ||||||
(defaults to empty string). | ||||||
""" | ||||||
|
@@ -192,3 +209,22 @@ def export_dagmc_cubit_native( | |||||
# exports | ||||||
if delete_upon_export: | ||||||
cubit.cmd(f"delete mesh volume all propagate") | ||||||
|
||||||
|
||||||
def cubit_importer(filename, import_dir=""): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it might be best to modify this function name to start with a verb to follow our established convention/best practices, such as |
||||||
"""Attempts to open a geometry file with the appropriate cubit_io function, | ||||||
based on file extension | ||||||
Arguments: | ||||||
filename (path): name of the file to import, including the suffix | ||||||
import_dir (str): directory from which to import the file. | ||||||
Returns: | ||||||
vol_id (int): Cubit volume ID of imported CAD solid. | ||||||
""" | ||||||
importers = { | ||||||
".step": import_step_cubit, | ||||||
".stp": import_step_cubit, | ||||||
".cub5": import_cub5_cubit, | ||||||
} | ||||||
filename = Path(filename) | ||||||
vol_id = importers[filename.suffix](filename, import_dir) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To make this compatible with the corresponding import functions, see the below suggestion.
Suggested change
|
||||||
return vol_id |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,92 @@ | |
|
||
|
||
class MagnetSet(object): | ||
"""An object representing a set of modular stellarator magnet coils. | ||
"""A minimum viable class which can be used to build a DAGMC model with | ||
parastell utilizing pre-defined geometry | ||
|
||
Arguments: | ||
geom_filename (path): Path to the predefined magnet geometry | ||
logger (object): logger object (optional, defaults to None). If no | ||
logger is supplied, a default logger will be instantiated. | ||
|
||
Optional Attributes | ||
mat_tag (str): DAGMC material tag to use for magnets in DAGMC | ||
neutronics model (defaults to 'magnets'). | ||
""" | ||
|
||
def __init__(self, geom_filename, logger=None, **kwargs): | ||
self.logger = logger | ||
geom_path = Path(geom_filename).resolve() | ||
self.geom_filename = geom_path.name | ||
self.export_dir = geom_path.parent | ||
self.mat_tag = "magnets" | ||
for name in kwargs.keys() & ("mat_tag"): | ||
self.__setattr__(name, kwargs[name]) | ||
|
||
@property | ||
def logger(self): | ||
return self._logger | ||
|
||
@logger.setter | ||
def logger(self, logger_object): | ||
self._logger = log.check_init(logger_object) | ||
|
||
def import_geom_cubit(self): | ||
"""Import geom file for magnet set into Coreform Cubit.""" | ||
first_vol_id = 1 | ||
if cubit_io.initialized: | ||
first_vol_id += cubit.get_last_id("volume") | ||
|
||
# TODO cubit importer | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this to-do still relevant? |
||
last_vol_id = cubit_io.cubit_importer( | ||
self.geom_filename, self.export_dir | ||
) | ||
|
||
self.volume_ids = list(range(first_vol_id, last_vol_id + 1)) | ||
|
||
def mesh_magnets(self, min_size=20.0, max_size=50.0, max_gradient=1.5): | ||
"""Creates tetrahedral mesh of magnet volumes via Coreform Cubit. | ||
|
||
Arguments: | ||
min_size (float): minimum size of mesh elements (defaults to 20.0). | ||
max_size (float): maximum size of mesh elements (defaults to 50.0). | ||
max_gradient (float): maximum transition in mesh element size | ||
(defaults to 1.5). | ||
""" | ||
self._logger.info("Generating tetrahedral mesh of magnet coils...") | ||
|
||
if not hasattr(self, "volume_ids"): | ||
self.import_geom_cubit() | ||
|
||
volume_ids_str = " ".join(str(id) for id in self.volume_ids) | ||
cubit.cmd(f"volume {volume_ids_str} scheme tetmesh") | ||
cubit.cmd( | ||
f"volume {volume_ids_str} sizing function type skeleton min_size " | ||
f"{min_size} max_size {max_size} max_gradient {max_gradient} " | ||
"min_num_layers_3d 1 min_num_layers_2d 1 min_num_layers_1d 1" | ||
) | ||
cubit.cmd(f"mesh volume {volume_ids_str}") | ||
|
||
def export_mesh(self, mesh_filename="magnet_mesh", export_dir=""): | ||
"""Creates tetrahedral mesh of magnet volumes and exports H5M format | ||
via Coreform Cubit and MOAB. | ||
|
||
Arguments: | ||
mesh_filename (str): name of H5M output file, excluding '.h5m' | ||
extension (optional, defaults to 'magnet_mesh'). | ||
export_dir (str): directory to which to export the H5M output file | ||
(optional, defaults to empty string). | ||
""" | ||
self._logger.info("Exporting mesh H5M file for magnet coils...") | ||
|
||
cubit_io.export_mesh_cubit( | ||
filename=mesh_filename, export_dir=export_dir | ||
) | ||
|
||
|
||
class BuildableMagnetSet(MagnetSet): | ||
"""An object representing a set of modular stellarator magnet coils, and | ||
can use filament data to build 3D step files with cadquery. | ||
|
||
Arguments: | ||
coils_file (str): path to coil filament data file. | ||
|
@@ -104,14 +189,6 @@ def toroidal_extent(self, angle): | |
self._logger.error(e.args[0]) | ||
raise e | ||
|
||
@property | ||
def logger(self): | ||
return self._logger | ||
|
||
@logger.setter | ||
def logger(self, logger_object): | ||
self._logger = log.check_init(logger_object) | ||
|
||
def _instantiate_coils(self): | ||
"""Extracts filament coordinate data from input data file and | ||
instantiates MagnetCoil class objects. | ||
|
@@ -238,18 +315,6 @@ def build_magnet_coils(self): | |
|
||
self._cut_magnets() | ||
|
||
def import_step_cubit(self): | ||
"""Import STEP file for magnet set into Coreform Cubit.""" | ||
first_vol_id = 1 | ||
if cubit_io.initialized: | ||
first_vol_id += cubit.get_last_id("volume") | ||
|
||
last_vol_id = cubit_io.import_step_cubit( | ||
self.step_filename, self.export_dir | ||
) | ||
|
||
self.volume_ids = list(range(first_vol_id, last_vol_id + 1)) | ||
|
||
def export_step(self, step_filename="magnet_set", export_dir=""): | ||
"""Export CAD solids as a STEP file via CadQuery. | ||
|
||
|
@@ -262,56 +327,15 @@ def export_step(self, step_filename="magnet_set", export_dir=""): | |
self._logger.info("Exporting STEP file for magnet coils...") | ||
|
||
self.export_dir = export_dir | ||
self.step_filename = step_filename | ||
self.geom_filename = Path(step_filename).with_suffix(".step") | ||
|
||
export_path = Path(self.export_dir) / Path( | ||
self.step_filename | ||
).with_suffix(".step") | ||
export_path = Path(self.export_dir) / self.geom_filename | ||
|
||
coil_set = cq.Compound.makeCompound( | ||
[coil.solid for coil in self.magnet_coils] | ||
) | ||
cq.exporters.export(coil_set, str(export_path)) | ||
|
||
def mesh_magnets(self, min_size=20.0, max_size=50.0, max_gradient=1.5): | ||
"""Creates tetrahedral mesh of magnet volumes via Coreform Cubit. | ||
|
||
Arguments: | ||
min_size (float): minimum size of mesh elements (defaults to 20.0). | ||
max_size (float): maximum size of mesh elements (defaults to 50.0). | ||
max_gradient (float): maximum transition in mesh element size | ||
(defaults to 1.5). | ||
""" | ||
self._logger.info("Generating tetrahedral mesh of magnet coils...") | ||
|
||
if not hasattr(self, "volume_ids"): | ||
self.import_step_cubit() | ||
|
||
volume_ids_str = " ".join(str(id) for id in self.volume_ids) | ||
cubit.cmd(f"volume {volume_ids_str} scheme tetmesh") | ||
cubit.cmd( | ||
f"volume {volume_ids_str} sizing function type skeleton min_size " | ||
f"{min_size} max_size {max_size} max_gradient {max_gradient} " | ||
"min_num_layers_3d 1 min_num_layers_2d 1 min_num_layers_1d 1" | ||
) | ||
cubit.cmd(f"mesh volume {volume_ids_str}") | ||
|
||
def export_mesh(self, mesh_filename="magnet_mesh", export_dir=""): | ||
"""Creates tetrahedral mesh of magnet volumes and exports H5M format | ||
via Coreform Cubit and MOAB. | ||
|
||
Arguments: | ||
mesh_filename (str): name of H5M output file, excluding '.h5m' | ||
extension (optional, defaults to 'magnet_mesh'). | ||
export_dir (str): directory to which to export the H5M output file | ||
(optional, defaults to empty string). | ||
""" | ||
self._logger.info("Exporting mesh H5M file for magnet coils...") | ||
|
||
cubit_io.export_mesh_cubit( | ||
filename=mesh_filename, export_dir=export_dir | ||
) | ||
|
||
def sort_coils_toroidally(self): | ||
"""Reorders list of coils by toroidal angle on range [-pi, pi]. | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,8 @@ | |
import numpy as np | ||
|
||
import parastell.magnet_coils as magnet_coils | ||
import cubit | ||
from parastell import cubit_io | ||
|
||
|
||
def remove_files(): | ||
|
@@ -29,13 +31,22 @@ def coil_set(): | |
toroidal_extent = 90.0 | ||
sample_mod = 10 | ||
|
||
coil_set_obj = magnet_coils.MagnetSet( | ||
coil_set_obj = magnet_coils.BuildableMagnetSet( | ||
coils_file, width, thickness, toroidal_extent, sample_mod=sample_mod | ||
) | ||
|
||
return coil_set_obj | ||
|
||
|
||
@pytest.fixture | ||
def coil_set_from_geom(): | ||
|
||
geom_file = Path("files_for_tests") / "magnet_set.step" | ||
coil_set_from_geom_obj = magnet_coils.MagnetSet(geom_file) | ||
|
||
return coil_set_from_geom_obj | ||
|
||
|
||
def test_magnet_construction(coil_set): | ||
|
||
width_exp = 40.0 | ||
|
@@ -67,6 +78,11 @@ def test_magnet_construction(coil_set): | |
|
||
def test_magnet_exports(coil_set): | ||
|
||
if cubit_io.initialized: | ||
cubit.cmd("new") | ||
else: | ||
cubit_io.init_cubit() | ||
Comment on lines
+81
to
+84
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this necessary? Not sure how/if pytest isolates these tests |
||
|
||
volume_ids_exp = list(range(1, 2)) | ||
|
||
remove_files() | ||
|
@@ -83,3 +99,38 @@ def test_magnet_exports(coil_set): | |
assert Path("magnet_mesh.h5m").exists() | ||
|
||
remove_files() | ||
|
||
|
||
def test_magnets_from_geom_cubit_import(coil_set_from_geom): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Call |
||
|
||
if cubit_io.initialized: | ||
cubit.cmd("new") | ||
else: | ||
cubit_io.init_cubit() | ||
Comment on lines
+106
to
+109
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question as above |
||
|
||
volume_ids_exp = list(range(1, 2)) | ||
|
||
coil_set_from_geom.import_geom_cubit() | ||
|
||
assert coil_set_from_geom.volume_ids == volume_ids_exp | ||
|
||
cubit.cmd("new") | ||
|
||
coil_set_from_geom.geom_filename = "magnet_set.cub5" | ||
|
||
coil_set_from_geom.import_geom_cubit() | ||
|
||
assert coil_set_from_geom.volume_ids == volume_ids_exp | ||
|
||
|
||
def test_magnets_from_geom_exports(coil_set_from_geom): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Call |
||
|
||
if cubit_io.initialized: | ||
cubit.cmd("new") | ||
else: | ||
cubit_io.init_cubit() | ||
Comment on lines
+128
to
+131
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See above |
||
|
||
coil_set_from_geom.mesh_magnets() | ||
|
||
coil_set_from_geom.export_mesh() | ||
assert Path("magnet_mesh.h5m").exists() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason we've removed the specification that the '.step' extension should be excluded? The function still needs that to be the case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Path(filename).with_suffix(".step")
replaces whatever suffix is there with .step or adds it if no suffix is present
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I didn't know that about
with_suffix
. Neat