Coverage for fiqus/geom_generators/GeometryMultipole.py: 98%
261 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
5import json
7from fiqus.utils.Utils import GmshUtils
8from fiqus.utils.Utils import FilesAndFolders as Util
9from fiqus.utils.Utils import GeometricFunctions as Func
10from fiqus.data import DataFiQuS as dF
11from fiqus.data import DataMultipole as dM
14class Geometry:
15 def __init__(self, data: dF.FDM() = None, geom: dF.FiQuSGeometry() = None, sett: dF.FiQuSSettings() = None,
16 geom_folder: str = None, verbose: bool = False):
17 """
18 Class to generate geometry
19 :param data: FiQuS data model
20 :param geom: ROXIE geometry data
21 :param sett: settings data model
22 :param verbose: If True more information is printed in python console.
23 """
24 self.data: dF.FDM() = data
25 self.geom: dF.FiQuSGeometry() = geom
26 self.sett: dF.FiQuSSettings() = sett
27 self.geom_folder = geom_folder
28 self.verbose: bool = verbose
30 self.md = dM.MultipoleData()
32 self.gu = GmshUtils(self.geom_folder, self.verbose)
33 self.gu.initialize()
34 self.occ = gmsh.model.occ
36 self.model_file = f"{os.path.join(self.geom_folder, self.data.general.magnet_name)}.brep"
37 self.iL, self.iR, self.oL, self.oR, self.iLr, self.iRr, self.oLr, self.oRr = [], [], [], [], [], [], [], []
39 def ending_step(self, gui: bool = False):
40 if gui:
41 self.gu.launch_interactive_GUI()
42 else:
43 gmsh.clear()
44 gmsh.finalize()
46 def saveHalfTurnCornerPositions(self):
47 json.dump({'iL': self.iL, 'iR': self.iR, 'oL': self.oL, 'oR': self.oR,
48 'iLr': self.iLr, 'iRr': self.iRr, 'oLr': self.oLr, 'oRr': self.oRr},
49 open(f"{os.path.join(self.geom_folder, self.data.general.magnet_name)}.crns", 'w'))
51 def saveStrandPositions(self):
52 ht_nr = 0
53 parser_x, parser_y, blocks, ht = [], [], [], []
54 for eo in self.geom.Roxie_Data.coil.electrical_order:
55 block = self.geom.Roxie_Data.coil.coils[eo.coil].poles[eo.pole].layers[eo.layer].windings[
56 eo.winding].blocks[eo.block]
57 for halfTurn_nr, halfTurn in block.half_turns.items():
58 ht_nr += 1
59 for strand_group_nr, strand_group in halfTurn.strand_groups.items():
60 for strand_nr, strand in strand_group.strand_positions.items():
61 blocks.append(eo.block)
62 ht.append(ht_nr)
63 parser_x.append(strand.x)
64 parser_y.append(strand.y)
65 json.dump({'x': parser_x, 'y': parser_y, 'block': blocks, 'ht': ht},
66 open(f"{os.path.join(self.geom_folder, self.data.general.magnet_name)}.strs", 'w'))
68 def saveBoundaryRepresentationFile(self):
69 self.occ.synchronize()
70 gmsh.write(self.model_file)
71 gmsh.clear()
73 def loadBoundaryRepresentationFile(self):
74 gmsh.option.setString('Geometry.OCCTargetUnit', 'M') # set units to meters
75 gmsh.open(self.model_file)
77 def saveAuxiliaryFile(self):
78 Util.write_data_to_yaml(f"{os.path.join(self.geom_folder, self.data.general.magnet_name)}.aux", self.md.dict())
80 def constructIronGeometry(self):
81 """
82 Generates points, hyper lines, and curve loops for the iron yoke
83 """
84 iron = self.geom.Roxie_Data.iron
85 self.md.geometries.iron.quadrants = {1: dM.Region(), 2: dM.Region(), 3: dM.Region(), 4: dM.Region()}
86 quadrants = self.md.geometries.iron.quadrants
88 lc = 1e-2
89 for point_name, point in iron.key_points.items():
90 quadrants[1].points[point_name] = self.occ.addPoint(point.x, point.y, 0, lc)
92 if point.x == 0.:
93 quadrants[2].points[point_name] = quadrants[1].points[point_name]
94 else:
95 quadrants[2].points[point_name] = self.occ.copy([(0, quadrants[1].points[point_name])])[0][1]
96 self.occ.mirror([(0, quadrants[2].points[point_name])], 1, 0, 0, 0)
97 if point.y == 0.:
98 quadrants[3].points[point_name] = quadrants[2].points[point_name]
99 quadrants[4].points[point_name] = quadrants[1].points[point_name]
100 else:
101 quadrants[3].points[point_name] = self.occ.copy([(0, quadrants[2].points[point_name])])[0][1]
102 self.occ.mirror([(0, quadrants[3].points[point_name])], 0, 1, 0, 0)
103 quadrants[4].points[point_name] = self.occ.copy([(0, quadrants[1].points[point_name])])[0][1]
104 self.occ.mirror([(0, quadrants[4].points[point_name])], 0, 1, 0, 0)
106 mirror_x = [1, -1, -1, 1]
107 mirror_y = [1, 1, -1, -1]
108 for line_name, line in iron.hyper_lines.items():
109 if line.type == 'line':
110 for quadrant, qq in quadrants.items():
111 if quadrant == 1:
112 qq.lines[line_name] = self.occ.addLine(qq.points[line.kp1], qq.points[line.kp2])
113 elif quadrant == 2:
114 if iron.key_points[line.kp1].x == 0. and iron.key_points[line.kp2].x == 0.:
115 qq.lines[line_name] = quadrants[1].lines[line_name]
116 else:
117 qq.lines[line_name] = self.occ.addLine(qq.points[line.kp1], qq.points[line.kp2])
118 elif quadrant == 3:
119 if iron.key_points[line.kp1].y == 0. and iron.key_points[line.kp2].y == 0.:
120 qq.lines[line_name] = quadrants[2].lines[line_name]
121 else:
122 qq.lines[line_name] = self.occ.addLine(qq.points[line.kp1], qq.points[line.kp2])
123 else:
124 if iron.key_points[line.kp1].y == 0. and iron.key_points[line.kp2].y == 0.:
125 qq.lines[line_name] = quadrants[1].lines[line_name]
126 else:
127 qq.lines[line_name] = self.occ.addLine(qq.points[line.kp1], qq.points[line.kp2])
129 elif line.type == 'arc':
130 center = Func.arc_center_from_3_points(
131 iron.key_points[line.kp1], iron.key_points[line.kp3], iron.key_points[line.kp2])
132 new_point_name = 'kp' + line_name + '_center'
133 quadrants[1].points[new_point_name] = self.occ.addPoint(center[0], center[1], 0)
134 # gmsh.model.setEntityName(0, gm.iron.quadrants[1].points[new_point_name], 'iron_' + new_point_name)
135 if center[0] == 0.:
136 quadrants[2].points[new_point_name] = quadrants[1].points[new_point_name]
137 else:
138 quadrants[2].points[new_point_name] = self.occ.copy([(0, quadrants[1].points[new_point_name])])[0][1]
139 self.occ.mirror([(0, quadrants[2].points[new_point_name])], 1, 0, 0, 0)
140 if center[1] == 0.:
141 quadrants[3].points[new_point_name] = quadrants[2].points[new_point_name]
142 quadrants[4].points[new_point_name] = quadrants[1].points[new_point_name]
143 else:
144 quadrants[3].points[new_point_name] = self.occ.copy([(0, quadrants[2].points[new_point_name])])[0][1]
145 self.occ.mirror([(0, quadrants[3].points[new_point_name])], 0, 1, 0, 0)
146 quadrants[4].points[new_point_name] = self.occ.copy([(0, quadrants[1].points[new_point_name])])[0][1]
147 self.occ.mirror([(0, quadrants[4].points[new_point_name])], 0, 1, 0, 0)
149 for quadrant, qq in quadrants.items():
150 qq.lines[line_name] = self.occ.addCircleArc(
151 qq.points[line.kp1], qq.points[new_point_name], qq.points[line.kp2])
153 elif line.type == 'circle':
154 pt1 = iron.key_points[line.kp1]
155 pt2 = iron.key_points[line.kp2]
156 center = [(pt1.x + pt2.x) / 2, (pt1.y + pt2.y) / 2]
157 radius = (sqrt(square(pt1.x - center[0]) + square(pt1.y - center[1])) +
158 sqrt(square(pt2.x - center[0]) + square(pt2.y - center[1]))) / 2
160 for quadrant, qq in quadrants.items():
161 qq.lines[line_name] = self.occ.addCircle(
162 mirror_x[quadrant - 1] * center[0], mirror_y[quadrant - 1] * center[1], 0, radius)
163 qq.points['kp' + line_name] = len(qq.points) + 1
165 else:
166 raise ValueError('Hyper line {} not supported'.format(line.type))
168 for quadrant, qq in quadrants.items():
169 for area_name, area in iron.hyper_areas.items():
170 qq.areas[area_name] = dM.Area(loop=self.occ.addCurveLoop([qq.lines[line] for line in area.lines]))
171 if (iron.hyper_areas[area_name].material not in self.md.domains.groups_surfaces and
172 iron.hyper_areas[area_name].material != 'BH_air'):
173 self.md.domains.groups_surfaces[iron.hyper_areas[area_name].material] = []
175 def constructWedgeGeometry(self):
176 """
177 Generates points, hyper lines, and curve loops for the wedges
178 """
179 wedges = self.geom.Roxie_Data.wedges
180 for i in wedges:
181 kp0_inner = self.occ.addPoint(wedges[i].corrected_center.inner.x, wedges[i].corrected_center.inner.y, 0)
182 kp0_outer = self.occ.addPoint(wedges[i].corrected_center.outer.x, wedges[i].corrected_center.outer.y, 0)
183 arg = [self.occ.addPoint(wedges[i].corners.iL.x, wedges[i].corners.iL.y, 0),
184 self.occ.addPoint(wedges[i].corners.iR.x, wedges[i].corners.iR.y, 0),
185 self.occ.addPoint(wedges[i].corners.oL.x, wedges[i].corners.oL.y, 0),
186 self.occ.addPoint(wedges[i].corners.oR.x, wedges[i].corners.oR.y, 0)]
188 left = self.occ.addLine(arg[0], arg[2])
189 right = self.occ.addLine(arg[1], arg[3])
190 inner = self.occ.addCircleArc(arg[0], kp0_inner, arg[1])
191 outer = self.occ.addCircleArc(arg[2], kp0_outer, arg[3])
192 self.md.geometries.wedges.areas[i] = dM.Area(loop=self.occ.addCurveLoop([inner, right, outer, left]))
194 def constructCoilGeometry(self):
195 """
196 Generates points, hyper lines, and curve loops for the coil half-turns
197 """
198 frac = 1 # self.frac if self.frac else 1
199 for coil_nr, coil in self.geom.Roxie_Data.coil.coils.items():
200 self.md.geometries.coil.coils[coil_nr] = dM.Pole()
201 coils = self.md.geometries.coil.coils[coil_nr]
202 for pole_nr, pole in coil.poles.items():
203 coils.poles[pole_nr] = dM.Layer()
204 poles = coils.poles[pole_nr]
205 for layer_nr, layer in pole.layers.items():
206 poles.layers[layer_nr] = dM.Winding()
207 layers = poles.layers[layer_nr]
208 for winding_nr, winding in layer.windings.items():
209 layers.windings[winding_nr] = dM.Block(conductor_name=winding.conductor_name,
210 conductors_number=winding.conductors_number)
211 windings = layers.windings[winding_nr]
212 ll = self.sett.Model_Data_GS.conductors[winding.conductor_name].cable.bare_cable_height_mean
213 for block_key, block in winding.blocks.items():
214 windings.blocks[block_key] = dM.BlockData(current_sign=block.current_sign)
215 hts = windings.blocks[block_key].half_turns
217 for halfTurn_nr, halfTurn in block.half_turns.items():
218 ht = str(halfTurn_nr)
219 hts.areas[ht] = dM.Area()
220 hf_current = halfTurn.corners.insulated
221 if halfTurn_nr == 1:
222 bc = block.block_corners
223 if (hf_current.iL.y < 0 and block_key == max(winding.blocks.keys())) or \
224 (hf_current.iL.y > 0 and block_key == min(winding.blocks.keys())):
225 hts.points[ht + 'i'] = self.occ.addPoint(bc.iR.x, bc.iR.y, 0, ll / frac)
226 hts.points[ht + 'o'] = self.occ.addPoint(bc.oR.x, bc.oR.y, 0, ll / frac)
227 else:
228 hts.points[ht + 'i'] = self.occ.addPoint(bc.iL.x, bc.iL.y, 0, ll / frac)
229 hts.points[ht + 'o'] = self.occ.addPoint(bc.oL.x, bc.oL.y, 0, ll / frac)
230 hts.lines[ht + 'r'] = self.occ.addLine(hts.points[ht + 'i'], hts.points[ht + 'o'])
232 if halfTurn_nr == winding.conductors_number:
233 bc = block.block_corners
235 if (hf_current.iL.y < 0 and block_key == max(winding.blocks.keys())) or \
236 (hf_current.iL.y > 0 and block_key == min(winding.blocks.keys())):
237 hts.points['end_i'] = self.occ.addPoint(bc.iL.x, bc.iL.y, 0, ll/frac)
238 hts.points['end_o'] = self.occ.addPoint(bc.oL.x, bc.oL.y, 0, ll/frac)
239 else:
240 hts.points['end_i'] = self.occ.addPoint(bc.iR.x, bc.iR.y, 0, ll/frac)
241 hts.points['end_o'] = self.occ.addPoint(bc.oR.x, bc.oR.y, 0, ll/frac)
243 hts.lines[ht + 'i'] = self.occ.addLine(hts.points['end_i'], hts.points[ht + 'i'])
244 hts.lines[ht + 'o'] = self.occ.addLine(hts.points['end_o'], hts.points[ht + 'o'])
245 hts.lines['end'] = self.occ.addLine(hts.points['end_i'], hts.points['end_o'])
247 # For plotting only
248 if pole_nr == 1:
249 self.occ.synchronize()
250 self.iL.append(list(gmsh.model.getValue(0, hts.points['end_i'], [])[:-1]))
251 self.oL.append(list(gmsh.model.getValue(0, hts.points['end_o'], [])[:-1]))
252 # if block_key == 1 or block_key == 3 or block_key == 5 or block_key == 6:
253 # radius = np.sqrt(np.square(iL[0] - coil.bore_center.x) +
254 # np.square(iL[1] - coil.bore_center.y))
255 # self.ax.add_patch(
256 # patches.Circle((coil.bore_center.x, coil.bore_center.y), radius=radius,
257 # color='b', fill=False))
258 # radius = np.sqrt(np.square(oL[0] - coil.bore_center.x) +
259 # np.square(oL[1] - coil.bore_center.y))
260 # self.ax.add_patch(
261 # patches.Circle((coil.bore_center.x, coil.bore_center.y), radius=radius,
262 # color='c', fill=False))
263 ################
265 left = hts.lines['end']
266 else:
267 hf_next = block.half_turns[halfTurn_nr + 1].corners.insulated
268 next_ht = str(halfTurn_nr + 1)
269 mid_point_i = [(hf_next.iR.x + hf_current.iL.x) / 2,
270 (hf_next.iR.y + hf_current.iL.y) / 2]
271 mid_point_o = [(hf_next.oR.x + hf_current.oL.x) / 2,
272 (hf_next.oR.y + hf_current.oL.y) / 2]
273 hts.points[next_ht + 'i'] = self.occ.addPoint(mid_point_i[0], mid_point_i[1], 0,
274 ll / frac)
275 hts.points[next_ht + 'o'] = self.occ.addPoint(mid_point_o[0], mid_point_o[1], 0,
276 ll / frac)
278 hts.lines[ht + 'i'] = \
279 self.occ.addLine(hts.points[next_ht + 'i'], hts.points[ht + 'i'])
280 hts.lines[ht + 'o'] = \
281 self.occ.addLine(hts.points[next_ht + 'o'], hts.points[ht + 'o'])
282 hts.lines[next_ht + 'r'] = \
283 self.occ.addLine(hts.points[next_ht + 'i'], hts.points[next_ht + 'o'])
284 left = hts.lines[next_ht + 'r']
286 # For plotting only
287 if pole_nr == 1:
288 self.occ.synchronize()
289 self.iL.append(list(gmsh.model.getValue(0, hts.points[next_ht + 'i'], [])[:-1]))
290 self.oL.append(list(gmsh.model.getValue(0, hts.points[next_ht + 'o'], [])[:-1]))
291 if pole_nr == 1:
292 self.iR.append(list(gmsh.model.getValue(0, hts.points[ht + 'i'], [])[:-1]))
293 self.oR.append(list(gmsh.model.getValue(0, hts.points[ht + 'o'], [])[:-1]))
294 hti = halfTurn.corners.insulated
295 self.iLr.append([hti.iL.x, hti.iL.y])
296 self.iRr.append([hti.iR.x, hti.iR.y])
297 self.oLr.append([hti.oL.x, hti.oL.y])
298 self.oRr.append([hti.oR.x, hti.oR.y])
299 ################
301 hts.areas[ht].loop = self.occ.addCurveLoop([hts.lines[ht + 'i'], # inner
302 hts.lines[ht + 'r'], # right
303 hts.lines[ht + 'o'], # outer
304 left]) # left
306 self.saveHalfTurnCornerPositions()
308 def buildDomains(self):
309 """
310 Generates plane surfaces from the curve loops
311 """
312 iron = self.geom.Roxie_Data.iron
313 gm = self.md.geometries
315 for i in iron.key_points:
316 gm.iron.max_radius = max(gm.iron.max_radius, max(iron.key_points[i].x, iron.key_points[i].y))
318 # Create and build air far field
319 radius_in = gm.iron.max_radius * (2.5 if self.data.magnet.geometry.with_iron_yoke else 6)
320 gm.air_inf.lines['inner'] = self.occ.addCircle(0., 0., 0., radius_in)
321 gm.air_inf.areas['inner'] = dM.Area(loop=self.occ.addCurveLoop([gm.air_inf.lines['inner']]))
322 gm.air_inf.areas['inner'].surface = self.occ.addPlaneSurface([self.md.geometries.air_inf.areas['inner'].loop])
323 radius_out = gm.iron.max_radius * (3.2 if self.data.magnet.geometry.with_iron_yoke else 8)
324 gm.air_inf.lines['outer'] = self.occ.addCircle(0., 0., 0., radius_out)
325 gm.air_inf.areas['outer'] = dM.Area(loop=self.occ.addCurveLoop([gm.air_inf.lines['outer']]))
326 gm.air_inf.areas['outer'].surface = self.occ.addPlaneSurface([gm.air_inf.areas['outer'].loop,
327 gm.air_inf.areas['inner'].loop])
328 self.md.domains.groups_surfaces['air_inf'] = [gm.air_inf.areas['outer'].surface]
330 # Build iron yoke domains
331 if self.data.magnet.geometry.with_iron_yoke:
332 for quadrant, qq in gm.iron.quadrants.items():
333 for area_name, area in qq.areas.items():
334 build = True
335 loops = [area.loop]
336 for hole_key, hole in iron.hyper_holes.items():
337 if area_name == hole.areas[1]:
338 loops.append(qq.areas[hole.areas[0]].loop)
339 elif area_name == hole.areas[0]: # or iron.hyper_areas[area_name].material == 'BH_air':
340 build = False
341 if build:
342 area.surface = self.occ.addPlaneSurface(loops)
343 self.md.domains.groups_surfaces[iron.hyper_areas[area_name].material].append(area.surface)
345 # Build coil domains
346 for coil_nr, coil in gm.coil.coils.items():
347 for pole_nr, pole in coil.poles.items():
348 for layer_nr, layer in pole.layers.items():
349 for winding_nr, winding in layer.windings.items():
350 # cable = self.set.Model_Data_GS.conductors[winding.conductor_name].cable
351 # ht_area = cable.bare_cable_height_mean * cable.bare_cable_width
352 for block_key, block in winding.blocks.items():
353 current_sign = '_neg' if block.current_sign < 0 else '_pos'
354 for area_name, area in block.half_turns.areas.items():
355 ht_name = 'block' + str(block_key) + '_ht' + str(area_name) + current_sign
356 # self.conductor_areas[ht_name] = ht_area
357 area.surface = self.occ.addPlaneSurface([area.loop])
358 self.md.domains.groups_surfaces[ht_name] = [area.surface]
360 # Build wedges domains
361 for wedge_nr, wedge in gm.wedges.areas.items():
362 wedge.surface = self.occ.addPlaneSurface([wedge.loop])
363 self.md.domains.groups_surfaces['wedge_' + str(wedge_nr)] = [wedge.surface]
365 def updateTags(self):
366 self.md.geometries.coil.electrical_order = self.geom.Roxie_Data.coil.electrical_order
367 for coil_nr, coil in self.geom.Roxie_Data.coil.coils.items():
368 self.md.geometries.coil.coils[coil_nr].bore_center = coil.bore_center
370 for coil_nr, coil in self.md.geometries.coil.coils.items():
371 for pole_nr, pole in coil.poles.items():
372 for layer_nr, layer in pole.layers.items():
373 for winding_nr, winding in layer.windings.items():
374 for block_key, block in winding.blocks.items():
375 hts = block.half_turns
376 for ht_nr, ht in hts.areas.items():
377 lines_tags = gmsh.model.getAdjacencies(2, ht.surface)[1]
378 first_tag = int(min(lines_tags))
379 hts.lines[ht_nr + ('i' if ht_nr == '1' else 'r')] = first_tag
380 hts.lines[ht_nr + ('r' if ht_nr == '1' else 'i')] = first_tag + 1
381 hts.lines[ht_nr + 'o'] = first_tag + 2
382 if int(ht_nr) == len(hts.areas):
383 hts.lines['end'] = first_tag + 3
385 lines_tags = gmsh.model.getAdjacencies(2, self.md.geometries.air_inf.areas['outer'].surface)[1]
386 self.md.geometries.air_inf.lines['outer'] = int(lines_tags[0])
387 self.md.geometries.air_inf.lines['inner'] = int(lines_tags[1])