Coverage for fiqus/mesh_generators/MeshMultipole.py: 90%
184 statements
« prev ^ index » next coverage.py v6.4.4, created at 2024-05-20 03:24 +0200
« prev ^ index » next coverage.py v6.4.4, created at 2024-05-20 03:24 +0200
1import os
2import gmsh
3from numpy import square as square
4from numpy import sqrt as sqrt
6from fiqus.utils.Utils import GmshUtils
7from fiqus.utils.Utils import FilesAndFolders as Util
8from fiqus.data import DataFiQuS as dF
9from fiqus.data import DataMultipole as dM
10from fiqus.data.RegionsModelFiQuS import RegionsModel as rM
13class Mesh:
14 def __init__(self, data: dF.FDM() = None, sett: dF.FiQuSSettings() = None, mesh_folder: str = None,
15 verbose: bool = False):
16 """
17 Class to generate mesh
18 :param data: FiQuS data model
19 :param sett: settings data model
20 :param verbose: If True more information is printed in python console.
21 """
22 self.data: dF.FDM() = data
23 self.set: dF.FiQuSSettings() = sett
24 self.mesh_folder = mesh_folder
25 self.verbose: bool = verbose
27 self.md = dM.MultipoleData()
28 self.rm = rM()
30 self.gu = GmshUtils(self.mesh_folder, self.verbose)
31 self.gu.initialize()
32 self.occ = gmsh.model.occ
33 self.mesh = gmsh.model.mesh
35 self.brep_iron_curves = {1: set(), 2: set(), 3: set(), 4: set()}
36 self.mesh_parameters = dict.fromkeys(['SJ', 'SICN', 'SIGE', 'Gamma', 'nodes'])
37 self.geom_folder = os.path.dirname(self.mesh_folder)
38 self.model_file = f"{os.path.join(self.mesh_folder, self.data.general.magnet_name)}.msh"
40 self.colors = {'wedges': [86, 180, 233], # sky blue
41 'half_turns_pos': [213, 94, 0], # vermilion
42 'half_turns_neg': [255, 136, 42], # light vermilion
43 'air': [240, 228, 66], # yellow
44 'air_inf': [220, 208, 46], # dark yellow
45 # yoke
46 'BHiron1': [0, 114, 178], # blue
47 'BHiron2': [0, 158, 115], # bluish green
48 'BHiron4': [86, 180, 233], # sky blue
49 # key
50 'BHiron3': [220, 208, 46], # dark yellow
51 # [230, 159, 0], # orange
52 # collar
53 'BHiron5': [204, 121, 167], # hopbush
54 'BHiron6': [0, 114, 178], # blue
55 'BHiron7': [204, 121, 167]} # reddish purple
57 def ending_step(self, gui: bool = False):
58 if gui:
59 self.gu.launch_interactive_GUI()
60 else:
61 gmsh.clear()
62 gmsh.finalize()
64 def loadAuxiliaryFile(self):
65 self.md = Util.read_data_from_yaml(f"{os.path.join(self.geom_folder, self.data.general.magnet_name)}.aux", dM.MultipoleData)
67 def updateAuxiliaryFile(self):
68 md2 = Util.read_data_from_yaml(f"{os.path.join(self.geom_folder, self.data.general.magnet_name)}.aux", dM.MultipoleData)
69 md2.domains.physical_groups = self.md.domains.physical_groups
70 Util.write_data_to_yaml(f"{os.path.join(self.geom_folder, self.data.general.magnet_name)}.aux", md2.dict())
72 def saveMeshFile(self):
73 gmsh.write(self.model_file)
75 def saveRegionFile(self):
76 Util.write_data_to_yaml(f"{os.path.join(self.mesh_folder, self.data.general.magnet_name)}.reg", self.rm.dict())
78 def getIronCurvesTags(self):
79 if self.data.magnet.geometry.with_iron_yoke:
80 for quadrant, qq in self.md.geometries.iron.quadrants.items():
81 for area_name, area in qq.areas.items():
82 if area.surface:
83 self.brep_iron_curves[quadrant] |= set(gmsh.model.getAdjacencies(2, area.surface)[1])
85 def defineMesh(self):
86 curves_list = []
87 for coil_nr, coil in self.md.geometries.coil.coils.items():
88 for pole_nr, pole in coil.poles.items():
89 for layer_nr, layer in pole.layers.items():
90 for winding_nr, winding in layer.windings.items():
91 for block_key, block in winding.blocks.items():
92 hts = block.half_turns
93 for i in range(len(hts.areas) - 1):
94 curves_list.append(self.occ.copy([(1, hts.lines[str(i + 1) + 'r'])])[0][1])
95 curves_list.append(self.occ.copy([(1, hts.lines['end'])])[0][1])
97 self.occ.synchronize()
98 if not self.data.magnet.mesh.default_mesh:
99 curve_list_iron = []
100 if self.data.magnet.geometry.with_iron_yoke:
101 # if self.from_brep:
102 for quadrant, qq in self.brep_iron_curves.items():
103 for line in qq:
104 curve_list_iron.append(self.occ.copy([(1, line)])[0][1])
105 # else:
106 # for quadrant, qq in self.md.geometry.iron.quadrants.items():
107 # for line_name, line in qq.lines.items():
108 # curve_list_iron.append(self.occ.copy([(1, line)])[0][1])
110 size_min = self.data.magnet.mesh.mesh_coil.SizeMin
111 size_max = self.data.magnet.mesh.mesh_coil.SizeMax
112 dist_min = self.data.magnet.mesh.mesh_coil.DistMin
113 dist_max = self.data.magnet.mesh.mesh_coil.DistMax # iron max_radius
115 list_aux_type = "CurvesList"
116 list_aux = curve_list_iron
117 size_min_aux = self.data.magnet.mesh.mesh_iron.SizeMin
118 size_max_aux = self.data.magnet.mesh.mesh_iron.SizeMax
119 dist_min_aux = self.data.magnet.mesh.mesh_iron.DistMin
120 dist_max_aux = self.data.magnet.mesh.mesh_iron.DistMax
122 else:
123 min_height = 1.
124 for name, cond in self.set.Model_Data_GS.conductors.items():
125 min_height = min(min_height, cond.cable.bare_cable_height_mean)
127 bore_centers = [self.occ.addPoint(coil.bore_center.x, coil.bore_center.y, 0.)
128 for coil_nr, coil in self.md.geometries.coil.coils.items()]
129 point = gmsh.model.getValue(0, self.md.geometries.coil.coils[1].poles[1].layers[1].windings[1].blocks[
130 1].half_turns.points['1i'], [])
131 min_dist = sqrt(square(point[0] - self.md.geometries.coil.coils[1].bore_center.x) +
132 square(point[1] - self.md.geometries.coil.coils[1].bore_center.y))
134 size_min = min_height / 2
135 size_max = min_height * 20
136 dist_min = min_height * 2
137 dist_max = self.md.geometries.iron.max_radius
139 list_aux_type = "PointsList"
140 list_aux = bore_centers
141 size_min_aux = min_height / 2
142 size_max_aux = min_height * 20
143 dist_min_aux = min_dist
144 dist_max_aux = min_dist
146 self.occ.synchronize()
148 distance_coil = self.mesh.field.add("Distance")
149 self.mesh.field.setNumbers(distance_coil, "CurvesList", curves_list)
150 self.mesh.field.setNumber(distance_coil, "Sampling", 100)
152 threshold = self.mesh.field.add("Threshold")
153 self.mesh.field.setNumber(threshold, "InField", distance_coil)
154 self.mesh.field.setNumber(threshold, "SizeMin", size_min)
155 self.mesh.field.setNumber(threshold, "SizeMax", size_max)
156 self.mesh.field.setNumber(threshold, "DistMin", dist_min)
157 self.mesh.field.setNumber(threshold, "DistMax", dist_max)
159 distance_aux = self.mesh.field.add("Distance")
160 self.mesh.field.setNumbers(distance_aux, list_aux_type, list_aux)
161 self.mesh.field.setNumber(distance_aux, "Sampling", 100)
163 threshold_aux = self.mesh.field.add("Threshold")
164 self.mesh.field.setNumber(threshold_aux, "InField", distance_aux)
165 self.mesh.field.setNumber(threshold_aux, "SizeMin", size_min_aux)
166 self.mesh.field.setNumber(threshold_aux, "SizeMax", size_max_aux)
167 self.mesh.field.setNumber(threshold_aux, "DistMin", dist_min_aux)
168 self.mesh.field.setNumber(threshold_aux, "DistMax", dist_max_aux)
170 background = self.mesh.field.add("Min")
171 self.mesh.field.setNumbers(background, "FieldsList", [threshold, threshold_aux] if (
172 self.data.magnet.geometry.with_iron_yoke and not self.data.magnet.mesh.default_mesh
173 or self.data.magnet.mesh.default_mesh) else [threshold])
174 self.mesh.field.setAsBackgroundMesh(background)
176 def fragment(self):
177 """
178 Fragment and group air domains
179 """
180 holes = []
181 for group_name, surfaces in self.md.domains.groups_surfaces.items():
182 if group_name != 'air_inf':
183 holes.extend([(2, s) for s in surfaces])
184 fragmented = self.occ.fragment([(2, self.md.geometries.air_inf.areas['inner'].surface)], holes)[1]
185 self.md.domains.groups_surfaces['air'] = []
186 existing_domains = [e[0][1] for e in fragmented[1:]]
187 for e in fragmented[0]:
188 if e[1] not in existing_domains:
189 self.md.domains.groups_surfaces['air'].append(e[1])
191 self.occ.synchronize()
193 def createPhysicalGroups(self):
194 """
195 Creates physical groups by grouping the mirrored entities according to the Roxie domains
196 """
197 pg = self.md.domains.physical_groups
198 for group_name, surfaces in self.md.domains.groups_surfaces.items():
199 pg.surfaces[group_name] = gmsh.model.addPhysicalGroup(2, surfaces)
200 gmsh.model.setPhysicalName(2, pg.surfaces[group_name], group_name)
201 if group_name[:5] == 'block':
202 color = self.colors['half_turns_pos'] if group_name[-3:] == 'pos' else self.colors['half_turns_neg']
203 elif group_name[:5] == 'wedge':
204 color = self.colors['wedges']
205 else:
206 color = self.colors[group_name]
207 gmsh.model.setColor([(2, i) for i in surfaces], color[0], color[1], color[2])
209 pg.curves['air_inf'] = gmsh.model.addPhysicalGroup(1, [self.md.geometries.air_inf.lines['outer']])
210 gmsh.model.setPhysicalName(1, pg.curves['air_inf'], 'air_inf')
212 def assignRegionsTags(self):
213 self.rm.air_far_field.vol.radius_out = float(gmsh.model.getValue(0, gmsh.model.getAdjacencies(
214 1, self.md.geometries.air_inf.lines['outer'])[1][0], []).max())
215 self.rm.air_far_field.vol.radius_in = float(gmsh.model.getValue(0, gmsh.model.getAdjacencies(
216 1, self.md.geometries.air_inf.lines['inner'])[1][0], []).max())
218 self.rm.air.vol.name = "Air"
219 self.rm.air.vol.number = self.md.domains.physical_groups.surfaces['air']
220 self.rm.air_far_field.vol.names = ["AirInf"]
221 self.rm.air_far_field.vol.numbers = [self.md.domains.physical_groups.surfaces['air_inf']]
222 self.rm.powered.vol.names = []
223 self.rm.powered.vol.numbers = []
224 self.rm.iron.vol.names = []
225 self.rm.iron.vol.numbers = []
226 self.rm.induced.vol.names = []
227 self.rm.induced.vol.numbers = []
228 for group_name, surface in self.md.domains.physical_groups.surfaces.items():
229 if group_name[:5] == 'block':
230 self.rm.powered.vol.names.append(group_name)
231 self.rm.powered.vol.numbers.append(surface)
232 # self.fd.half_turns.cross_sections.append(self.conductor_areas[name])
233 elif group_name[:2] == 'BH':
234 self.rm.iron.vol.names.append(group_name)
235 self.rm.iron.vol.numbers.append(surface)
236 elif group_name[:5] == 'wedge':
237 self.rm.induced.vol.names.append(group_name)
238 self.rm.induced.vol.numbers.append(surface)
240 self.rm.air_far_field.surf.name = "Surface_Inf"
241 self.rm.air_far_field.surf.number = self.md.domains.physical_groups.curves['air_inf']
243 def setMeshOptions(self):
244 """
245 Meshes the generated domain
246 """
247 gmsh.option.setNumber("Mesh.MeshSizeExtendFromBoundary", 0)
248 gmsh.option.setNumber("Mesh.MeshSizeFromPoints", 0)
249 gmsh.option.setNumber("Mesh.MeshSizeFromCurvature", 0)
250 if not self.data.magnet.mesh.default_mesh:
251 # gmsh.option.setNumber("Mesh.AngleToleranceFacetOverlap", self.data.one_lab.mesh.AngleToleranceFacetOverlap)
252 # gmsh.option.setNumber("Mesh.MeshSizeFromCurvature", self.data.one_lab.mesh.MeshSizeFromCurvature)
253 # gmsh.option.setNumber("Mesh.MeshSizeMin", self.data.one_lab.mesh.MeshSizeMin)
254 # gmsh.option.setNumber("Mesh.MeshSizeMax", self.data.one_lab.mesh.MeshSizeMax)
255 gmsh.option.setNumber("Mesh.Algorithm", self.data.magnet.mesh.Algorithm)
256 gmsh.option.setNumber("Mesh.Optimize", self.data.magnet.mesh.Optimize)
257 gmsh.option.setNumber("Mesh.ElementOrder", self.data.magnet.mesh.ElementOrder)
258 else:
259 gmsh.option.setNumber("Mesh.Algorithm", 6)
260 gmsh.option.setNumber("Mesh.Optimize", 1)
261 gmsh.option.setNumber("Mesh.ElementOrder", 2)
263 def generateMesh(self):
264 self.mesh.generate(2)
265 self.mesh.removeDuplicateNodes()
267 def checkMeshQuality(self):
268 tags = self.mesh.getElements(2)[1][0]
270 self.mesh_parameters['SJ'] = min(self.mesh.getElementQualities(elementTags=tags, qualityName='minSJ'))
271 self.mesh_parameters['SICN'] = min(self.mesh.getElementQualities(elementTags=tags, qualityName='minSICN'))
272 self.mesh_parameters['SIGE'] = min(self.mesh.getElementQualities(elementTags=tags, qualityName='minSIGE'))
273 self.mesh_parameters['Gamma'] = min(self.mesh.getElementQualities(elementTags=tags, qualityName='gamma'))
274 self.mesh_parameters['nodes'] = len(self.mesh.getNodes()[0])
276 # gmsh.plugin.setNumber("AnalyseMeshQuality", "JacobianDeterminant", 1)
277 # gmsh.plugin.setNumber("AnalyseMeshQuality", "CreateView", 100)
278 # test = gmsh.plugin.run("AnalyseMeshQuality")
279 # test2 = gmsh.view.getModelData(test, test)
281 # gmsh.logger.getLastError()
282 # gmsh.logger.get()