diff --git a/Examples/custom_source_example.py b/Examples/custom_source_example.py index a7fea9c..53113cf 100644 --- a/Examples/custom_source_example.py +++ b/Examples/custom_source_example.py @@ -5,20 +5,23 @@ # Parastell supports defining custom plasma conditions and reaction rates. -# These can be passed as keyword arguments both to SourceMesh and to +# These can be passed as keyword arguments both to SourceMesh and to # Stellarator.construct_source_mesh(). Included is an example of defining -# custom (non-physical) plasma conditions and reaction rates. See the +# custom (non-physical) plasma conditions and reaction rates. See the # docstrings in source_mesh.py for additional information. + def my_custom_plasma_conditions(s): - T_i = np.sin(s*np.pi) + T_i = np.sin(s * np.pi) n_i = 1 return n_i, T_i + def my_custom_reaction_rate(n_i, T_i): - return n_i*T_i + return n_i * T_i + -vmec_file = 'wout_vmec.nc' +vmec_file = "wout_vmec.nc" vmec_obj = read_vmec.VMECData(vmec_file) @@ -29,12 +32,12 @@ def my_custom_reaction_rate(n_i, T_i): vmec_obj, mesh_size, toroidal_extent, - plasma_conditions = my_custom_plasma_conditions, - reaction_rate = my_custom_reaction_rate + plasma_conditions=my_custom_plasma_conditions, + reaction_rate=my_custom_reaction_rate, ) source_mesh_obj.create_vertices() source_mesh_obj.create_mesh() # export and convert to vtk for visualization source_mesh_obj.export_mesh() -subprocess.run('mbconvert source_mesh.h5m source_mesh.vtk', shell=True) \ No newline at end of file +subprocess.run("mbconvert source_mesh.h5m source_mesh.vtk", shell=True) diff --git a/Examples/nwl_geom_example.py b/Examples/nwl_geom_example.py index 6d945ef..d8466c6 100644 --- a/Examples/nwl_geom_example.py +++ b/Examples/nwl_geom_example.py @@ -6,9 +6,9 @@ # Define directory to export all output files to -export_dir = '' +export_dir = "" # Define plasma equilibrium VMEC file -vmec_file = 'wout_vmec.nc' +vmec_file = "wout_vmec.nc" # Instantiate ParaStell build stellarator = ps.Stellarator(vmec_file) @@ -25,42 +25,29 @@ poloidal_angles, wall_s, empty_radial_build_dict, - split_chamber=False + split_chamber=False, ) # Export in-vessel component files stellarator.export_invessel_build( - export_cad_to_dagmc=False, - export_dir=export_dir + export_cad_to_dagmc=False, export_dir=export_dir ) # Define source mesh parameters mesh_size = (11, 81, 61) toroidal_extent = 90.0 # Construct source -stellarator.construct_source_mesh( - mesh_size, - toroidal_extent -) +stellarator.construct_source_mesh(mesh_size, toroidal_extent) # Export source file -stellarator.export_source_mesh( - filename='source_mesh', - export_dir=export_dir -) +stellarator.export_source_mesh(filename="source_mesh", export_dir=export_dir) strengths = stellarator.source_mesh.strengths - -filepath = Path(export_dir) / 'source_strengths.txt' -file = open(filepath, 'w') +filepath = Path(export_dir) / "source_strengths.txt" + +file = open(filepath, "w") for tet in strengths: - file.write(f'{tet}\n') + file.write(f"{tet}\n") # Export DAGMC neutronics H5M file -stellarator.build_cubit_model( - skip_imprint=True, - legacy_faceting=True -) -stellarator.export_dagmc( - filename='nwl_geom', - export_dir=export_dir -) +stellarator.build_cubit_model(skip_imprint=True, legacy_faceting=True) +stellarator.export_dagmc(filename="nwl_geom", export_dir=export_dir) diff --git a/Examples/parastell_example.py b/Examples/parastell_example.py index 7f8e27d..b5d2bcc 100644 --- a/Examples/parastell_example.py +++ b/Examples/parastell_example.py @@ -4,9 +4,9 @@ # Define directory to export all output files to -export_dir = '' +export_dir = "" # Define plasma equilibrium VMEC file -vmec_file = 'wout_vmec.nc' +vmec_file = "wout_vmec.nc" # Instantiate ParaStell build stellarator = ps.Stellarator(vmec_file) @@ -20,86 +20,64 @@ uniform_unit_thickness = np.ones((len(toroidal_angles), len(poloidal_angles))) radial_build_dict = { - 'first_wall': { - 'thickness_matrix': uniform_unit_thickness * 5 + "first_wall": {"thickness_matrix": uniform_unit_thickness * 5}, + "breeder": { + "thickness_matrix": ( + [ + [75.0, 75.0, 75.0, 25.0, 25.0, 25.0, 75.0, 75.0, 75.0], + [75.0, 75.0, 75.0, 25.0, 25.0, 75.0, 75.0, 75.0, 75.0], + [75.0, 75.0, 25.0, 25.0, 75.0, 75.0, 75.0, 75.0, 75.0], + [65.0, 25.0, 25.0, 65.0, 75.0, 75.0, 75.0, 75.0, 65.0], + [45.0, 45.0, 75.0, 75.0, 75.0, 75.0, 75.0, 45.0, 45.0], + [65.0, 75.0, 75.0, 75.0, 75.0, 65.0, 25.0, 25.0, 65.0], + [75.0, 75.0, 75.0, 75.0, 75.0, 25.0, 25.0, 75.0, 75.0], + [75.0, 75.0, 75.0, 75.0, 25.0, 25.0, 75.0, 75.0, 75.0], + [75.0, 75.0, 75.0, 25.0, 25.0, 25.0, 75.0, 75.0, 75.0], + ] + ) }, - 'breeder': { - 'thickness_matrix': ([ - [75.0, 75.0, 75.0, 25.0, 25.0, 25.0, 75.0, 75.0, 75.0], - [75.0, 75.0, 75.0, 25.0, 25.0, 75.0, 75.0, 75.0, 75.0], - [75.0, 75.0, 25.0, 25.0, 75.0, 75.0, 75.0, 75.0, 75.0], - [65.0, 25.0, 25.0, 65.0, 75.0, 75.0, 75.0, 75.0, 65.0], - [45.0, 45.0, 75.0, 75.0, 75.0, 75.0, 75.0, 45.0, 45.0], - [65.0, 75.0, 75.0, 75.0, 75.0, 65.0, 25.0, 25.0, 65.0], - [75.0, 75.0, 75.0, 75.0, 75.0, 25.0, 25.0, 75.0, 75.0], - [75.0, 75.0, 75.0, 75.0, 25.0, 25.0, 75.0, 75.0, 75.0], - [75.0, 75.0, 75.0, 25.0, 25.0, 25.0, 75.0, 75.0, 75.0] - ]) + "back_wall": {"thickness_matrix": uniform_unit_thickness * 5}, + "shield": {"thickness_matrix": uniform_unit_thickness * 50}, + "vacuum_vessel": { + "thickness_matrix": uniform_unit_thickness * 10, + "h5m_tag": "vac_vessel", }, - 'back_wall': { - 'thickness_matrix': uniform_unit_thickness * 5 - }, - 'shield': { - 'thickness_matrix': uniform_unit_thickness * 50 - }, - 'vacuum_vessel': { - 'thickness_matrix': uniform_unit_thickness * 10, - 'h5m_tag': 'vac_vessel' - } } # Construct in-vessel components stellarator.construct_invessel_build( - toroidal_angles, - poloidal_angles, - wall_s, - radial_build_dict + toroidal_angles, poloidal_angles, wall_s, radial_build_dict ) # Export in-vessel component files stellarator.export_invessel_build( - export_cad_to_dagmc=False, - export_dir=export_dir + export_cad_to_dagmc=False, export_dir=export_dir ) # Define build parameters for magnet coils -coils_file = 'coils.example' -cross_section = ['circle', 20] +coils_file = "coils.example" +cross_section = ["circle", 20] toroidal_extent = 90.0 # Construct magnets stellarator.construct_magnets( - coils_file, - cross_section, - toroidal_extent, - sample_mod=6 + coils_file, cross_section, toroidal_extent, sample_mod=6 ) # Export magnet files stellarator.export_magnets( - step_filename='magnets', + step_filename="magnets", export_mesh=True, - mesh_filename='magnet_mesh', - export_dir=export_dir + mesh_filename="magnet_mesh", + export_dir=export_dir, ) # Define source mesh parameters mesh_size = (11, 81, 61) toroidal_extent = 90.0 # Construct source -stellarator.construct_source_mesh( - mesh_size, - toroidal_extent -) +stellarator.construct_source_mesh(mesh_size, toroidal_extent) # Export source file -stellarator.export_source_mesh( - filename='source_mesh', - export_dir=export_dir -) +stellarator.export_source_mesh(filename="source_mesh", export_dir=export_dir) # Build Cubit model of Parastell Components -stellarator.build_cubit_model( - skip_imprint=False, - legacy_faceting=True) +stellarator.build_cubit_model(skip_imprint=False, legacy_faceting=True) # Export DAGMC neutronics H5M file -stellarator.export_dagmc( - filename='dagmc', - export_dir=export_dir -) +stellarator.export_dagmc(filename="dagmc", export_dir=export_dir) diff --git a/parastell/cubit_io.py b/parastell/cubit_io.py index 0096a49..108938a 100644 --- a/parastell/cubit_io.py +++ b/parastell/cubit_io.py @@ -9,23 +9,26 @@ def init_cubit(): - """Initializes Coreform Cubit with the DAGMC plugin. - """ + """Initializes Coreform Cubit with the DAGMC plugin.""" global initialized - + if not initialized: - cubit_plugin_dir = ( - Path(os.path.dirname(inspect.getfile(cubit))) / Path('plugins') + cubit_plugin_dir = Path( + os.path.dirname(inspect.getfile(cubit)) + ) / Path("plugins") + cubit.init( + [ + "cubit", + "-nojournal", + "-nographics", + "-information", + "off", + "-warning", + "off", + "-commandplugindir", + str(cubit_plugin_dir), + ] ) - cubit.init([ - 'cubit', - '-nojournal', - '-nographics', - '-information', 'off', - '-warning', 'off', - '-commandplugindir', - str(cubit_plugin_dir) - ]) initialized = True @@ -41,14 +44,14 @@ def import_step_cubit(filename, import_dir): """ init_cubit() - import_path = Path(import_dir) / Path(filename).with_suffix('.step') + import_path = Path(import_dir) / Path(filename).with_suffix(".step") cubit.cmd(f'import step "{import_path}" heal') vol_id = cubit.get_last_id("volume") return vol_id -def export_step_cubit(filename, export_dir=''): +def export_step_cubit(filename, export_dir=""): """Export CAD solid as a STEP file via Coreform Cubit. Arguments: @@ -58,12 +61,13 @@ def export_step_cubit(filename, export_dir=''): """ init_cubit() - export_path = Path(export_dir) / Path(filename).with_suffix('.step') + export_path = Path(export_dir) / Path(filename).with_suffix(".step") cubit.cmd(f'export step "{export_path}" overwrite') -def export_cub5(filename, export_dir=''): + +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. export_dir (str): directory to which to export the cub5 output file @@ -71,10 +75,11 @@ def export_cub5(filename, export_dir=''): """ init_cubit() - export_path = Path(export_dir) / Path(filename).with_suffix('.cub5') + export_path = Path(export_dir) / Path(filename).with_suffix(".cub5") cubit.cmd(f'save cub5 "{export_path}" overwrite') -def export_mesh_cubit(filename, export_dir=''): + +def export_mesh_cubit(filename, export_dir=""): """Exports Cubit mesh to H5M file format, first exporting to Exodus format via Coreform Cubit and converting to H5M via MOAB. @@ -84,18 +89,21 @@ def export_mesh_cubit(filename, export_dir=''): (defaults to empty string). """ init_cubit() - - exo_path = Path(export_dir) / Path(filename).with_suffix('.exo') - h5m_path = Path(export_dir) / Path(filename).with_suffix('.h5m') + + exo_path = Path(export_dir) / Path(filename).with_suffix(".exo") + h5m_path = Path(export_dir) / Path(filename).with_suffix(".h5m") cubit.cmd(f'export mesh "{exo_path}" overwrite') - subprocess.run(f'mbconvert {exo_path} {h5m_path}', shell=True) + subprocess.run(f"mbconvert {exo_path} {h5m_path}", shell=True) Path.unlink(exo_path) def export_dagmc_cubit_legacy( - faceting_tolerance=None, length_tolerance=None, normal_tolerance=None, - filename='dagmc', export_dir='' + faceting_tolerance=None, + length_tolerance=None, + normal_tolerance=None, + filename="dagmc", + export_dir="", ): """Exports DAGMC neutronics H5M file of ParaStell components via legacy plug-in faceting method for Coreform Cubit. @@ -113,25 +121,25 @@ def export_dagmc_cubit_legacy( (defaults to empty string). """ init_cubit() - - tol_str = '' + + tol_str = "" if faceting_tolerance is not None: - tol_str += f'faceting_tolerance {faceting_tolerance}' + tol_str += f"faceting_tolerance {faceting_tolerance}" if length_tolerance is not None: - tol_str += f'length_tolerance {length_tolerance}' + tol_str += f"length_tolerance {length_tolerance}" if normal_tolerance is not None: - tol_str += f'normal_tolerance {normal_tolerance}' + tol_str += f"normal_tolerance {normal_tolerance}" - export_path = Path(export_dir) / Path(filename).with_suffix('.h5m') - cubit.cmd( - f'export dagmc "{export_path}" {tol_str} make_watertight' - ) + export_path = Path(export_dir) / Path(filename).with_suffix(".h5m") + cubit.cmd(f'export dagmc "{export_path}" {tol_str} make_watertight') def export_dagmc_cubit_native( - anisotropic_ratio=100.0, deviation_angle=5.0, filename='dagmc', - export_dir='' + anisotropic_ratio=100.0, + deviation_angle=5.0, + filename="dagmc", + export_dir="", ): """Exports DAGMC neutronics H5M file of ParaStell components via native faceting method for Coreform Cubit. @@ -148,13 +156,13 @@ def export_dagmc_cubit_native( (defaults to empty string). """ init_cubit() - + cubit.cmd( - f'set trimesher coarse on ratio {anisotropic_ratio} ' - f'angle {deviation_angle}' + f"set trimesher coarse on ratio {anisotropic_ratio} " + f"angle {deviation_angle}" ) cubit.cmd("surface all scheme trimesh") cubit.cmd("mesh surface all") - export_path = Path(export_dir) / Path(filename).with_suffix('.h5m') + export_path = Path(export_dir) / Path(filename).with_suffix(".h5m") cubit.cmd(f'export cf_dagmc "{export_path}" overwrite') diff --git a/parastell/invessel_build.py b/parastell/invessel_build.py index 3073719..374264b 100644 --- a/parastell/invessel_build.py +++ b/parastell/invessel_build.py @@ -11,10 +11,14 @@ from . import log from .utils import ( - normalize, expand_ang_list, read_yaml_config, filter_kwargs, m2cm + normalize, + expand_ang_list, + read_yaml_config, + filter_kwargs, + m2cm, ) -export_allowed_kwargs = ['export_cad_to_dagmc', 'dagmc_filename'] +export_allowed_kwargs = ["export_cad_to_dagmc", "dagmc_filename"] def orient_spline_surfaces(volume_id): @@ -28,12 +32,12 @@ def orient_spline_surfaces(volume_id): inner_surface_id (int): Cubit ID of in-vessel component inner surface. outer_surface_id (int): Cubit ID of in-vessel component outer surface. """ - - surfaces = cubit.get_relatives('volume', volume_id, 'surface') + + surfaces = cubit.get_relatives("volume", volume_id, "surface") spline_surfaces = [] for surface in surfaces: - if cubit.get_surface_type(surface) == 'spline surface': + if cubit.get_surface_type(surface) == "spline surface": spline_surfaces.append(surface) if len(spline_surfaces) == 1: @@ -42,8 +46,8 @@ def orient_spline_surfaces(volume_id): else: # The outer surface bounding box will have the larger maximum XY value if ( - cubit.get_bounding_box('surface', spline_surfaces[1])[4] > - cubit.get_bounding_box('surface', spline_surfaces[0])[4] + cubit.get_bounding_box("surface", spline_surfaces[1])[4] + > cubit.get_bounding_box("surface", spline_surfaces[0])[4] ): outer_surface_id = spline_surfaces[1] inner_surface_id = spline_surfaces[0] @@ -68,7 +72,7 @@ class InVesselBuild(object): defined. logger (object): logger object (optional, defaults to None). If no logger is supplied, a default logger will be instantiated. - + Optional attributes: repeat (int): number of times to repeat build segment for full model (defaults to 0). @@ -84,27 +88,24 @@ class InVesselBuild(object): (defaults to m2cm = 100). """ - def __init__( - self, - vmec_obj, - radial_build, - logger=None, - **kwargs - ): + def __init__(self, vmec_obj, radial_build, logger=None, **kwargs): self.logger = logger self.vmec_obj = vmec_obj self.radial_build = radial_build - + self.repeat = 0 self.num_ribs = 61 self.num_rib_pts = 67 self.scale = m2cm for name in kwargs.keys() & ( - 'repeat', 'num_ribs', 'num_rib_pts', 'scale' + "repeat", + "num_ribs", + "num_rib_pts", + "scale", ): - self.__setattr__(name,kwargs[name]) + self.__setattr__(name, kwargs[name]) self.Surfaces = {} self.Components = {} @@ -112,7 +113,7 @@ def __init__( @property def vmec_obj(self): return self._vmec_obj - + @vmec_obj.setter def vmec_obj(self, vmec_object): self._vmec_obj = vmec_object @@ -120,27 +121,27 @@ def vmec_obj(self, vmec_object): @property def logger(self): return self._logger - + @logger.setter def logger(self, logger_object): self._logger = log.check_init(logger_object) - + @property def repeat(self): return self._repeat - + @repeat.setter def repeat(self, num): self._repeat = num if (self._repeat + 1) * self.radial_build.toroidal_angles[-1] > 360.0: e = AssertionError( - 'Total toroidal extent requested with repeated geometry ' + "Total toroidal extent requested with repeated geometry " 'exceeds 360 degrees. Please examine the "repeat" parameter ' 'and the "toroidal_angles" parameter of "radial_build".' ) self._logger.error(e.args[0]) raise e - + def _interpolate_offset_matrix(self, offset_mat): """Interpolates total offset for expanded angle lists using cubic spline interpolation. @@ -154,19 +155,21 @@ def _interpolate_offset_matrix(self, offset_mat): interpolator = RegularGridInterpolator( ( self.radial_build.toroidal_angles, - self.radial_build.poloidal_angles + self.radial_build.poloidal_angles, ), offset_mat, - method='pchip' + method="pchip", ) - interpolated_offset_mat = np.array([ + interpolated_offset_mat = np.array( [ - interpolator([np.rad2deg(phi), np.rad2deg(theta)])[0] - for theta in self._poloidal_angles_exp + [ + interpolator([np.rad2deg(phi), np.rad2deg(theta)])[0] + for theta in self._poloidal_angles_exp + ] + for phi in self._toroidal_angles_exp ] - for phi in self._toroidal_angles_exp - ]) + ) return interpolated_offset_mat @@ -175,47 +178,50 @@ def populate_surfaces(self): each component specified in the radial build. """ self._logger.info( - 'Populating surface objects for in-vessel components...' + "Populating surface objects for in-vessel components..." ) self._toroidal_angles_exp = expand_ang_list( - self.radial_build.toroidal_angles, - self.num_ribs + self.radial_build.toroidal_angles, self.num_ribs ) self._poloidal_angles_exp = expand_ang_list( - self.radial_build.poloidal_angles, - self.num_rib_pts + self.radial_build.poloidal_angles, self.num_rib_pts + ) + + offset_mat = np.zeros( + ( + len(self.radial_build.toroidal_angles), + len(self.radial_build.poloidal_angles), + ) ) - - offset_mat = np.zeros(( - len(self.radial_build.toroidal_angles), - len(self.radial_build.poloidal_angles) - )) for name, layer_data in self.radial_build.radial_build.items(): - if name == 'plasma': + if name == "plasma": s = 1.0 else: s = self.radial_build.wall_s - offset_mat += np.array(layer_data['thickness_matrix']) + offset_mat += np.array(layer_data["thickness_matrix"]) interpolated_offset_mat = self._interpolate_offset_matrix( offset_mat ) self.Surfaces[name] = Surface( - self._vmec_obj, s, self._poloidal_angles_exp, - self._toroidal_angles_exp, - interpolated_offset_mat, self.scale - ) - + self._vmec_obj, + s, + self._poloidal_angles_exp, + self._toroidal_angles_exp, + interpolated_offset_mat, + self.scale, + ) + [surface.populate_ribs() for surface in self.Surfaces.values()] def calculate_loci(self): """Calls calculate_loci method in Surface class for each component specified in the radial build. """ - self._logger.info('Computing point cloud for in-vessel components...') + self._logger.info("Computing point cloud for in-vessel components...") [surface.calculate_loci() for surface in self.Surfaces.values()] @@ -225,7 +231,7 @@ def generate_components(self): solid for a given component. """ self._logger.info( - 'Constructing CadQuery objects for in-vessel components...' + "Constructing CadQuery objects for in-vessel components..." ) interior_surface = None @@ -233,7 +239,7 @@ def generate_components(self): segment_angles = np.linspace( self.radial_build.toroidal_angles[-1], self._repeat * self.radial_build.toroidal_angles[-1], - num=self._repeat + num=self._repeat, ) for name, surface in self.Surfaces.items(): @@ -257,9 +263,9 @@ def get_loci(self): """Returns the set of point-loci defining the outer surfaces of the components specified in the radial build. """ - return np.array([ - surface.get_loci() for surface in self.Surfaces.values() - ]) + return np.array( + [surface.get_loci() for surface in self.Surfaces.values()] + ) def merge_layer_surfaces(self): """Merges ParaStell in-vessel component surfaces in Coreform Cubit @@ -273,8 +279,8 @@ def merge_layer_surfaces(self): for data in self.radial_build.radial_build.values(): - inner_surface_id, outer_surface_id = ( - orient_spline_surfaces(data['vol_id']) + inner_surface_id, outer_surface_id = orient_spline_surfaces( + data["vol_id"] ) # Conditionally skip merging (first iteration only) @@ -282,32 +288,28 @@ def merge_layer_surfaces(self): prev_outer_surface_id = outer_surface_id else: cubit.cmd( - f'merge surface {inner_surface_id} {prev_outer_surface_id}' + f"merge surface {inner_surface_id} {prev_outer_surface_id}" ) prev_outer_surface_id = outer_surface_id - - def export_step(self, export_dir=''): + def export_step(self, export_dir=""): """Export CAD solids as STEP files via CadQuery. Arguments: export_dir (str): directory to which to export the STEP output files (optional, defaults to empty string). """ - self._logger.info('Exporting STEP files for in-vessel components...') + self._logger.info("Exporting STEP files for in-vessel components...") self.export_dir = export_dir for name, component in self.Components.items(): - export_path = ( - Path(self.export_dir) / Path(name).with_suffix('.step') - ) - cq.exporters.export( - component, - str(export_path) + export_path = Path(self.export_dir) / Path(name).with_suffix( + ".step" ) + cq.exporters.export(component, str(export_path)) - def export_cad_to_dagmc(self, dagmc_filename='dagmc', export_dir=''): + def export_cad_to_dagmc(self, dagmc_filename="dagmc", export_dir=""): """Exports DAGMC neutronics H5M file of ParaStell in-vessel components via CAD-to-DAGMC. @@ -318,7 +320,7 @@ def export_cad_to_dagmc(self, dagmc_filename='dagmc', export_dir=''): (optional, defaults to empty string). """ self._logger.info( - 'Exporting DAGMC neutronics model of in-vessel components...' + "Exporting DAGMC neutronics model of in-vessel components..." ) model = cad_to_dagmc.CadToDagmc() @@ -326,16 +328,16 @@ def export_cad_to_dagmc(self, dagmc_filename='dagmc', export_dir=''): for name, component in self.Components.items(): model.add_cadquery_object( component, - material_tags=[self.radial_build.radial_build[name]['mat_tag']] + material_tags=[ + self.radial_build.radial_build[name]["mat_tag"] + ], ) - export_path = ( - Path(export_dir) / Path(dagmc_filename).with_suffix('.h5m') + export_path = Path(export_dir) / Path(dagmc_filename).with_suffix( + ".h5m" ) - model.export_dagmc_h5m_file( - filename=str(export_path) - ) + model.export_dagmc_h5m_file(filename=str(export_path)) class Surface(object): @@ -361,16 +363,8 @@ class Surface(object): scale (float): a scaling factor between the units of VMEC and [cm]. """ - def __init__( - self, - vmec_obj, - s, - theta_list, - phi_list, - offset_mat, - scale - ): - + def __init__(self, vmec_obj, s, theta_list, phi_list, offset_mat, scale): + self.vmec_obj = vmec_obj self.s = s self.theta_list = theta_list @@ -386,20 +380,22 @@ def populate_ribs(self): """ self.Ribs = [ Rib( - self.vmec_obj, self.s, self.theta_list, phi, - self.offset_mat[i, :], self.scale + self.vmec_obj, + self.s, + self.theta_list, + phi, + self.offset_mat[i, :], + self.scale, ) for i, phi in enumerate(self.phi_list) ] def calculate_loci(self): - """Calls calculate_loci method in Rib class for each rib in the surface. - """ + """Calls calculate_loci method in Rib class for each rib in the surface.""" [rib.calculate_loci() for rib in self.Ribs] def generate_surface(self): - """Constructs a surface by lofting across a set of rib splines. - """ + """Constructs a surface by lofting across a set of rib splines.""" if not self.surface: self.surface = cq.Solid.makeLoft( [rib.generate_rib() for rib in self.Ribs] @@ -408,8 +404,7 @@ def generate_surface(self): return self.surface def get_loci(self): - """Returns the set of point-loci defining the ribs in the surface. - """ + """Returns the set of point-loci defining the ribs in the surface.""" return np.array([rib.rib_loci for rib in self.Ribs]) @@ -436,16 +431,8 @@ class Rib(object): scale (float): a scaling factor between the units of VMEC and [cm]. """ - def __init__( - self, - vmec_obj, - s, - theta_list, - phi, - offset_list, - scale - ): - + def __init__(self, vmec_obj, s, theta_list, phi, offset_list, scale): + self.vmec_obj = vmec_obj self.s = s self.theta_list = theta_list @@ -494,14 +481,11 @@ def _normals(self): return normalize(norm) def calculate_loci(self): - """Generates Cartesian point-loci for stellarator rib. - """ + """Generates Cartesian point-loci for stellarator rib.""" self.rib_loci = self._vmec2xyz() if not np.all(self.offset_list == 0): - self.rib_loci += ( - self.offset_list[:, np.newaxis] * self._normals() - ) + self.rib_loci += self.offset_list[:, np.newaxis] * self._normals() def generate_rib(self): """Constructs component rib by constructing a spline connecting all @@ -512,7 +496,7 @@ def generate_rib(self): rib_spline = cq.Wire.assembleEdges([spline]).close() return rib_spline - + class RadialBuild(object): """Parametrically defines ParaStell in-vessel component geometries. @@ -567,9 +551,9 @@ def __init__( radial_build, split_chamber=False, logger=None, - **kwargs + **kwargs, ): - + self.logger = logger self.toroidal_angles = toroidal_angles self.poloidal_angles = poloidal_angles @@ -578,60 +562,56 @@ def __init__( self.split_chamber = split_chamber for name in kwargs.keys() & ( - 'plasma_mat_tag', 'sol_mat_tag', 'chamber_mat_tag' + "plasma_mat_tag", + "sol_mat_tag", + "chamber_mat_tag", ): - self.__setattr__(name,kwargs[name]) + self.__setattr__(name, kwargs[name]) - self._logger.info( - 'Constructing radial build...' - ) + self._logger.info("Constructing radial build...") @property def toroidal_angles(self): return self._toroidal_angles - + @toroidal_angles.setter def toroidal_angles(self, angle_list): - if hasattr(self, 'toroidal_angles'): + if hasattr(self, "toroidal_angles"): e = AttributeError( '"toroidal_angles" cannot be set after class initialization. ' - 'Please create new class instance to alter this attribute.' + "Please create new class instance to alter this attribute." ) self._logger.error(e.args[0]) raise e - + self._toroidal_angles = angle_list if self._toroidal_angles[0] != 0.0: - e = ValueError( - 'The first entry in toroidal_angles must be 0.0.' - ) + e = ValueError("The first entry in toroidal_angles must be 0.0.") self._logger.error(e.args[0]) raise e if self._toroidal_angles[-1] > 360.0: - e = ValueError( - 'Toroidal extent cannot exceed 360.0 degrees.' - ) + e = ValueError("Toroidal extent cannot exceed 360.0 degrees.") self._logger.error(e.args[0]) raise e @property def poloidal_angles(self): return self._poloidal_angles - + @poloidal_angles.setter def poloidal_angles(self, angle_list): - if hasattr(self, 'poloidal_angles'): + if hasattr(self, "poloidal_angles"): e = AttributeError( '"poloidal_angles" cannot be set after class initialization. ' - 'Please create new class instance to alter this attribute.' + "Please create new class instance to alter this attribute." ) self._logger.error(e.args[0]) raise e - + self._poloidal_angles = angle_list if self._poloidal_angles[-1] - self._poloidal_angles[0] > 360.0: e = AssertionError( - 'Poloidal extent must span exactly 360.0 degrees.' + "Poloidal extent must span exactly 360.0 degrees." ) self._logger.error(e.args[0]) raise e @@ -639,147 +619,146 @@ def poloidal_angles(self, angle_list): @property def wall_s(self): return self._wall_s - + @wall_s.setter def wall_s(self, s): - if hasattr(self, 'wall_s'): + if hasattr(self, "wall_s"): e = AttributeError( '"wall_s" cannot be set after class initialization. Please ' - 'create new class instance to alter this attribute.' + "create new class instance to alter this attribute." ) self._logger.error(e.args[0]) raise e - self._wall_s = s if self._wall_s < 1.0: - e = ValueError( - 'wall_s must be greater than or equal to 1.0.' - ) + e = ValueError("wall_s must be greater than or equal to 1.0.") self._logger.error(e.args[0]) raise e - + @property def radial_build(self): return self._radial_build - + @radial_build.setter def radial_build(self, build_dict): self._radial_build = build_dict - + for name, component in self._radial_build.items(): - component['thickness_matrix'] = \ - np.array(component['thickness_matrix']) - if ( - component['thickness_matrix'].shape != - (len(self._toroidal_angles), len(self._poloidal_angles)) + component["thickness_matrix"] = np.array( + component["thickness_matrix"] + ) + if component["thickness_matrix"].shape != ( + len(self._toroidal_angles), + len(self._poloidal_angles), ): e = AssertionError( - f'The dimensions of {name}\'s thickness matrix ' + f"The dimensions of {name}'s thickness matrix " f'{component["thickness_matrix"].shape} must match the ' - 'dimensions defined by the toroidal and poloidal angle ' - 'lists ' - f'{len(self._toroidal_angles),len(self._poloidal_angles)}, ' - 'which define the rows and columns of the matrix, ' - 'respectively.' + "dimensions defined by the toroidal and poloidal angle " + "lists " + f"{len(self._toroidal_angles),len(self._poloidal_angles)}, " + "which define the rows and columns of the matrix, " + "respectively." ) self._logger.error(e.args[0]) raise e - - if np.any(component['thickness_matrix'] < 0): + + if np.any(component["thickness_matrix"] < 0): e = ValueError( - 'Component thicknesses must be greater than or equal to 0. ' - 'Check thickness inputs for negative values.' + "Component thicknesses must be greater than or equal to 0. " + "Check thickness inputs for negative values." ) self._logger.error(e.args[0]) raise e - if 'mat_tag' not in component: + if "mat_tag" not in component: self._set_mat_tag(name, name) @property def split_chamber(self): return self._split_chamber - + @split_chamber.setter def split_chamber(self, value): - if hasattr(self, 'split_chamber'): + if hasattr(self, "split_chamber"): e = AttributeError( '"split_chamber" cannot be set after class initialization. ' - 'Please create new class instance to alter this attribute.' - ) + "Please create new class instance to alter this attribute." + ) self._logger.error(e.args[0]) raise e - + self._split_chamber = value - + if self._split_chamber: - if self._wall_s > 1.0 and 'sol' not in self._radial_build: + if self._wall_s > 1.0 and "sol" not in self._radial_build: self.radial_build = { - 'sol': { - 'thickness_matrix': np.zeros(( - len(self._toroidal_angles), - len(self._poloidal_angles) - )) + "sol": { + "thickness_matrix": np.zeros( + ( + len(self._toroidal_angles), + len(self._poloidal_angles), + ) + ) }, - **self.radial_build + **self.radial_build, } - if not hasattr(self, 'sol_mat_tag'): - self.sol_mat_tag = 'Vacuum' + if not hasattr(self, "sol_mat_tag"): + self.sol_mat_tag = "Vacuum" - inner_volume_name = 'plasma' - inner_volume_tag = 'plasma_mat_tag' + inner_volume_name = "plasma" + inner_volume_tag = "plasma_mat_tag" else: - inner_volume_name = 'chamber' - inner_volume_tag = 'chamber_mat_tag' - + inner_volume_name = "chamber" + inner_volume_tag = "chamber_mat_tag" + self.radial_build = { inner_volume_name: { - 'thickness_matrix': np.zeros(( - len(self._toroidal_angles), - len(self._poloidal_angles) - )) + "thickness_matrix": np.zeros( + (len(self._toroidal_angles), len(self._poloidal_angles)) + ) }, - **self.radial_build + **self.radial_build, } if not hasattr(self, inner_volume_tag): - self.__setattr__(inner_volume_tag, 'Vacuum') + self.__setattr__(inner_volume_tag, "Vacuum") @property def logger(self): return self._logger - + @logger.setter def logger(self, logger_object): self._logger = log.check_init(logger_object) - + @property def plasma_mat_tag(self): return self._plasma_mat_tag - + @plasma_mat_tag.setter def plasma_mat_tag(self, mat_tag): self._plasma_mat_tag = mat_tag - self._set_mat_tag('plasma', self._plasma_mat_tag) + self._set_mat_tag("plasma", self._plasma_mat_tag) @property def sol_mat_tag(self): return self._sol_mat_tag - + @sol_mat_tag.setter def sol_mat_tag(self, mat_tag): self._sol_mat_tag = mat_tag - self._set_mat_tag('sol', self._sol_mat_tag) + self._set_mat_tag("sol", self._sol_mat_tag) @property def chamber_mat_tag(self): return self._chamber_mat_tag - + @chamber_mat_tag.setter def chamber_mat_tag(self, mat_tag): self._chamber_mat_tag = mat_tag - self._set_mat_tag('chamber', self._chamber_mat_tag) - + self._set_mat_tag("chamber", self._chamber_mat_tag) + def _set_mat_tag(self, name, mat_tag): """Sets DAGMC material tag for a given component. (Internal function not intended to be called externally) @@ -788,43 +767,43 @@ def _set_mat_tag(self, name, mat_tag): name (str): name of component. mat_tag (str): DAGMC material tag. """ - self.radial_build[name]['mat_tag'] = mat_tag + self.radial_build[name]["mat_tag"] = mat_tag def parse_args(): - """Parser for running as a script. - """ - parser = argparse.ArgumentParser(prog='invessel_build') + """Parser for running as a script.""" + parser = argparse.ArgumentParser(prog="invessel_build") parser.add_argument( - 'filename', - help='YAML file defining ParaStell in-vessel component configuration' + "filename", + help="YAML file defining ParaStell in-vessel component configuration", ) parser.add_argument( - '-e', '--export_dir', - default='', + "-e", + "--export_dir", + default="", help=( - 'Directory to which output files are exported (default: working ' - 'directory)' + "Directory to which output files are exported (default: working " + "directory)" ), - metavar='' + metavar="", ) parser.add_argument( - '-l', '--logger', + "-l", + "--logger", default=False, help=( - 'Flag to indicate whether to instantiate a logger object (default: ' - 'False)' + "Flag to indicate whether to instantiate a logger object (default: " + "False)" ), - metavar='' + metavar="", ) return parser.parse_args() def generate_invessel_build(): - """Main method when run as a command line script. - """ + """Main method when run as a command line script.""" args = parse_args() all_data = read_yaml_config(args.filename) @@ -834,25 +813,21 @@ def generate_invessel_build(): else: logger = log.NullLogger() - vmec_file = all_data['vmec_file'] + vmec_file = all_data["vmec_file"] vmec_obj = read_vmec.VMECData(vmec_file) - invessel_build_dict = all_data['invessel_build'] + invessel_build_dict = all_data["invessel_build"] radial_build = RadialBuild( - invessel_build_dict['toroidal_angles'], - invessel_build_dict['poloidal_angles'], - invessel_build_dict['wall_s'], - invessel_build_dict['radial_build'], - logger=logger - **invessel_build_dict + invessel_build_dict["toroidal_angles"], + invessel_build_dict["poloidal_angles"], + invessel_build_dict["wall_s"], + invessel_build_dict["radial_build"], + logger=logger**invessel_build_dict, ) invessel_build = InVesselBuild( - vmec_obj, - radial_build, - logger=logger, - **invessel_build_dict + vmec_obj, radial_build, logger=logger, **invessel_build_dict ) invessel_build.populate_surfaces() @@ -861,11 +836,11 @@ def generate_invessel_build(): invessel_build.export_step(export_dir=args.export_dir) - if invessel_build_dict['export_cad_to_dagmc']: - + if invessel_build_dict["export_cad_to_dagmc"]: + invessel_build.export_cad_to_dagmc( export_dir=args.export_dir, - **(filter_kwargs(invessel_build_dict, ['dagmc_filename'])) + **(filter_kwargs(invessel_build_dict, ["dagmc_filename"])), ) diff --git a/parastell/log.py b/parastell/log.py index 942204e..950b585 100644 --- a/parastell/log.py +++ b/parastell/log.py @@ -22,6 +22,7 @@ class NullLogger(object): """Creates a pseudo logger object mimicking an actual logger object whose methods do nothing when called. """ + def __init__(self): pass @@ -30,15 +31,15 @@ def hasHandlers(self): def info(self, message): current_time = time.localtime() - current_time = time.strftime('%H:%M:%S', current_time) - print(f'{current_time}: {message}') + current_time = time.strftime("%H:%M:%S", current_time) + print(f"{current_time}: {message}") def warning(self, *args): pass def error(self, *args): pass - + def init(): """Creates and configures logger with separate stream and file handlers. @@ -46,19 +47,15 @@ def init(): Returns: logger (object): logger object. """ - logger = logging.getLogger('log') + logger = logging.getLogger("log") logger.setLevel(logging.INFO) s_handler = logging.StreamHandler() - f_handler = logging.FileHandler( - filename='stellarator.log', - mode='w' - ) + f_handler = logging.FileHandler(filename="stellarator.log", mode="w") format = logging.Formatter( - fmt = '%(asctime)s: %(message)s', - datefmt = '%H:%M:%S' + fmt="%(asctime)s: %(message)s", datefmt="%H:%M:%S" ) s_handler.setFormatter(format) f_handler.setFormatter(format) diff --git a/parastell/magnet_coils.py b/parastell/magnet_coils.py index fafb77e..0bd5ff9 100644 --- a/parastell/magnet_coils.py +++ b/parastell/magnet_coils.py @@ -5,12 +5,10 @@ import cubit from . import log -from . import cubit_io as cubit_io -from .utils import ( - normalize, read_yaml_config, filter_kwargs, m2cm -) +from . import cubit_io as cubit_io +from .utils import normalize, read_yaml_config, filter_kwargs, m2cm -export_allowed_kwargs = ['step_filename', 'export_mesh', 'mesh_filename'] +export_allowed_kwargs = ["step_filename", "export_mesh", "mesh_filename"] class MagnetSet(object): @@ -40,14 +38,9 @@ class MagnetSet(object): """ def __init__( - self, - coils_file, - cross_section, - toroidal_extent, - logger=None, - **kwargs + self, coils_file, cross_section, toroidal_extent, logger=None, **kwargs ): - + self.logger = logger self.coils_file = coils_file self.cross_section = cross_section @@ -56,17 +49,22 @@ def __init__( self.start_line = 3 self.sample_mod = 1 self.scale = m2cm - self.mat_tag = 'magnets' + self.mat_tag = "magnets" - for name in kwargs.keys() & ('start_line', 'sample_mod', 'scale', 'mat_tag'): - self.__setattr__(name,kwargs[name]) + for name in kwargs.keys() & ( + "start_line", + "sample_mod", + "scale", + "mat_tag", + ): + self.__setattr__(name, kwargs[name]) cubit_io.init_cubit() @property def cross_section(self): return self._cross_section - + @cross_section.setter def cross_section(self, shape): self._cross_section = shape @@ -75,21 +73,19 @@ def cross_section(self, shape): @property def toroidal_extent(self): return self._toroidal_extent - + @toroidal_extent.setter def toroidal_extent(self, angle): self._toroidal_extent = np.deg2rad(angle) if self._toroidal_extent > 360.0: - e = ValueError( - 'Toroidal extent cannot exceed 360.0 degrees.' - ) + e = ValueError("Toroidal extent cannot exceed 360.0 degrees.") 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) @@ -121,35 +117,35 @@ def _extract_cross_section(self): shape = self._cross_section[0] # Conditionally extract parameters for circular cross-section - if shape == 'circle': + if shape == "circle": # Check that list format is correct if len(self._cross_section) == 1: e = ValueError( - 'Format of list defining circular cross-section must be\n' + "Format of list defining circular cross-section must be\n" '["circle" (str), radius (float, cm)]' ) self._logger.error(e.args[0]) raise e elif len(self._cross_section) > 2: w = Warning( - 'More than one length dimension has been defined for ' - 'cross_section. Interpreting the first as the circle\'s' + "More than one length dimension has been defined for " + "cross_section. Interpreting the first as the circle's" 'radius; did you mean to use "rectangle"?' ) self._logger.warning(w.args[0]) - + # Extract parameters mag_len = self._cross_section[1] # Define string to pass to Cubit for cross-section generation - shape_str = f'{shape} radius {mag_len}' + shape_str = f"{shape} radius {mag_len}" # Conditinally extract parameters for rectangular cross-section - elif shape == 'rectangle': + elif shape == "rectangle": # Check that list format is correct if len(self._cross_section) != 3: e = ValueError( - 'Format of list defining rectangular cross-section must \n' + "Format of list defining rectangular cross-section must \n" 'be ["rectangle" (str), width (float, cm), thickness ' - '(float, cm)]' + "(float, cm)]" ) self._logger.error(e.args[0]) raise e @@ -159,22 +155,22 @@ def _extract_cross_section(self): # Detemine largest parameter mag_len = max(width, thickness) # Define string to pass to Cubit for cross-section generation - shape_str = f'{shape} width {thickness} height {width}' + shape_str = f"{shape} width {thickness} height {width}" # Otherwise, if input string is neither 'circle' nor 'rectangle', # raise an exception else: e = ValueError( - 'Magnet cross-section must be either a circle or rectangle. ' - 'The first entry of the list defining the cross-section must be' - ' the shape, with the following entries defining the shape' - 'parameters.\n' - '\n' - 'For a circular cross-section, the list format is\n' + "Magnet cross-section must be either a circle or rectangle. " + "The first entry of the list defining the cross-section must be" + " the shape, with the following entries defining the shape" + "parameters.\n" + "\n" + "For a circular cross-section, the list format is\n" '["circle" (str), radius (float, cm)]\n' - '\n' - 'For a rectangular cross-section, the list format is\n' + "\n" + "For a rectangular cross-section, the list format is\n" '["rectangle" (str), width (float, cm),' - 'thickness (float, cm)]' + "thickness (float, cm)]" ) self._logger.error(e.args[0]) raise e @@ -187,8 +183,8 @@ def _extract_filaments(self): """Extracts filament data from magnet coil data file. (Internal function not intended to be called externally) """ - with open(self.coils_file, 'r') as file: - data = file.readlines()[self.start_line:] + with open(self.coils_file, "r") as file: + data = file.readlines()[self.start_line :] coords = [] filaments = [] @@ -199,12 +195,12 @@ def _extract_filaments(self): for line in data: columns = line.strip().split() - if columns[0] == 'end': + if columns[0] == "end": break - x = float(columns[0])*self.scale - y = float(columns[1])*self.scale - z = float(columns[2])*self.scale + x = float(columns[0]) * self.scale + y = float(columns[1]) * self.scale + z = float(columns[2]) * self.scale # Coil current s = float(columns[3]) @@ -244,7 +240,7 @@ def _set_filtered_filaments(self): mag_len (float): characteristic length of magnets. Returns: - filtered_filaments (list of list of list of float): sorted list + filtered_filaments (list of list of list of float): sorted list of filament coordinates. """ # Initialize data for filaments within toroidal extent of model @@ -255,10 +251,10 @@ def _set_filtered_filaments(self): # Define tolerance of toroidal extent to account for width of coils # Multiply by factor of 2 to be conservative - tol = 2*np.arctan2(self.mag_len, self.average_radial_distance) + tol = 2 * np.arctan2(self.mag_len, self.average_radial_distance) # Compute lower and upper bounds of toroidal extent within tolerance - min_rad = 2*np.pi - tol + min_rad = 2 * np.pi - tol max_rad = self._toroidal_extent + tol for fil in self.filaments: @@ -267,15 +263,14 @@ def _set_filtered_filaments(self): # Compute toroidal angle of each point in filament phi_pts = np.arctan2(fil[:, 1], fil[:, 0]) # Ensure angles are positive - phi_pts = (phi_pts + 2*np.pi) % (2*np.pi) + phi_pts = (phi_pts + 2 * np.pi) % (2 * np.pi) # Compute bounds of toroidal extent of filament min_phi = np.min(phi_pts) max_phi = np.max(phi_pts) # Determine if filament toroidal extent overlaps with that of model - if ( - (min_phi >= min_rad or min_phi <= max_rad) or - (max_phi >= min_rad or max_phi <= max_rad) + if (min_phi >= min_rad or min_phi <= max_rad) or ( + max_phi >= min_rad or max_phi <= max_rad ): reduced_fils.append(fil) com_list.append(com) @@ -285,11 +280,12 @@ def _set_filtered_filaments(self): # Compute toroidal angles of filament centers of mass phi_arr = np.arctan2(com_list[:, 1], com_list[:, 0]) - phi_arr = (phi_arr + 2*np.pi) % (2*np.pi) + phi_arr = (phi_arr + 2 * np.pi) % (2 * np.pi) # Sort filaments by toroidal angle self.filtered_filaments = [ - x for _, x in sorted(zip(phi_arr, reduced_fils))] + x for _, x in sorted(zip(phi_arr, reduced_fils)) + ] def _cut_magnets(self, volume_ids): """Cleanly cuts the magnets at the planes defining the toriodal extent. @@ -304,40 +300,43 @@ def _cut_magnets(self, volume_ids): """ # Define sweeping surface width # Multiply by factor of 2 to be conservative - rec_width = 2*self.average_radial_distance + rec_width = 2 * self.average_radial_distance - cubit.cmd(f'create surface rectangle width {rec_width} yplane') + cubit.cmd(f"create surface rectangle width {rec_width} yplane") surf_id = cubit.get_last_id("surface") # Shift surface to positive x axis - cubit.cmd(f'move Surface {surf_id} x {rec_width/2}') + cubit.cmd(f"move Surface {surf_id} x {rec_width/2}") # Revolve surface to create wedge spanning toroidal extent cubit.cmd( - (f'sweep surface {surf_id} zaxis angle ' - f'{np.rad2deg(self._toroidal_extent)}') + ( + f"sweep surface {surf_id} zaxis angle " + f"{np.rad2deg(self._toroidal_extent)}" + ) ) sweep_id = cubit.get_last_id("volume") # Remove magnets and magnet portions not within toroidal extent cubit.cmd( - 'intersect volume ' + ' '.join(str(i) for i in volume_ids) - + f' {sweep_id}' + "intersect volume " + + " ".join(str(i) for i in volume_ids) + + f" {sweep_id}" ) # Renumber volume ids from 1 to N - cubit.cmd('compress all') + cubit.cmd("compress all") # Extract new volume ids - volume_ids = cubit.get_entities('volume') + volume_ids = cubit.get_entities("volume") return volume_ids - + def build_magnet_coils(self): """Builds each filament in self.filtered_filaments in cubit, then cuts to the toroidal extent using self._cut_magnets(). """ - self._logger.info('Constructing magnet coils...') + self._logger.info("Constructing magnet coils...") self._extract_filaments() self._set_average_radial_distance() @@ -358,7 +357,7 @@ def build_magnet_coils(self): self.volume_ids = volume_ids - def export_step(self, step_filename='magnets', export_dir=''): + def export_step(self, step_filename="magnets", export_dir=""): """Export CAD solids as a STEP file via Coreform Cubit. Arguments: @@ -367,22 +366,21 @@ def export_step(self, step_filename='magnets', export_dir=''): export_dir (str): directory to which to export the STEP output file (optional, defaults to empty string). """ - self._logger.info('Exporting STEP file for magnet coils...') + self._logger.info("Exporting STEP file for magnet coils...") cubit_io.export_step_cubit( filename=step_filename, export_dir=export_dir ) def mesh_magnets(self): - """Creates tetrahedral mesh of magnet volumes via Coreform Cubit. - """ - self._logger.info('Generating tetrahedral mesh of magnet coils...') - + """Creates tetrahedral mesh of magnet volumes via Coreform Cubit.""" + self._logger.info("Generating tetrahedral mesh of magnet coils...") + for vol in self.volume_ids: - cubit.cmd(f'volume {vol} scheme tetmesh') - cubit.cmd(f'mesh volume {vol}') - - def export_mesh(self, mesh_filename='magnet_mesh', export_dir=''): + cubit.cmd(f"volume {vol} scheme tetmesh") + cubit.cmd(f"mesh volume {vol}") + + 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. @@ -392,8 +390,8 @@ def export_mesh(self, mesh_filename='magnet_mesh', export_dir=''): 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...') - + self._logger.info("Exporting mesh H5M file for magnet coils...") + cubit_io.export_mesh_cubit( filename=mesh_filename, export_dir=export_dir ) @@ -409,13 +407,8 @@ class MagnetCoil(object): shape_str (str): string defining cross-section shape for Coreform Cubit. """ - def __init__( - self, - filament, - shape, - shape_str - ): - + def __init__(self, filament, shape, shape_str): + self.filament = filament self.shape = shape self.shape_str = shape_str @@ -441,7 +434,7 @@ def _orient_rectangle( # oriented along filament origin tangent # Compute part of thickness vector parallel to rotation axis - t_vec_par = normalize(np.inner(t_vec, rot_axis)*rot_axis) + t_vec_par = normalize(np.inner(t_vec, rot_axis) * rot_axis) # Compute part of thickness vector orthogonal to rotation axis t_vec_perp = normalize(t_vec - t_vec_par) @@ -455,7 +448,7 @@ def _orient_rectangle( rot_perp = np.sin(rot_ang_norm) # Compute orthogonal part of thickness vector after rotation - t_vec_perp_rot = rot_par*t_vec_perp + rot_perp*orth + t_vec_perp_rot = rot_par * t_vec_perp + rot_perp * orth # Compute thickness vector after rotation t_vec_rot = normalize(t_vec_perp_rot + t_vec_par) @@ -465,7 +458,7 @@ def _orient_rectangle( pos = cubit.vertex(path_origin).coordinates() # Project position vector onto cross-section - pos_proj = normalize(pos - np.inner(pos, norm)*norm) + pos_proj = normalize(pos - np.inner(pos, norm) * norm) # Compute angle by which to rotate cross-section such that it faces the # origin @@ -474,10 +467,10 @@ def _orient_rectangle( # Re-orient rotated cross-section such that thickness vector faces # origin cubit.cmd( - f'rotate Surface {surf_id} angle {np.rad2deg(rot_ang_orig)} about ' - 'origin 0 0 0 direction ' + ' '.join(str(i) for i in norm) + f"rotate Surface {surf_id} angle {np.rad2deg(rot_ang_orig)} about " + "origin 0 0 0 direction " + " ".join(str(i) for i in norm) ) - + def create_magnet(self): """Creates magnet coil volumes in cubit. @@ -489,7 +482,7 @@ def create_magnet(self): t_vec = np.array([1, 0, 0]) # Create cross-section for sweep - cubit.cmd(f'create surface ' + self.shape_str + ' zplane') + cubit.cmd(f"create surface " + self.shape_str + " zplane") # Store cross-section index cs_id = cubit.get_last_id("surface") @@ -502,15 +495,15 @@ def create_magnet(self): # Create vertices in filament path for x, y, z in self.filament: - cubit.cmd(f'create vertex {x} {y} {z}') + cubit.cmd(f"create vertex {x} {y} {z}") path += [cubit.get_last_id("vertex")] # Ensure final vertex in path is the same as the first path += [path[0]] cubit.cmd( - f'create curve spline location vertex ' + - ' '.join(str(i) for i in path) + f"create curve spline location vertex " + + " ".join(str(i) for i in path) ) curve_id = cubit.get_last_id("curve") @@ -534,74 +527,73 @@ def create_magnet(self): rot_ang_norm = np.arccos(np.inner(cs_axis, tang)) # Copy cross-section for sweep - cubit.cmd(f'surface {cs_id} copy') + cubit.cmd(f"surface {cs_id} copy") surf_id = cubit.get_last_id("surface") # Orient cross-section along defined normal cubit.cmd( - f'rotate Surface {surf_id} angle {np.rad2deg(rot_ang_norm)} about ' - 'origin 0 0 0 direction ' + ' '.join(str(i) for i in rot_axis) + f"rotate Surface {surf_id} angle {np.rad2deg(rot_ang_norm)} about " + "origin 0 0 0 direction " + " ".join(str(i) for i in rot_axis) ) # Conditionally orients rectangular cross-section - if self.shape == 'rectangle': + if self.shape == "rectangle": self._orient_rectangle( path[0], surf_id, t_vec, tang, rot_axis, rot_ang_norm ) # Move cross-section to initial path point - cubit.cmd(f'move Surface {surf_id} location vertex {path[0]}') + cubit.cmd(f"move Surface {surf_id} location vertex {path[0]}") # Sweep cross-section to create magnet coil cubit.cmd( - f'sweep surface {surf_id} along curve {curve_id} ' - f'individual' + f"sweep surface {surf_id} along curve {curve_id} " f"individual" ) volume_id = cubit.get_last_id("volume") # Delete extraneous curves and vertices - cubit.cmd(f'delete curve {curve_id}') - cubit.cmd('delete vertex all') + cubit.cmd(f"delete curve {curve_id}") + cubit.cmd("delete vertex all") # Delete original cross-section - cubit.cmd(f'delete surface {cs_id}') + cubit.cmd(f"delete surface {cs_id}") return volume_id def parse_args(): - """Parser for running as a script - """ - parser = argparse.ArgumentParser(prog='magnet_coils') + """Parser for running as a script""" + parser = argparse.ArgumentParser(prog="magnet_coils") parser.add_argument( - 'filename', help='YAML file defining ParaStell magnet configuration' + "filename", help="YAML file defining ParaStell magnet configuration" ) parser.add_argument( - '-e', '--export_dir', - default='', + "-e", + "--export_dir", + default="", help=( - 'Directory to which output files are exported (default: working ' - 'directory)' + "Directory to which output files are exported (default: working " + "directory)" ), - metavar='' + metavar="", ) parser.add_argument( - '-l', '--logger', + "-l", + "--logger", default=False, help=( - 'Flag to indicate whether to instantiate a logger object (default: ' - 'False)' + "Flag to indicate whether to instantiate a logger object (default: " + "False)" ), - metavar='' + metavar="", ) return parser.parse_args() def generate_magnet_set(): - """Main method when run as command line script. - """ + """Main method when run as command line script.""" args = parse_args() all_data = read_yaml_config(args.filename) @@ -611,29 +603,28 @@ def generate_magnet_set(): else: logger = log.NullLogger() - magnet_coils_dict = all_data['magnet_coils'] + magnet_coils_dict = all_data["magnet_coils"] magnet_set = MagnetSet( - magnet_coils_dict['coils_file'], - magnet_coils_dict['cross_section'], - magnet_coils_dict['toroidal_extent'], - logger=logger - **magnet_coils_dict + magnet_coils_dict["coils_file"], + magnet_coils_dict["cross_section"], + magnet_coils_dict["toroidal_extent"], + logger=logger**magnet_coils_dict, ) magnet_set.build_magnet_coils() magnet_set.export_step( export_dir=args.export_dir, - **(filter_kwargs(magnet_coils_dict, ['step_filename'])) + **(filter_kwargs(magnet_coils_dict, ["step_filename"])), ) - if magnet_coils_dict['export_mesh']: + if magnet_coils_dict["export_mesh"]: magnet_set.export_mesh( export_dir=args.export_dir, - **(filter_kwargs(magnet_coils_dict, ['mesh_filename'])) + **(filter_kwargs(magnet_coils_dict, ["mesh_filename"])), ) -if __name__ == '__main__': +if __name__ == "__main__": generate_magnet_set() diff --git a/parastell/parastell.py b/parastell/parastell.py index 5b098b9..dc4e654 100644 --- a/parastell/parastell.py +++ b/parastell/parastell.py @@ -14,10 +14,14 @@ from . import cubit_io from .utils import read_yaml_config, filter_kwargs, m2cm -build_cubit_model_allowed_kwargs = ['skip_imprint', 'legacy_faceting'] -export_dagmc_allowed_kwargs = ['faceting_tolerance', 'length_tolerance', - 'normal_tolerance', 'anisotropic_ratio', - 'deviation_angle'] +build_cubit_model_allowed_kwargs = ["skip_imprint", "legacy_faceting"] +export_dagmc_allowed_kwargs = [ + "faceting_tolerance", + "length_tolerance", + "normal_tolerance", + "anisotropic_ratio", + "deviation_angle", +] def make_material_block(mat_tag, block_id, vol_id_str): @@ -30,16 +34,9 @@ def make_material_block(mat_tag, block_id, vol_id_str): vol_id_str (str) : space-separated list of volume ids """ - cubit.cmd( - f'create material "{mat_tag}" property_group ' - '"CUBIT-ABAQUS"' - ) - cubit.cmd( - f'block {block_id} add volume {vol_id_str}' - ) - cubit.cmd( - f'block {block_id} material "{mat_tag}"' - ) + cubit.cmd(f'create material "{mat_tag}" property_group ' '"CUBIT-ABAQUS"') + cubit.cmd(f"block {block_id} add volume {vol_id_str}") + cubit.cmd(f'block {block_id} material "{mat_tag}"') class Stellarator(object): @@ -58,11 +55,7 @@ class Stellarator(object): logger is supplied, a default logger will be instantiated. """ - def __init__( - self, - vmec_file, - logger=None - ): + def __init__(self, vmec_file, logger=None): self.logger = logger self.vmec_file = vmec_file @@ -93,7 +86,13 @@ def logger(self, logger_object): self._logger = log.check_init(logger_object) def construct_invessel_build( - self, toroidal_angles, poloidal_angles, wall_s, radial_build, split_chamber=False, **kwargs + self, + toroidal_angles, + poloidal_angles, + wall_s, + radial_build, + split_chamber=False, + **kwargs, ): """Construct InVesselBuild class object. @@ -155,14 +154,11 @@ def construct_invessel_build( radial_build, split_chamber=split_chamber, logger=self._logger, - **kwargs + **kwargs, ) self.invessel_build = ivb.InVesselBuild( - self._vmec_obj, - self.radial_build, - logger=self._logger, - **kwargs + self._vmec_obj, self.radial_build, logger=self._logger, **kwargs ) self.invessel_build.populate_surfaces() @@ -170,7 +166,7 @@ def construct_invessel_build( self.invessel_build.generate_components() def export_invessel_build( - self, export_cad_to_dagmc=False, dagmc_filename='dagmc', export_dir='' + self, export_cad_to_dagmc=False, dagmc_filename="dagmc", export_dir="" ): """Exports InVesselBuild component STEP files and, optionally, a DAGMC neutronics H5M file of in-vessel components via CAD-to-DAGMC. @@ -188,8 +184,7 @@ def export_invessel_build( if export_cad_to_dagmc: self.invessel_build.export_cad_to_dagmc( - dagmc_filename=dagmc_filename, - export_dir=export_dir + dagmc_filename=dagmc_filename, export_dir=export_dir ) def construct_magnets( @@ -222,14 +217,17 @@ def construct_magnets( cross_section, toroidal_extent, logger=self._logger, - **kwargs + **kwargs, ) self.magnet_set.build_magnet_coils() def export_magnets( - self, step_filename='magnets', export_mesh=False, - mesh_filename='magnet_mesh', export_dir='', + self, + step_filename="magnets", + export_mesh=False, + mesh_filename="magnet_mesh", + export_dir="", ): """Export magnet components. @@ -244,20 +242,16 @@ def export_magnets( (optional, defaults to empty string). """ self.magnet_set.export_step( - step_filename=step_filename, - export_dir=export_dir + step_filename=step_filename, export_dir=export_dir ) if export_mesh: self.magnet_set.mesh_magnets() self.magnet_set.export_mesh( - mesh_filename=mesh_filename, - export_dir=export_dir + mesh_filename=mesh_filename, export_dir=export_dir ) - def construct_source_mesh( - self, mesh_size, toroidal_extent, **kwargs - ): + def construct_source_mesh(self, mesh_size, toroidal_extent, **kwargs): """Constructs SourceMesh class object. Arguments: @@ -276,10 +270,10 @@ def construct_source_mesh( (defaults to m2cm = 100). plasma_conditions (function): function that takes the plasma parameter s, and returns temperature and ion density with - suitable units for the reaction_rate() function. Defaults to + suitable units for the reaction_rate() function. Defaults to default_plasma_conditions() reaction_rate (function): function that takes the values returned by - plasma_conditions() and returns a reaction rate in + plasma_conditions() and returns a reaction rate in reactions/cm3/s """ self.source_mesh = sm.SourceMesh( @@ -287,13 +281,13 @@ def construct_source_mesh( mesh_size, toroidal_extent, logger=self._logger, - **kwargs + **kwargs, ) self.source_mesh.create_vertices() self.source_mesh.create_mesh() - def export_source_mesh(self, filename='source_mesh', export_dir=''): + def export_source_mesh(self, filename="source_mesh", export_dir=""): """Export source mesh Arguments: @@ -302,22 +296,20 @@ def export_source_mesh(self, filename='source_mesh', export_dir=''): export_dir (str): directory to which to export H5M output file (optional, defaults to empty string). """ - self.source_mesh.export_mesh( - filename=filename, - export_dir=export_dir - ) + self.source_mesh.export_mesh(filename=filename, export_dir=export_dir) def _import_ivb_step(self): """Imports STEP files from in-vessel build into Coreform Cubit. (Internal function not intended to be called externally) """ - for name, data in ( - self.invessel_build.radial_build.radial_build.items() - ): + for ( + name, + data, + ) in self.invessel_build.radial_build.radial_build.items(): vol_id = cubit_io.import_step_cubit( name, self.invessel_build.export_dir ) - data['vol_id'] = vol_id + data["vol_id"] = vol_id def _tag_materials_legacy(self): """Applies material tags to corresponding CAD volumes for legacy DAGMC @@ -333,9 +325,7 @@ def _tag_materials_legacy(self): ) if self.invessel_build: - for data in ( - self.invessel_build.radial_build.radial_build.values() - ): + for data in self.invessel_build.radial_build.radial_build.values(): cubit.cmd( f'group "mat:{data["mat_tag"]}" add volume {data["vol_id"]}' ) @@ -345,7 +335,7 @@ def _tag_materials_native(self): neutronics model export. (Internal function not intended to be called externally) """ - cubit.cmd('set duplicate block elements off') + cubit.cmd("set duplicate block elements off") if self.magnet_set: vol_list = list(self.magnet_set.volume_ids) @@ -354,12 +344,10 @@ def _tag_materials_native(self): make_material_block(self.magnet_set.mat_tag, block_id, vol_id_str) if self.invessel_build: - for data in ( - self.invessel_build.radial_build.radial_build.values() - ): - block_id = data['vol_id'] + for data in self.invessel_build.radial_build.radial_build.values(): + block_id = data["vol_id"] vol_id_str = str(block_id) - make_material_block(data['mat_tag'], block_id, vol_id_str) + make_material_block(data["mat_tag"], block_id, vol_id_str) def build_cubit_model(self, skip_imprint=False, legacy_faceting=True): """Build model for DAGMC neutronics H5M file of Parastell components via @@ -375,7 +363,7 @@ def build_cubit_model(self, skip_imprint=False, legacy_faceting=True): self.legacy_faceting = legacy_faceting self._logger.info( - 'Building DAGMC neutronics model via Coreform Cubit...' + "Building DAGMC neutronics model via Coreform Cubit..." ) if self.invessel_build: @@ -384,15 +372,15 @@ def build_cubit_model(self, skip_imprint=False, legacy_faceting=True): if skip_imprint: self.invessel_build.merge_layer_surfaces() else: - cubit.cmd('imprint volume all') - cubit.cmd('merge volume all') + cubit.cmd("imprint volume all") + cubit.cmd("merge volume all") if legacy_faceting: self._tag_materials_legacy() else: self._tag_materials_native() - def export_dagmc(self, filename='dagmc', export_dir='', **kwargs): + def export_dagmc(self, filename="dagmc", export_dir="", **kwargs): """Exports DAGMC neutronics H5M file of ParaStell components via Coreform Cubit. @@ -423,24 +411,18 @@ def export_dagmc(self, filename='dagmc', export_dir='', **kwargs): """ cubit_io.init_cubit() - self._logger.info( - 'Exporting DAGMC neutronics model...' - ) + self._logger.info("Exporting DAGMC neutronics model...") if self.legacy_faceting: cubit_io.export_dagmc_cubit_legacy( - filename=filename, - export_dir=export_dir, - **kwargs + filename=filename, export_dir=export_dir, **kwargs ) else: cubit_io.export_dagmc_cubit_native( - filename=filename, - export_dir=export_dir, - **kwargs + filename=filename, export_dir=export_dir, **kwargs ) - def export_cub5(self, filename='stellarator', export_dir=''): + def export_cub5(self, filename="stellarator", export_dir=""): """Export native Coreform Cubit format (cub5) of Parastell model. Arguments: @@ -451,74 +433,76 @@ def export_cub5(self, filename='stellarator', export_dir=''): """ cubit_io.init_cubit() - self._logger.info( - 'Exporting cub5 model...' - ) + self._logger.info("Exporting cub5 model...") - cubit_io.export_cub5(filename=filename, - export_dir=export_dir) + cubit_io.export_cub5(filename=filename, export_dir=export_dir) def parse_args(): - """Parser for running as a script. - """ - parser = argparse.ArgumentParser(prog='stellarator') + """Parser for running as a script.""" + parser = argparse.ArgumentParser(prog="stellarator") parser.add_argument( - 'filename', - help='YAML file defining ParaStell stellarator configuration' + "filename", + help="YAML file defining ParaStell stellarator configuration", ) parser.add_argument( - '-e', '--export_dir', - default='', + "-e", + "--export_dir", + default="", help=( - 'directory to which output files are exported (default: working ' - 'directory)' + "directory to which output files are exported (default: working " + "directory)" ), - metavar='' + metavar="", ) parser.add_argument( - '-l', '--logger', - action='store_true', + "-l", + "--logger", + action="store_true", help=( - 'flag to indicate the instantiation of a logger object (default: ' - 'False)' - ) + "flag to indicate the instantiation of a logger object (default: " + "False)" + ), ) parser.add_argument( - '-i', '--ivb', - action='store_true', + "-i", + "--ivb", + action="store_true", help=( - 'flag to indicate the creation of in-vessel component geometry ' - '(default: False)' - ) + "flag to indicate the creation of in-vessel component geometry " + "(default: False)" + ), ) parser.add_argument( - '-m', '--magnets', - action='store_true', + "-m", + "--magnets", + action="store_true", help=( - 'flag to indicate the creation of magnet geometry (default: False)' - ) + "flag to indicate the creation of magnet geometry (default: False)" + ), ) parser.add_argument( - '-s', '--source', - action='store_true', + "-s", + "--source", + action="store_true", help=( - 'flag to indicate the creation of a tetrahedral source mesh ' - '(default: False)' - ) + "flag to indicate the creation of a tetrahedral source mesh " + "(default: False)" + ), ) parser.add_argument( - '-n', '--nwl', - action='store_true', + "-n", + "--nwl", + action="store_true", help=( - 'flag to indicate the creation of a geometry for neutron wall ' - 'loading calculations (default: False)' - ) + "flag to indicate the creation of a geometry for neutron wall " + "loading calculations (default: False)" + ), ) return parser.parse_args() @@ -537,83 +521,82 @@ def check_inputs( dagmc_export (dict): dictionary of DAGMC export parameters. logger (object): logger object. """ - if 'repeat' in invessel_build: - repeat = invessel_build['repeat'] + if "repeat" in invessel_build: + repeat = invessel_build["repeat"] else: repeat = 0 - ivb_tor_ext = (repeat + 1) * invessel_build['toroidal_angles'][-1] - mag_tor_ext = magnet_coils['toroidal_extent'] - src_tor_ext = source_mesh['toroidal_extent'] + ivb_tor_ext = (repeat + 1) * invessel_build["toroidal_angles"][-1] + mag_tor_ext = magnet_coils["toroidal_extent"] + src_tor_ext = source_mesh["toroidal_extent"] if ivb_tor_ext != mag_tor_ext: w = Warning( - f'The total toroidal extent of the in-vessel build, {ivb_tor_ext} ' - 'degrees, does not match the toroidal extent of the magnet coils, ' - f'{mag_tor_ext} degrees.' + f"The total toroidal extent of the in-vessel build, {ivb_tor_ext} " + "degrees, does not match the toroidal extent of the magnet coils, " + f"{mag_tor_ext} degrees." ) logger.warning(w.args[0]) if ivb_tor_ext != src_tor_ext: w = Warning( - f'The total toroidal extent of the in-vessel build, {ivb_tor_ext} ' - 'degrees, does not match the toroidal extent of the source mesh, ' - f'{src_tor_ext} degrees.' + f"The total toroidal extent of the in-vessel build, {ivb_tor_ext} " + "degrees, does not match the toroidal extent of the source mesh, " + f"{src_tor_ext} degrees." ) logger.warning(w.args[0]) if mag_tor_ext != src_tor_ext: w = Warning( - f'The toroidal extent of the magnet coils, {mag_tor_ext} degrees, ' - f'does not match that of the source mesh, {src_tor_ext} degrees.' + f"The toroidal extent of the magnet coils, {mag_tor_ext} degrees, " + f"does not match that of the source mesh, {src_tor_ext} degrees." ) logger.warning(w.args[0]) - if 'scale' in invessel_build: - ivb_scale = invessel_build['scale'] + if "scale" in invessel_build: + ivb_scale = invessel_build["scale"] else: ivb_scale = m2cm - if 'scale' in source_mesh: - src_scale = source_mesh['scale'] + if "scale" in source_mesh: + src_scale = source_mesh["scale"] else: src_scale = m2cm if ivb_scale != src_scale: e = ValueError( - f'The conversion scale of the in-vessel build, {ivb_scale}, does ' - f'not match that of the source mesh, {src_scale}.' + f"The conversion scale of the in-vessel build, {ivb_scale}, does " + f"not match that of the source mesh, {src_scale}." ) logger.error(e.args[0]) raise e if ( - 'export_cad_to_dagmc' in invessel_build and - invessel_build['export_cad_to_dagmc'] + "export_cad_to_dagmc" in invessel_build + and invessel_build["export_cad_to_dagmc"] ): - if 'dagmc_filename' in invessel_build: - ivb_dagmc_filename = invessel_build['dagmc_filename'] + if "dagmc_filename" in invessel_build: + ivb_dagmc_filename = invessel_build["dagmc_filename"] else: - ivb_dagmc_filename = 'dagmc' + ivb_dagmc_filename = "dagmc" - if 'filename' in dagmc_export: - ps_dagmc_filename = dagmc_export['filename'] + if "filename" in dagmc_export: + ps_dagmc_filename = dagmc_export["filename"] else: - ps_dagmc_filename = 'dagmc' + ps_dagmc_filename = "dagmc" if ivb_dagmc_filename == ps_dagmc_filename: e = ValueError( - 'The DAGMC H5M filename for the CAD-to-DAGMC export matches ' - 'that of the Coreform Cubit DAGMC export. Please change one to ' - 'prevent overwriting files.' + "The DAGMC H5M filename for the CAD-to-DAGMC export matches " + "that of the Coreform Cubit DAGMC export. Please change one to " + "prevent overwriting files." ) logger.error(e.args[0]) raise e def parastell(): - """Main method when run as a command line script. - """ + """Main method when run as a command line script.""" args = parse_args() all_data = read_yaml_config(args.filename) @@ -624,83 +607,73 @@ def parastell(): logger = log.NullLogger() check_inputs( - all_data['invessel_build'], - all_data['magnet_coils'], - all_data['source_mesh'], - all_data['dagmc_export'], - logger + all_data["invessel_build"], + all_data["magnet_coils"], + all_data["source_mesh"], + all_data["dagmc_export"], + logger, ) - vmec_file = all_data['vmec_file'] + vmec_file = all_data["vmec_file"] - stellarator = Stellarator( - vmec_file, - logger=logger - ) + stellarator = Stellarator(vmec_file, logger=logger) if args.ivb: - invessel_build = all_data['invessel_build'] + invessel_build = all_data["invessel_build"] stellarator.construct_invessel_build(**invessel_build) stellarator.export_invessel_build( export_dir=args.export_dir, - **(filter_kwargs(invessel_build, ivb.export_allowed_kwargs)) + **(filter_kwargs(invessel_build, ivb.export_allowed_kwargs)), ) if args.magnets: - magnet_coils = all_data['magnet_coils'] + magnet_coils = all_data["magnet_coils"] stellarator.construct_magnets(**magnet_coils) stellarator.export_magnets( export_dir=args.export_dir, - **(filter_kwargs(magnet_coils, mc.export_allowed_kwargs)) + **(filter_kwargs(magnet_coils, mc.export_allowed_kwargs)), ) if args.source: - source_mesh = all_data['source_mesh'] + source_mesh = all_data["source_mesh"] stellarator.construct_source_mesh(**source_mesh) stellarator.export_source_mesh( export_dir=args.export_dir, - **(filter_kwargs(source_mesh, sm.export_allowed_kwargs)) + **(filter_kwargs(source_mesh, sm.export_allowed_kwargs)), ) if args.ivb or args.magnets: - dagmc_export = all_data['dagmc_export'] + dagmc_export = all_data["dagmc_export"] stellarator.build_cubit_model( **(filter_kwargs(dagmc_export, build_cubit_model_allowed_kwargs)) ) stellarator.export_dagmc( export_dir=args.export_dir, - **(filter_kwargs(dagmc_export, export_dagmc_allowed_kwargs)) + **(filter_kwargs(dagmc_export, export_dagmc_allowed_kwargs)), ) - if all_data['cub5_export']: + if all_data["cub5_export"]: stellarator.export_cub5(export_dir=args.export_dir) if args.nwl: if not args.ivb: - invessel_build = all_data['invessel_build'] + invessel_build = all_data["invessel_build"] if not args.magnets: - dagmc_export = all_data['dagmc_export'] + dagmc_export = all_data["dagmc_export"] if cubit_io.initialized: - cubit.cmd('new') + cubit.cmd("new") - nwl_geom = Stellarator( - vmec_file, - logger=logger - ) + nwl_geom = Stellarator(vmec_file, logger=logger) - nwl_required_keys = [ - 'toroidal_angles', 'poloidal_angles', 'wall_s' - ] + nwl_required_keys = ["toroidal_angles", "poloidal_angles", "wall_s"] nwl_build = {} for key in nwl_keys: nwl_build[key] = invessel_build[key] - nwl_build['radial_build'] = {} + nwl_build["radial_build"] = {} - nwl_optional_keys = [ - 'num_ribs', 'num_rib_pts', 'repeat', 'scale' - ] + nwl_optional_keys = ["num_ribs", "num_rib_pts", "repeat", "scale"] for key in invessel_build.keys() & nwl_optional_keys: nwl_build[key] = invessel_build[key] @@ -709,9 +682,7 @@ def parastell(): nwl_geom.export_invessel_build(export_dir=args.export_dir) nwl_geom.export_dagmc( - skip_imprint=True, - filename='nwl_geom', - export_dir=args.export_dir + skip_imprint=True, filename="nwl_geom", export_dir=args.export_dir ) diff --git a/parastell/source_mesh.py b/parastell/source_mesh.py index b2cae64..df750fe 100644 --- a/parastell/source_mesh.py +++ b/parastell/source_mesh.py @@ -8,12 +8,12 @@ from . import log as log from .utils import read_yaml_config, filter_kwargs, m2cm, m3tocm3 -export_allowed_kwargs = ['filename'] +export_allowed_kwargs = ["filename"] def default_reaction_rate(n_i, T_i): - """Default reaction rate formula for DT fusion assumes an equal mixture of - D and T in a hot plasma. From A. Bader et al 2021 Nucl. Fusion 61 116060 + """Default reaction rate formula for DT fusion assumes an equal mixture of + D and T in a hot plasma. From A. Bader et al 2021 Nucl. Fusion 61 116060 DOI 10.1088/1741-4326/ac2991 @@ -41,7 +41,7 @@ def default_reaction_rate(n_i, T_i): def default_plasma_conditions(s): """Calculates ion density and temperature as a function of the - plasma paramter s using profiles found in A. Bader et al 2021 Nucl. Fusion + plasma paramter s using profiles found in A. Bader et al 2021 Nucl. Fusion 61 116060 DOI 10.1088/1741-4326/ac2991 Arguments: @@ -64,7 +64,7 @@ def default_plasma_conditions(s): class SourceMesh(object): """Generates a source mesh that describes the relative source intensity of neutrons in a magnetically confined plasma described by a VMEC plasma - equilibrium. + equilibrium. The mesh will be defined on a regular grid in the plasma coordinates of s, theta, phi. Mesh vertices will be defined on circular grid at each toroidal @@ -100,19 +100,14 @@ class SourceMesh(object): (defaults to m2cm = 100). plasma_conditions (function): function that takes the plasma parameter s, and returns temperature and ion density with suitable units for - the reaction_rate() function. Defaults to + the reaction_rate() function. Defaults to default_plasma_conditions() reaction_rate (function): function that takes the values returned by plasma_conditions() and returns a reaction rate in reactions/cm3/s """ def __init__( - self, - vmec_obj, - mesh_size, - toroidal_extent, - logger=None, - **kwargs + self, vmec_obj, mesh_size, toroidal_extent, logger=None, **kwargs ): self.logger = logger @@ -145,9 +140,7 @@ def toroidal_extent(self): def toroidal_extent(self, angle): self._toroidal_extent = np.deg2rad(angle) if self._toroidal_extent > 360.0: - e = ValueError( - 'Toroidal extent cannot exceed 360.0 degrees.' - ) + e = ValueError("Toroidal extent cannot exceed 360.0 degrees.") self._logger.error(e.args[0]) raise e @@ -174,8 +167,7 @@ def _create_mbc(self): storage_type = types.MB_TAG_DENSE tag_name = "SourceStrength" self.tag_handle = self.mbc.tag_get_handle( - tag_name, tag_size, tag_type, storage_type, - create_if_missing=True + tag_name, tag_size, tag_type, storage_type, create_if_missing=True ) def create_vertices(self): @@ -187,16 +179,16 @@ def create_vertices(self): mesh at the 0 == 2 * pi wrap so that everything is closed and consistent. """ - self._logger.info('Computing source mesh point cloud...') + self._logger.info("Computing source mesh point cloud...") phi_list = np.linspace(0, self._toroidal_extent, num=self.num_phi) # don't include magnetic axis in list of s values s_list = np.linspace(0.0, 1.0, num=self.num_s)[1:] # don't include repeated entry at 0 == 2*pi - theta_list = np.linspace(0, 2*np.pi, num=self.num_theta)[:-1] + theta_list = np.linspace(0, 2 * np.pi, num=self.num_theta)[:-1] # don't include repeated entry at 0 == 2*pi - if self._toroidal_extent == 2*np.pi: + if self._toroidal_extent == 2 * np.pi: phi_list = phi_list[:-1] self.verts_per_ring = theta_list.shape[0] @@ -212,8 +204,9 @@ def create_vertices(self): for phi in phi_list: # vertex coordinates on magnetic axis - self.coords[vert_idx, :] = np.array( - self.vmec_obj.vmec2xyz(0, 0, phi)) * self.scale + self.coords[vert_idx, :] = ( + np.array(self.vmec_obj.vmec2xyz(0, 0, phi)) * self.scale + ) self.coords_s[vert_idx] = 0 vert_idx += 1 @@ -221,9 +214,10 @@ def create_vertices(self): # vertex coordinate away from magnetic axis for s in s_list: for theta in theta_list: - self.coords[vert_idx, :] = np.array( - self.vmec_obj.vmec2xyz(s, theta, phi) - ) * self.scale + self.coords[vert_idx, :] = ( + np.array(self.vmec_obj.vmec2xyz(s, theta, phi)) + * self.scale + ) self.coords_s[vert_idx] = s vert_idx += 1 @@ -247,20 +241,20 @@ def _source_strength(self, tet_ids): # Initialize list of source strengths for each tetrahedron vertex vertex_strengths = [ - self.reaction_rate( - *self.plasma_conditions(self.coords_s[id]) - ) + self.reaction_rate(*self.plasma_conditions(self.coords_s[id])) for id in tet_ids ] # Define barycentric coordinates for integration points - bary_coords = np.array([ - [0.25, 0.25, 0.25, 0.25], - [0.5, 1/6, 1/6, 1/6], - [1/6, 0.5, 1/6, 1/6], - [1/6, 1/6, 0.5, 1/6], - [1/6, 1/6, 1/6, 0.5] - ]) + bary_coords = np.array( + [ + [0.25, 0.25, 0.25, 0.25], + [0.5, 1 / 6, 1 / 6, 1 / 6], + [1 / 6, 0.5, 1 / 6, 1 / 6], + [1 / 6, 1 / 6, 0.5, 1 / 6], + [1 / 6, 1 / 6, 1 / 6, 0.5], + ] + ) # Define weights for integration points int_w = np.array([-0.8, 0.45, 0.45, 0.45, 0.45]) @@ -271,7 +265,7 @@ def _source_strength(self, tet_ids): # Compute edge vectors between tetrahedron vertices edge_vectors = np.subtract(tet_coords[:3], tet_coords[3]).T - tet_vol = np.abs(np.linalg.det(edge_vectors))/6 + tet_vol = np.abs(np.linalg.det(edge_vectors)) / 6 ss = tet_vol * np.dot(int_w, ss_int_pts) @@ -302,7 +296,7 @@ def _get_vertex_id(self, vertex_idx): (Internal function not intended to be called externally) Arguments: - vert_idx (list of int): list of vertex + vert_idx (list of int): list of vertex [flux surface index, poloidal angle index, toroidal angle index] Returns: @@ -314,7 +308,7 @@ def _get_vertex_id(self, vertex_idx): ma_offset = phi_idx * self.verts_per_plane # Wrap around if final plane and it is 2*pi - if self._toroidal_extent == 2*np.pi and phi_idx == self.num_phi - 1: + if self._toroidal_extent == 2 * np.pi and phi_idx == self.num_phi - 1: ma_offset = 0 # Compute index offset from closed flux surface @@ -339,23 +333,27 @@ def _create_tets_from_hex(self, s_idx, theta_idx, phi_idx): """ # relative offsets of vertices in a 3-D index space - hex_vertex_stencil = np.array([ - [0, 0, 0], - [1, 0, 0], - [1, 1, 0], - [0, 1, 0], - [0, 0, 1], - [1, 0, 1], - [1, 1, 1], - [0, 1, 1] - ]) + hex_vertex_stencil = np.array( + [ + [0, 0, 0], + [1, 0, 0], + [1, 1, 0], + [0, 1, 0], + [0, 0, 1], + [1, 0, 1], + [1, 1, 1], + [0, 1, 1], + ] + ) # Ids of hex vertices applying offset stencil to current point - hex_idx_data = np.array( - [s_idx, theta_idx, phi_idx]) + hex_vertex_stencil + hex_idx_data = ( + np.array([s_idx, theta_idx, phi_idx]) + hex_vertex_stencil + ) - idx_list = [self._get_vertex_id(vertex_idx) - for vertex_idx in hex_idx_data] + idx_list = [ + self._get_vertex_id(vertex_idx) for vertex_idx in hex_idx_data + ] # Define MOAB canonical ordering of hexahedron vertex indices hex_canon_ids = [ @@ -363,7 +361,7 @@ def _create_tets_from_hex(self, s_idx, theta_idx, phi_idx): [idx_list[7], idx_list[4], idx_list[6], idx_list[3]], [idx_list[2], idx_list[1], idx_list[3], idx_list[6]], [idx_list[5], idx_list[6], idx_list[4], idx_list[1]], - [idx_list[3], idx_list[1], idx_list[4], idx_list[6]] + [idx_list[3], idx_list[1], idx_list[4], idx_list[6]], ] for vertex_ids in hex_canon_ids: @@ -378,35 +376,37 @@ def _create_tets_from_wedge(self, theta_idx, phi_idx): """ # relative offsets of wedge vertices in a 3-D index space - wedge_vertex_stencil = np.array([ - [0, 0, 0], - [0, theta_idx, 0], - [0, theta_idx + 1, 0], - [0, 0, 1], - [0, theta_idx, 1], - [0, theta_idx + 1, 1] - ]) + wedge_vertex_stencil = np.array( + [ + [0, 0, 0], + [0, theta_idx, 0], + [0, theta_idx + 1, 0], + [0, 0, 1], + [0, theta_idx, 1], + [0, theta_idx + 1, 1], + ] + ) # Ids of wedge vertices applying offset stencil to current point wedge_idx_data = np.array([0, 0, phi_idx]) + wedge_vertex_stencil - idx_list = [self._get_vertex_id(vertex_idx) - for vertex_idx in wedge_idx_data] + idx_list = [ + self._get_vertex_id(vertex_idx) for vertex_idx in wedge_idx_data + ] # Define MOAB canonical ordering of wedge vertex indices wedge_canon_ids = [ [idx_list[1], idx_list[2], idx_list[4], idx_list[0]], [idx_list[5], idx_list[4], idx_list[2], idx_list[3]], - [idx_list[0], idx_list[2], idx_list[4], idx_list[3]] + [idx_list[0], idx_list[2], idx_list[4], idx_list[3]], ] for vertex_ids in wedge_canon_ids: self._create_tet(vertex_ids) def create_mesh(self): - """Creates volumetric source mesh in real space. - """ - self._logger.info('Constructing source mesh...') + """Creates volumetric source mesh in real space.""" + self._logger.info("Constructing source mesh...") self.mesh_set = self.mbc.create_meshset() self.mbc.add_entity(self.mesh_set, self.verts) @@ -421,7 +421,7 @@ def create_mesh(self): for theta_idx in range(1, self.num_theta): self._create_tets_from_hex(s_idx, theta_idx, phi_idx) - def export_mesh(self, filename='source_mesh', export_dir=''): + def export_mesh(self, filename="source_mesh", export_dir=""): """Use PyMOAB interface to write source mesh with source strengths tagged. @@ -431,46 +431,46 @@ def export_mesh(self, filename='source_mesh', export_dir=''): export_dir (str): directory to which to export the H5M output file (optional, defaults to empty string). """ - self._logger.info('Exporting source mesh H5M file...') + self._logger.info("Exporting source mesh H5M file...") - export_path = Path(export_dir) / Path(filename).with_suffix('.h5m') + export_path = Path(export_dir) / Path(filename).with_suffix(".h5m") self.mbc.write_file(str(export_path)) def parse_args(): - """Parser for running as a script - """ - parser = argparse.ArgumentParser(prog='source_mesh') + """Parser for running as a script""" + parser = argparse.ArgumentParser(prog="source_mesh") parser.add_argument( - 'filename', - help='YAML file defining ParaStell source mesh configuration' + "filename", + help="YAML file defining ParaStell source mesh configuration", ) parser.add_argument( - '-e', '--export_dir', - default='', + "-e", + "--export_dir", + default="", help=( - 'Directory to which output files are exported (default: working ' - 'directory)' + "Directory to which output files are exported (default: working " + "directory)" ), - metavar='' + metavar="", ) parser.add_argument( - '-l', '--logger', + "-l", + "--logger", default=False, help=( - 'Flag to indicate whether to instantiate a logger object (default: ' - 'False)' + "Flag to indicate whether to instantiate a logger object (default: " + "False)" ), - metavar='' + metavar="", ) return parser.parse_args() def generate_source_mesh(): - """Main method when run as a command line script. - """ + """Main method when run as a command line script.""" args = parse_args() all_data = read_yaml_config(args.filename) @@ -480,17 +480,16 @@ def generate_source_mesh(): else: logger = log.NullLogger() - vmec_file = all_data['vmec_file'] + vmec_file = all_data["vmec_file"] vmec_obj = read_vmec.VMECData(vmec_file) - source_mesh_dict = all_data['source_mesh'] + source_mesh_dict = all_data["source_mesh"] source_mesh = SourceMesh( vmec_obj, - source_mesh_dict['mesh_size'], - source_mesh_dict['toroidal_extent'], - logger=logger - **source_mesh_dict + source_mesh_dict["mesh_size"], + source_mesh_dict["toroidal_extent"], + logger=logger**source_mesh_dict, ) source_mesh.create_vertices() @@ -498,7 +497,7 @@ def generate_source_mesh(): source_mesh.export_mesh( export_dir=args.export_dir, - **(filter_kwargs(source_mesh_dict, ['filename'])) + **(filter_kwargs(source_mesh_dict, ["filename"])) ) diff --git a/parastell/utils.py b/parastell/utils.py index b728f0d..fcfed50 100644 --- a/parastell/utils.py +++ b/parastell/utils.py @@ -6,6 +6,7 @@ m2cm = 100 m3tocm3 = m2cm * m2cm * m2cm + def normalize(vec_list): """Normalizes a set of vectors. @@ -44,14 +45,13 @@ def expand_ang_list(ang_list, num_ang): final_ang = ang_list[-1] ang_extent = final_ang - init_ang - ang_diff_avg = ang_extent/(num_ang - 1) + ang_diff_avg = ang_extent / (num_ang - 1) for ang, next_ang in zip(ang_list[:-1], ang_list[1:]): - n_ang = math.ceil((next_ang - ang)/ang_diff_avg) + n_ang = math.ceil((next_ang - ang) / ang_diff_avg) ang_list_exp = np.append( - ang_list_exp, - np.linspace(ang, next_ang, num=n_ang + 1)[:-1] + ang_list_exp, np.linspace(ang, next_ang, num=n_ang + 1)[:-1] ) ang_list_exp = np.append(ang_list_exp, ang_list[-1]) @@ -60,8 +60,7 @@ def expand_ang_list(ang_list, num_ang): def read_yaml_config(filename): - """Read YAML file describing ParaStell configuration and extract all data. - """ + """Read YAML file describing ParaStell configuration and extract all data.""" with open(filename) as yaml_file: all_data = yaml.safe_load(yaml_file) @@ -95,10 +94,10 @@ def filter_kwargs( if all_kwargs and extra_keys: e = ValueError( - f'{extra_keys} not supported keyword argument(s) of ' + f"{extra_keys} not supported keyword argument(s) of " f'"{fn_name}"' ) logger.error(e.args[0]) raise e - + return {name: dict[name] for name in allowed_keys} diff --git a/tests/test_magnet_coils.py b/tests/test_magnet_coils.py index 2aa8a63..99d999a 100644 --- a/tests/test_magnet_coils.py +++ b/tests/test_magnet_coils.py @@ -7,31 +7,28 @@ def remove_files(): - if Path('magnets.step').exists(): - Path.unlink('magnets.step') - if Path('magnet_mesh.exo').exists(): - Path.unlink('magnet_mesh.exo') - if Path('magnet_mesh.h5m').exists(): - Path.unlink('magnet_mesh.h5m') - if Path('stellarator.log').exists(): - Path.unlink('stellarator.log') - if Path('step_export.log').exists(): - Path.unlink('step_export.log') + if Path("magnets.step").exists(): + Path.unlink("magnets.step") + if Path("magnet_mesh.exo").exists(): + Path.unlink("magnet_mesh.exo") + if Path("magnet_mesh.h5m").exists(): + Path.unlink("magnet_mesh.h5m") + if Path("stellarator.log").exists(): + Path.unlink("stellarator.log") + if Path("step_export.log").exists(): + Path.unlink("step_export.log") @pytest.fixture def rect_coil_set(): - - coils_file = Path('files_for_tests') / 'coils.example' - rect_cross_section = ['rectangle', 20, 60] + + coils_file = Path("files_for_tests") / "coils.example" + rect_cross_section = ["rectangle", 20, 60] toroidal_extent = 90.0 sample_mod = 6 - + rect_coil_obj = magnet_coils.MagnetSet( - coils_file, - rect_cross_section, - toroidal_extent, - sample_mod=sample_mod + coils_file, rect_cross_section, toroidal_extent, sample_mod=sample_mod ) return rect_coil_obj @@ -40,25 +37,22 @@ def rect_coil_set(): @pytest.fixture def circ_coil_set(): - coils_file = Path('files_for_tests') / 'coils.example' - circ_cross_section = ['circle', 25] + coils_file = Path("files_for_tests") / "coils.example" + circ_cross_section = ["circle", 25] toroidal_extent = 90.0 sample_mod = 6 circ_coil_obj = magnet_coils.MagnetSet( - coils_file, - circ_cross_section, - toroidal_extent, - sample_mod=sample_mod + coils_file, circ_cross_section, toroidal_extent, sample_mod=sample_mod ) return circ_coil_obj def test_rectangular_magnets(rect_coil_set): - - shape_exp = 'rectangle' - shape_str_exp = 'rectangle width 60 height 20' + + shape_exp = "rectangle" + shape_str_exp = "rectangle width 60 height 20" mag_len_exp = 60 remove_files() @@ -71,12 +65,12 @@ def test_rectangular_magnets(rect_coil_set): def test_circular_magnets(circ_coil_set): - + len_filaments_exp = 2 average_radial_distance_exp = 1068.3010006669892 len_filtered_filaments_exp = 1 - shape_exp = 'circle' - shape_str_exp = 'circle radius 25' + shape_exp = "circle" + shape_str_exp = "circle radius 25" mag_len_exp = 25 len_test_coil_filament_exp = 23 @@ -104,10 +98,10 @@ def test_magnet_exports(circ_coil_set): circ_coil_set.build_magnet_coils() circ_coil_set.export_step() - assert Path('magnets.step').exists() + assert Path("magnets.step").exists() circ_coil_set.mesh_magnets() circ_coil_set.export_mesh() - assert Path('magnet_mesh.h5m').exists() + assert Path("magnet_mesh.h5m").exists() remove_files() diff --git a/tests/test_parastell.py b/tests/test_parastell.py index 75d0f10..90bc734 100644 --- a/tests/test_parastell.py +++ b/tests/test_parastell.py @@ -8,34 +8,34 @@ def remove_files(): - if Path('chamber.step').exists(): - Path.unlink('chamber.step') - if Path('component.step').exists(): - Path.unlink('component.step') - if Path('magnets.step').exists(): - Path.unlink('magnets.step') - if Path('magnet_mesh.exo').exists(): - Path.unlink('magnet_mesh.exo') - if Path('magnet_mesh.h5m').exists(): - Path.unlink('magnet_mesh.h5m') - if Path('dagmc.h5m').exists(): - Path.unlink('dagmc.h5m') - if Path('dagmc.cub5').exists(): - Path.unlink('dagmc.cub5') - if Path('source_mesh.h5m').exists(): - Path.unlink('source_mesh.h5m') - if Path('stellarator.log').exists(): - Path.unlink('stellarator.log') - if Path('step_import.log').exists(): - Path.unlink('step_import.log') - if Path('step_export.log').exists(): - Path.unlink('step_export.log') + if Path("chamber.step").exists(): + Path.unlink("chamber.step") + if Path("component.step").exists(): + Path.unlink("component.step") + if Path("magnets.step").exists(): + Path.unlink("magnets.step") + if Path("magnet_mesh.exo").exists(): + Path.unlink("magnet_mesh.exo") + if Path("magnet_mesh.h5m").exists(): + Path.unlink("magnet_mesh.h5m") + if Path("dagmc.h5m").exists(): + Path.unlink("dagmc.h5m") + if Path("dagmc.cub5").exists(): + Path.unlink("dagmc.cub5") + if Path("source_mesh.h5m").exists(): + Path.unlink("source_mesh.h5m") + if Path("stellarator.log").exists(): + Path.unlink("stellarator.log") + if Path("step_import.log").exists(): + Path.unlink("step_import.log") + if Path("step_export.log").exists(): + Path.unlink("step_export.log") @pytest.fixture def stellarator(): - - vmec_file = Path('files_for_tests') / 'wout_vmec.nc' + + vmec_file = Path("files_for_tests") / "wout_vmec.nc" stellarator_obj = ps.Stellarator(vmec_file) @@ -51,26 +51,27 @@ def test_parastell(stellarator): toroidal_angles = [0.0, 5.0, 10.0, 15.0] poloidal_angles = [0.0, 120.0, 240.0, 360.0] wall_s = 1.08 - component_name_exp = 'component' + component_name_exp = "component" radial_build_dict = { component_name_exp: { - 'thickness_matrix': np.ones( + "thickness_matrix": np.ones( (len(toroidal_angles), len(poloidal_angles)) - )*10 + ) + * 10 } } num_ribs = 11 - + stellarator.construct_invessel_build( toroidal_angles, poloidal_angles, wall_s, radial_build_dict, - num_ribs=num_ribs + num_ribs=num_ribs, ) - chamber_filename_exp = Path('chamber').with_suffix('.step') - component_filename_exp = Path(component_name_exp).with_suffix('.step') + chamber_filename_exp = Path("chamber").with_suffix(".step") + component_filename_exp = Path(component_name_exp).with_suffix(".step") stellarator.export_invessel_build() @@ -78,53 +79,47 @@ def test_parastell(stellarator): assert component_filename_exp.exists() # Magnet Coils - - coils_file = Path('files_for_tests') / 'coils.example' - cross_section = ['circle', 25] + + coils_file = Path("files_for_tests") / "coils.example" + cross_section = ["circle", 25] toroidal_extent = 90.0 sample_mod = 6 stellarator.construct_magnets( - coils_file, - cross_section, - toroidal_extent, - sample_mod=sample_mod + coils_file, cross_section, toroidal_extent, sample_mod=sample_mod ) - step_filename_exp = 'magnets' + step_filename_exp = "magnets" export_mesh = True - mesh_filename_exp = 'magnet_mesh' + mesh_filename_exp = "magnet_mesh" stellarator.export_magnets( step_filename=step_filename_exp, export_mesh=export_mesh, - mesh_filename=mesh_filename_exp + mesh_filename=mesh_filename_exp, ) - assert Path(step_filename_exp).with_suffix('.step').exists() - assert Path(mesh_filename_exp).with_suffix('.h5m').exists() + assert Path(step_filename_exp).with_suffix(".step").exists() + assert Path(mesh_filename_exp).with_suffix(".h5m").exists() mesh_size = (4, 8, 4) toroidal_extent = 15.0 - stellarator.construct_source_mesh( - mesh_size, - toroidal_extent - ) + stellarator.construct_source_mesh(mesh_size, toroidal_extent) - filename_exp = 'source_mesh' + filename_exp = "source_mesh" stellarator.export_source_mesh(filename=filename_exp) - assert Path(filename_exp).with_suffix('.h5m').exists() + assert Path(filename_exp).with_suffix(".h5m").exists() - filename_exp = 'dagmc' + filename_exp = "dagmc" stellarator.build_cubit_model() stellarator.export_dagmc(filename=filename_exp) stellarator.export_cub5(filename=filename_exp) - assert Path(filename_exp).with_suffix('.h5m').exists() - assert Path(filename_exp).with_suffix('.cub5').exists() + assert Path(filename_exp).with_suffix(".h5m").exists() + assert Path(filename_exp).with_suffix(".cub5").exists() remove_files() diff --git a/tests/test_source_mesh.py b/tests/test_source_mesh.py index 5851d4d..ede946a 100644 --- a/tests/test_source_mesh.py +++ b/tests/test_source_mesh.py @@ -9,27 +9,23 @@ def remove_files(): - if Path('source_mesh.h5m').exists(): - Path.unlink('source_mesh.h5m') - if Path('stellarator.log').exists(): - Path.unlink('stellarator.log') + if Path("source_mesh.h5m").exists(): + Path.unlink("source_mesh.h5m") + if Path("stellarator.log").exists(): + Path.unlink("stellarator.log") @pytest.fixture def source_mesh(): - vmec_file = Path('files_for_tests') / 'wout_vmec.nc' + vmec_file = Path("files_for_tests") / "wout_vmec.nc" vmec_obj = read_vmec.VMECData(vmec_file) mesh_size = (4, 8, 4) toroidal_extent = 90.0 - source_mesh_obj = sm.SourceMesh( - vmec_obj, - mesh_size, - toroidal_extent - ) + source_mesh_obj = sm.SourceMesh(vmec_obj, mesh_size, toroidal_extent) return source_mesh_obj @@ -62,7 +58,7 @@ def test_vertices(source_mesh): num_verts_exp = num_phi * ((num_s - 1) * (num_theta - 1) + 1) remove_files() - + source_mesh.create_vertices() assert source_mesh.coords.shape == (num_verts_exp, 3) @@ -80,7 +76,6 @@ def test_export(source_mesh): source_mesh.create_mesh() source_mesh.export_mesh() - assert Path('source_mesh.h5m').exists() + assert Path("source_mesh.h5m").exists() remove_files() - \ No newline at end of file