Coverage for fiqus/geom_generators/GeometryHomogenizedConductor.py: 22%
86 statements
« prev ^ index » next coverage.py v7.4.4, created at 2025-11-29 01:35 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2025-11-29 01:35 +0000
1import os, json, gmsh, logging
3from fiqus.utils.Utils import GmshUtils
4from typing import Tuple
6logger = logging.getLogger('FiQuS')
8class Rectangle:
9 """ A class to represent a rectangular surface in gmsh. """
10 def __init__( self, x_center, y_center, z_center, width, height, name) -> None:
11 self.name = name
13 self.x_center = x_center
14 self.y_center = y_center
15 self.z_center = z_center
16 self.width = width
17 self.height = height
19 self.tag = gmsh.model.occ.addRectangle(x_center-width/2, y_center-height/2, z_center, width, height)
20 self.dimTag: Tuple = (2, self.tag)
23class Circle:
24 """ A class to represent a circle surface in gmsh. """
25 def __init__(self, x_center, y_center, z_center, radius, name) -> None:
26 self.name = name
28 self.x_center = x_center
29 self.y_center = y_center
30 self.z_center = z_center
31 self.radius = radius
33 self.tag = gmsh.model.occ.addDisk(self.x_center, self.y_center, self.z_center, radius, radius)
34 self.dimTag: Tuple = (2, self.tag)
37class HomogenizedConductor:
38 """
39 A class representing the geometry of a 2D cross section containing multiple cables and excitation coils in an isolating medium.
41 :ivar cables: list of surfaces representing the cable cross sections.
42 :vartype cables: list[list[Surface]]
43 :ivar excitation_coils: list of surfaces representing the excitation coils cross sections.
44 :vartype excitation_coils: list[list[Surface]]
45 :ivar Air: Surrounding surface representing the air region.
46 :vartype Air: Surface
47 """
48 def __init__(self, fdm) -> None:
49 """
50 Initializes the HomogenizedConductor object.
51 """
52 self.fdm = fdm
54 self.cables = self.generate_cables()
55 self.excitation_coils = self.generate_excitation_coils()
56 self.air = self.generate_air()
58 def generate_cables(self):
59 """
60 This function generates a list of rectangular surfaces according to the specifications of the cables within the yaml.
62 :return: List of rectangle surfaces
63 """
64 cables = []
65 for idx, cable in enumerate(self.fdm.magnet.geometry.cables):
66 cables.append(Rectangle(cable.center_position[0],cable.center_position[1],cable.center_position[2],
67 cable.width, cable.height, name='Cable'+str(idx+1)))
68 return cables
70 def generate_excitation_coils(self):
71 """
72 This function generates a list of rectangular surfaces according to the specifications of the excitation coils within the yaml.
74 :return: List of rectangle surfaces
75 """
76 excitation_coils = []
77 for idx, excitation_coil in enumerate(self.fdm.magnet.geometry.excitation_coils):
78 excitation_coils.append(Rectangle(excitation_coil.center_position[0],excitation_coil.center_position[1],excitation_coil.center_position[2],
79 excitation_coil.width, excitation_coil.height, name='Coil'+str(idx+1)))
80 return excitation_coils
82 def generate_air(self):
83 """
84 This function generates the surrounding air surface by fragmenting a base surface with the previously defined cable surfaces.
86 :raises ValueError: 'circle' is the only defined air surface form
87 :return: fragmented air surface
88 """
89 if self.fdm.magnet.geometry.air_form == 'circle':
90 air_pos = self.fdm.magnet.geometry.air.center_position
91 air = Circle(air_pos[0],air_pos[1],air_pos[2],
92 self.fdm.magnet.geometry.air.radius, name='Air')
93 else:
94 raise ValueError('Undefined air_form.')
95 # fragment air with cables and excitation coils
96 gmsh.model.occ.synchronize()
97 outDimTags, outDimTagsMap = gmsh.model.occ.fragment([(2, air.tag)], [cable.dimTag for cable in self.cables] + [excitation_coil.dimTag for excitation_coil in self.excitation_coils])
98 return air
100 def get_vi_dictionary(self):
101 """
102 This function dumps the dimTags of all surfaces into one dictionary, which can be used to generate the volume information file.
104 :return: dictionary with volume information
105 """
106 vi_dictionary = {}
107 # add all cables
108 for cable in self.cables:
109 vi_dictionary.update({str(cable.name):list(cable.dimTag)})
110 # add all excitation coils
111 for excitation_coil in self.excitation_coils:
112 vi_dictionary.update({str(excitation_coil.name):list(excitation_coil.dimTag)})
113 # add air
114 vi_dictionary.update({str(self.air.name):list(self.air.dimTag)})
115 return vi_dictionary
118class Geometry:
119 def __init__(self, fdm, inputs_folder_path, verbose=True) -> None:
120 """
121 Initializes the Geometry class.
123 :param fdm: The fiqus data model.
124 :type fdm: object
125 :param inputs_folder_path: The full path to the folder with input files, i.e., conductor and STEP files.
126 :type inputs_folder_path: str
127 :param verbose: If True, more information is printed in the Python console. Defaults to True.
128 :type verbose: bool, optional
129 """
130 self.fdm = fdm
131 self.inputs_folder = inputs_folder_path
132 self.geom_folder = os.path.join(os.getcwd())
133 self.geom_file = os.path.join(self.geom_folder, f'{fdm.general.magnet_name}.brep')
134 self.vi_file = os.path.join(self.geom_folder, f'{fdm.general.magnet_name}.vi')
135 self.verbose = verbose
136 # start GMSH
137 self.gu = GmshUtils(self.geom_folder, self.verbose)
138 self.gu.initialize(verbosity_Gmsh=fdm.run.verbosity_Gmsh)
139 # To see the surfaces in a better way in GUI:
140 gmsh.option.setNumber("Geometry.SurfaceType", 2)
142 def generate_geometry(self):
143 """
144 Generates the geometry of conductors within a surrounding air region as specified in the yaml.
146 :param gui: If True, launches an interactive GUI after generating the geometry. Default is False.
147 :type gui: bool, optional
148 :return: None
149 """
150 logger.info("Generating geometry")
152 # 1) Either load the geometry from a yaml file or create the model from scratch
153 self.geometry = HomogenizedConductor(fdm=self.fdm)
155 gmsh.model.occ.synchronize()
156 logger.info("Writing geometry")
157 gmsh.write(self.geom_file) # Write the geometry to a .brep file
159 def load_geometry(self):
160 """ Loads geometry from .brep file. """
161 logger.info("Loading geometry")
163 gmsh.clear()
164 gmsh.model.occ.importShapes(self.geom_file, format="brep")
165 gmsh.model.occ.synchronize()
167 def generate_vi_file(self, gui=False):
168 """
169 Generates volume information file. Volume information file stores dimTags of all
170 the stored volumes. Since this model is 2D, those volumes are equivalent to simple surfaces.
171 """
173 dimTagsDict = self.geometry.get_vi_dictionary()
174 with open(self.vi_file, 'w') as f:
175 json.dump(dimTagsDict, f)
177 if gui:
178 #self.generate_physical_groups()
179 self.gu.launch_interactive_GUI()
180 else:
181 if gmsh.isInitialized():
182 gmsh.clear()
183 gmsh.finalize()