fiqus 2026.1.0__py3-none-any.whl → 2026.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fiqus/MainFiQuS.py +1 -8
- fiqus/data/DataConductor.py +4 -8
- fiqus/data/DataFiQuSMultipole.py +358 -167
- fiqus/data/DataModelCommon.py +30 -15
- fiqus/data/DataMultipole.py +33 -10
- fiqus/data/DataWindingsCCT.py +37 -37
- fiqus/data/RegionsModelFiQuS.py +1 -1
- fiqus/geom_generators/GeometryMultipole.py +751 -54
- fiqus/getdp_runners/RunGetdpMultipole.py +181 -31
- fiqus/mains/MainMultipole.py +109 -17
- fiqus/mesh_generators/MeshCCT.py +209 -209
- fiqus/mesh_generators/MeshMultipole.py +938 -263
- fiqus/parsers/ParserCOND.py +2 -1
- fiqus/parsers/ParserDAT.py +16 -16
- fiqus/parsers/ParserGetDPOnSection.py +212 -212
- fiqus/parsers/ParserGetDPTimeTable.py +134 -134
- fiqus/parsers/ParserMSH.py +53 -53
- fiqus/parsers/ParserRES.py +142 -142
- fiqus/plotters/PlotPythonCCT.py +133 -133
- fiqus/plotters/PlotPythonMultipole.py +18 -18
- fiqus/post_processors/PostProcessMultipole.py +16 -6
- fiqus/pre_processors/PreProcessCCT.py +175 -175
- fiqus/pro_assemblers/ProAssembler.py +3 -3
- fiqus/pro_material_functions/ironBHcurves.pro +246 -246
- fiqus/pro_templates/combined/CC_Module.pro +1213 -0
- fiqus/pro_templates/combined/ConductorAC_template.pro +1025 -0
- fiqus/pro_templates/combined/Multipole_template.pro +2738 -1338
- fiqus/pro_templates/combined/TSA_materials.pro +102 -2
- fiqus/pro_templates/combined/materials.pro +54 -3
- fiqus/utils/Utils.py +18 -25
- fiqus/utils/update_data_settings.py +1 -1
- {fiqus-2026.1.0.dist-info → fiqus-2026.1.2.dist-info}/METADATA +64 -68
- {fiqus-2026.1.0.dist-info → fiqus-2026.1.2.dist-info}/RECORD +42 -40
- {fiqus-2026.1.0.dist-info → fiqus-2026.1.2.dist-info}/WHEEL +1 -1
- tests/test_geometry_generators.py +29 -32
- tests/test_mesh_generators.py +35 -34
- tests/test_solvers.py +32 -31
- tests/utils/fiqus_test_classes.py +396 -147
- tests/utils/generate_reference_files_ConductorAC.py +57 -57
- tests/utils/helpers.py +76 -1
- {fiqus-2026.1.0.dist-info → fiqus-2026.1.2.dist-info}/LICENSE.txt +0 -0
- {fiqus-2026.1.0.dist-info → fiqus-2026.1.2.dist-info}/top_level.txt +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import logging
|
|
1
3
|
import os
|
|
2
4
|
import gmsh
|
|
3
5
|
import numpy as np
|
|
@@ -14,6 +16,7 @@ from fiqus.data.DataRoxieParser import Corner
|
|
|
14
16
|
from fiqus.data.DataRoxieParser import Coord
|
|
15
17
|
import re
|
|
16
18
|
|
|
19
|
+
logger = logging.getLogger('FiQuS')
|
|
17
20
|
class Geometry:
|
|
18
21
|
def __init__(self, data: dF.FDM() = None, geom: FiQuSGeometry() = None,
|
|
19
22
|
geom_folder: str = None, verbose: bool = False):
|
|
@@ -25,6 +28,11 @@ class Geometry:
|
|
|
25
28
|
"""
|
|
26
29
|
self.data: dF.FDM() = data
|
|
27
30
|
self.geom: FiQuSGeometry() = geom.Roxie_Data
|
|
31
|
+
|
|
32
|
+
# move cooling holes to a desired position
|
|
33
|
+
if self.data.magnet.solve.thermal.collar_cooling.move_cooling_holes:
|
|
34
|
+
self.geom.iron.key_points = self.move_keypoints(self.geom.iron.key_points, self.data.magnet.solve.thermal.collar_cooling.move_cooling_holes)
|
|
35
|
+
|
|
28
36
|
self.geom_folder = geom_folder
|
|
29
37
|
self.verbose: bool = verbose
|
|
30
38
|
|
|
@@ -40,6 +48,9 @@ class Geometry:
|
|
|
40
48
|
self.ins_wire_lines = {} # for meshed insulation
|
|
41
49
|
self.block_coil_mid_pole_blks = {}
|
|
42
50
|
|
|
51
|
+
self.nc = {'collar': 'c', 'iron_yoke': 'i', 'poles': 'p'}
|
|
52
|
+
self.inv_nc = {v: k for k, v in self.nc.items()} #invert naming convention
|
|
53
|
+
|
|
43
54
|
if self.data.magnet.geometry.electromagnetics.symmetry != 'none':
|
|
44
55
|
self.symmetric_loop_lines = {'x': [], 'y': []}
|
|
45
56
|
self.symmetric_bnds = {'x_p': {'pnts': [], 'line_pnts': []}, 'y_p': {'pnts': [], 'line_pnts': []},
|
|
@@ -54,8 +65,9 @@ class Geometry:
|
|
|
54
65
|
if gui:
|
|
55
66
|
self.gu.launch_interactive_GUI()
|
|
56
67
|
else:
|
|
57
|
-
gmsh.
|
|
58
|
-
|
|
68
|
+
if gmsh.isInitialized():
|
|
69
|
+
gmsh.clear()
|
|
70
|
+
gmsh.finalize()
|
|
59
71
|
|
|
60
72
|
def saveHalfTurnCornerPositions(self):
|
|
61
73
|
self.occ.synchronize()
|
|
@@ -74,8 +86,9 @@ class Geometry:
|
|
|
74
86
|
iL.append([ht.iL.x, ht.iL.y])
|
|
75
87
|
oH.append([ht.oH.x, ht.oH.y])
|
|
76
88
|
oL.append([ht.oL.x, ht.oL.y])
|
|
77
|
-
|
|
78
|
-
|
|
89
|
+
with open(f"{self.model_file}.crns", 'w') as f:
|
|
90
|
+
json.dump({'iH': iH, 'iL': iL, 'oH': oH, 'oL': oL,
|
|
91
|
+
'iHr': iHr, 'iLr': iLr, 'oHr': oHr, 'oLr': oLr}, f)
|
|
79
92
|
|
|
80
93
|
def saveStrandPositions(self, run_type):
|
|
81
94
|
symmetry = self.data.magnet.geometry.electromagnetics.symmetry if run_type == 'EM' else 'none'
|
|
@@ -108,9 +121,10 @@ class Geometry:
|
|
|
108
121
|
subdf = df[(condition[qdr][0] * df['parser_x'] < 0) & (condition[qdr][1] * df['parser_y'] < 0)]
|
|
109
122
|
for strand, x, y in zip(subdf.index, subdf['parser_x'], subdf['parser_y']):
|
|
110
123
|
mirrored[strand] = df[(df['parser_x'] == mrr[0] * x) & (df['parser_y'] == mrr[1] * y)].index.item()
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
124
|
+
with open(f"{self.model_file}_{run_type}.strs", 'w') as f:
|
|
125
|
+
json.dump({'x': parser_x, 'y': parser_y, 'block': blocks, 'ht': ht, 'mirrored': mirrored,
|
|
126
|
+
'pole_1_blocks': pole_blocks, 'poles': len(self.geom.coil.coils[1].poles)},
|
|
127
|
+
f)
|
|
114
128
|
|
|
115
129
|
def saveBoundaryRepresentationFile(self, run_type):
|
|
116
130
|
self.occ.synchronize()
|
|
@@ -206,7 +220,7 @@ class Geometry:
|
|
|
206
220
|
elif 'current' in angles_to_correct:
|
|
207
221
|
if angle_next < np.pi / 2: angle_next += correction_angle
|
|
208
222
|
elif angle_next > np.pi * 3 / 2: angle_next = angle_next + correction_angle - 2 * np.pi
|
|
209
|
-
if abs(angle_curr - angle_next) < 1e-6:
|
|
223
|
+
if abs(angle_curr - angle_next) < 1e-6:
|
|
210
224
|
thin_shell_endpoints[side], angles[side], which_block[side] = pnt_curr, angle_curr, 'current'
|
|
211
225
|
elif angle_curr * (-1 if side == 'lower' else 1) < angle_next * (-1 if side == 'lower' else 1):
|
|
212
226
|
thin_shell_endpoints[side], angles[side], which_block[side] = pnt_curr, angle_curr, 'current'
|
|
@@ -215,11 +229,15 @@ class Geometry:
|
|
|
215
229
|
if angles['higher'] < angles['lower']: return None
|
|
216
230
|
else: return thin_shell_endpoints, which_block
|
|
217
231
|
|
|
218
|
-
def
|
|
232
|
+
def create_geom_dict(self, geometry_setting):
|
|
233
|
+
return {v: k in geometry_setting.areas for k, v in self.nc.items()}
|
|
234
|
+
|
|
235
|
+
def constructIronGeometry(self, symmetry, geometry_setting, run_type):
|
|
219
236
|
"""
|
|
220
237
|
Generates points, hyper lines, and curve loops for the iron yoke
|
|
221
238
|
"""
|
|
222
|
-
iron = self.geom.iron
|
|
239
|
+
iron = self.geom.iron #roxie
|
|
240
|
+
|
|
223
241
|
if symmetry == 'xy':
|
|
224
242
|
self.md.geometries.iron.quadrants = {1: dM.Region()}
|
|
225
243
|
list_bnds = ['x_p', 'y_p']
|
|
@@ -230,12 +248,16 @@ class Geometry:
|
|
|
230
248
|
self.md.geometries.iron.quadrants = {1: dM.Region(), 4: dM.Region()}
|
|
231
249
|
list_bnds = ['y_p', 'y_n']
|
|
232
250
|
else:
|
|
233
|
-
self.md.geometries.
|
|
251
|
+
for k in self.nc.keys(): getattr(self.md.geometries, k).quadrants = {1: dM.Region(), 2: dM.Region(), 4: dM.Region(), 3: dM.Region()}
|
|
234
252
|
list_bnds = []
|
|
235
|
-
quadrants = self.md.geometries.iron.quadrants
|
|
236
253
|
|
|
237
254
|
lc = 1e-2
|
|
255
|
+
geom_dict = self.create_geom_dict(geometry_setting)
|
|
256
|
+
|
|
238
257
|
for point_name, point in iron.key_points.items():
|
|
258
|
+
identifier = next((k for k in self.inv_nc.keys() if re.match(f'^{k}', point_name[2:])), None)
|
|
259
|
+
if not geom_dict.get(identifier, False): continue
|
|
260
|
+
quadrants = getattr(self.md.geometries, self.inv_nc[identifier]).quadrants #re.sub(r'\d+', '', point_name[2:])
|
|
239
261
|
if symmetry in ['x', 'xy']:
|
|
240
262
|
if point.y == 0.:
|
|
241
263
|
self.symmetric_bnds['x_p']['pnts'].append([point_name, point.x])
|
|
@@ -273,6 +295,9 @@ class Geometry:
|
|
|
273
295
|
symmetric_bnds_order = {'x': [], 'y': []}
|
|
274
296
|
sym_lines_tags = {'x_p': [], 'y_p': [], 'x_n': [], 'y_n': []}
|
|
275
297
|
for line_name, line in iron.hyper_lines.items():
|
|
298
|
+
identifier = next((k for k in self.inv_nc.keys() if re.match(f'^{k}', line_name[2:])), None)
|
|
299
|
+
if not geom_dict.get(identifier, False): continue
|
|
300
|
+
quadrants = getattr(self.md.geometries, self.inv_nc[identifier]).quadrants #re.sub(r'\d+', '', line_name[2:])
|
|
276
301
|
pt1 = iron.key_points[line.kp1]
|
|
277
302
|
pt2 = iron.key_points[line.kp2]
|
|
278
303
|
if line.type == 'line':
|
|
@@ -359,7 +384,7 @@ class Geometry:
|
|
|
359
384
|
|
|
360
385
|
for quadrant, qq in quadrants.items():
|
|
361
386
|
qq.lines[line_name] = self.occ.addCircleArc(
|
|
362
|
-
qq.points[line.kp1], qq.points[
|
|
387
|
+
qq.points[line.kp1], qq.points[line.kp3], qq.points[line.kp2], center=False)
|
|
363
388
|
|
|
364
389
|
elif line.type == 'circle':
|
|
365
390
|
center = [(pt1.x + pt2.x) / 2, (pt1.y + pt2.y) / 2]
|
|
@@ -445,6 +470,7 @@ class Geometry:
|
|
|
445
470
|
raise ValueError('Hyper line {} not supported'.format(line.type))
|
|
446
471
|
|
|
447
472
|
if symmetry != 'none':
|
|
473
|
+
quadrants = self.md.geometries.iron_yoke.quadrants
|
|
448
474
|
indexes = {'x_p': 1, 'y_p': 1, 'x_n': 1, 'y_n': 1}
|
|
449
475
|
self.md.geometries.air_inf.points['center'] = self.occ.addPoint(0, 0, 0)
|
|
450
476
|
for sym in list_bnds:
|
|
@@ -479,12 +505,74 @@ class Geometry:
|
|
|
479
505
|
sym_lines_tags['y_p'].reverse()
|
|
480
506
|
self.symmetric_loop_lines['y'] = sym_lines_tags['y_p'] + sym_lines_tags['y_n']
|
|
481
507
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
508
|
+
# add all areas of each quadrant. Useful for brep curves and meshing
|
|
509
|
+
for key in geometry_setting.areas: # only consider areas that are implemented
|
|
510
|
+
quadrants = getattr(self.md.geometries, key).quadrants
|
|
511
|
+
for quadrant, qq in quadrants.items():
|
|
512
|
+
for area_name, area in iron.hyper_areas.items(): ## all areas
|
|
513
|
+
def _add_loop():
|
|
514
|
+
# prevent additional curveloop generation when Enforcing the TSA mapping on the collar
|
|
515
|
+
if (run_type == 'TH'
|
|
516
|
+
and self.data.magnet.mesh.thermal.collar.Enforce_TSA_mapping
|
|
517
|
+
and (area_name.startswith('arc') and not area_name.startswith('arch') or area_name.startswith('arp'))
|
|
518
|
+
): # need to disable the pole area too as it is linked to the same curve
|
|
519
|
+
qq.areas[area_name] = dM.Area() ## initialise area without loop
|
|
520
|
+
else:
|
|
521
|
+
qq.areas[area_name] = dM.Area(
|
|
522
|
+
loop=self.occ.addCurveLoop([qq.lines[line] for line in area.lines]))
|
|
523
|
+
|
|
524
|
+
if iron.hyper_areas[area_name].material not in getattr(self.md.domains.groups_entities, key) and \
|
|
525
|
+
iron.hyper_areas[area_name].material != 'BH_air': ## add the material to the keys
|
|
526
|
+
# for the collar region, it is possible to overwrite the material -> intercept it here
|
|
527
|
+
if key == 'collar' and (self.data.magnet.solve.collar.material != iron.hyper_areas[area_name].material) and self.data.magnet.solve.collar.material is not None:
|
|
528
|
+
logger.warning("Overwriting the collar material for area {} to {} ".format(area_name, self.data.magnet.solve.collar.material))
|
|
529
|
+
iron.hyper_areas[area_name].material = self.data.magnet.solve.collar.material
|
|
530
|
+
getattr(self.md.domains.groups_entities, key)[iron.hyper_areas[area_name].material] = []
|
|
531
|
+
|
|
532
|
+
identifier = next((k for k in geom_dict.keys() if re.match(f'^{k}', area_name[2:])),
|
|
533
|
+
None) # match key from geom_dict to the area name (see naming convention)
|
|
534
|
+
|
|
535
|
+
if key == self.inv_nc.get(identifier, None): # re.sub(r'\d+', '', area_name[2:]),
|
|
536
|
+
_add_loop() # adds arch to collar, because c is in the naming convention of the collar
|
|
537
|
+
elif area_name.startswith('arh') and key == 'iron_yoke': # if not previous but it is a hole, assume iron
|
|
538
|
+
_add_loop()
|
|
539
|
+
|
|
540
|
+
# define inner collar lines
|
|
541
|
+
def define_inner_collar():
|
|
542
|
+
"""
|
|
543
|
+
Defines the inner collar line used for the thermal TSA + for the A projection
|
|
544
|
+
"""
|
|
545
|
+
self.occ.synchronize()
|
|
546
|
+
# only works if the inner collar line is an arc -> just disable 'arc' and calc for all lines
|
|
547
|
+
# alternative method. Find all "arc" lines and then select the closest to the center
|
|
548
|
+
for quad, object in self.md.geometries.collar.quadrants.items():
|
|
549
|
+
arc_line_tags = [object.lines[name] for name in object.lines.keys() if
|
|
550
|
+
self.geom.iron.hyper_lines[name].type == 'arc']
|
|
551
|
+
closest_dist = 1000.
|
|
552
|
+
for tag in arc_line_tags:
|
|
553
|
+
x, y, _ = gmsh.model.getValue(1, tag, [0.5]) # pick one point on the arc
|
|
554
|
+
dist = np.sqrt(x ** 2 + y ** 2)
|
|
555
|
+
if dist < closest_dist:
|
|
556
|
+
closest_dist = dist
|
|
557
|
+
closest_line = tag ## assumes it is only one line per quadrant
|
|
558
|
+
self.md.geometries.collar.inner_boundary_tags[quad] = [closest_line]
|
|
559
|
+
def define_collar_cooling():
|
|
560
|
+
"""
|
|
561
|
+
Defines the cooling holes in the collar
|
|
562
|
+
"""
|
|
563
|
+
self.occ.synchronize()
|
|
564
|
+
line_names = [item for key in self.geom.iron.hyper_areas.keys() if 'ch' in key for item in self.geom.iron.hyper_areas[key].lines]
|
|
565
|
+
# line names are the same in each quadrant. Tags are unique
|
|
566
|
+
for quad, qq in self.md.geometries.collar.quadrants.items():
|
|
567
|
+
self.md.geometries.collar.cooling_tags.extend([qq.lines[line] for line in line_names])
|
|
568
|
+
# these tags are only used to be skipped for enfrocing_TSA_mapping
|
|
569
|
+
|
|
570
|
+
# we need the inner collar lines if we want to do TSA, so no need to define it
|
|
571
|
+
if run_type == 'TH' and self.data.magnet.geometry.thermal.use_TSA_new: define_inner_collar()
|
|
572
|
+
# we only need to specify the air holes if we want cooling OR if we enforce TSA nodes on the collar
|
|
573
|
+
if run_type == 'TH' and (self.data.magnet.solve.thermal.collar_cooling.enabled or self.data.magnet.mesh.thermal.collar.Enforce_TSA_mapping): define_collar_cooling()
|
|
574
|
+
|
|
575
|
+
|
|
488
576
|
|
|
489
577
|
def constructWedgeGeometry(self, use_TSA):
|
|
490
578
|
"""
|
|
@@ -689,6 +777,11 @@ class Geometry:
|
|
|
689
777
|
wedge_reg.lines['l'] = self.occ.addLine(wedge_reg.points['il'], wedge_reg.points['ol'])
|
|
690
778
|
wedge_reg.lines['i'] = self.occ.addCircleArc(wedge_reg.points['ih'], wedge_reg.points['inner_center'], wedge_reg.points['il'])
|
|
691
779
|
wedge_reg.lines['o'] = self.occ.addCircleArc(wedge_reg.points['oh'], wedge_reg.points['outer_center'], wedge_reg.points['ol'])
|
|
780
|
+
"""
|
|
781
|
+
logger.warning("Using straight wedge geometry") # required for the projection
|
|
782
|
+
wedge_reg.lines['i'] = self.occ.addLine(wedge_reg.points['ih'], wedge_reg.points['il'])
|
|
783
|
+
wedge_reg.lines['o'] = self.occ.addLine(wedge_reg.points['oh'], wedge_reg.points['ol'])
|
|
784
|
+
"""
|
|
692
785
|
wedge_reg.areas[str(wedge_nr)] = dM.Area(loop=self.occ.addCurveLoop(
|
|
693
786
|
[wedge_reg.lines['i'], wedge_reg.lines['l'], wedge_reg.lines['o'], wedge_reg.lines['h']]))
|
|
694
787
|
|
|
@@ -1412,9 +1505,49 @@ class Geometry:
|
|
|
1412
1505
|
count_rest = -abs(indexes[0] - indexes[1]) if (case == 'beg' and i == 1) or (case == 'end' and i == 0) else 1 + abs(indexes[0] - indexes[1])
|
|
1413
1506
|
else:
|
|
1414
1507
|
count_rest = -abs(indexes[0] - indexes[1]) if case == 'beg' else 1 + abs(indexes[0] - indexes[1])
|
|
1508
|
+
|
|
1509
|
+
line_name = 'mid_layer_' + mid_l_name + ('b' if i == 1 else '') + (
|
|
1510
|
+
'_l' if case == 'beg' else '_h')
|
|
1511
|
+
|
|
1512
|
+
gmsh.model.occ.synchronize()
|
|
1513
|
+
"""
|
|
1514
|
+
The line that connects two layers may overlap with a new (pole) region. This can be avoided by
|
|
1515
|
+
modifying the line to be an L shape by adding an intermediate point.
|
|
1516
|
+
We add the point along the upper half-turn radial direction towards the center.
|
|
1517
|
+
"""
|
|
1518
|
+
p = ins_pnt[pnt_tag_name] # point to be extended
|
|
1519
|
+
linetag = gmsh.model.getAdjacencies(0, ins_pnt[pnt_tag_name])[0][0] # line to be extended
|
|
1520
|
+
# find the distance to move the point towards the center
|
|
1521
|
+
coord1 = gmsh.model.getValue(0, p, [])
|
|
1522
|
+
coord2 = gmsh.model.getValue(0, ins_pnt_prev[pnt_tag_name_opposite], [])
|
|
1523
|
+
# this is the
|
|
1524
|
+
r1 = np.sqrt(coord1[0] ** 2 + coord1[1] ** 2)
|
|
1525
|
+
r2 = np.sqrt(coord2[0] ** 2 + coord2[1] ** 2)
|
|
1526
|
+
distance = (r1 - r2) / 2
|
|
1527
|
+
|
|
1528
|
+
p1, p2 = [b[1] for b in gmsh.model.getBoundary([(1, linetag)], oriented=True)]
|
|
1529
|
+
dir_vector = gmsh.model.getValue(0, p1, [])-gmsh.model.getValue(0, p2, [])
|
|
1530
|
+
unit_vector = dir_vector / np.linalg.norm(dir_vector)
|
|
1531
|
+
coord = gmsh.model.getValue(0, p, [])
|
|
1532
|
+
X = self.occ.addPoint(
|
|
1533
|
+
coord[0] + unit_vector[0] * distance,
|
|
1534
|
+
coord[1] + unit_vector[1] * distance,
|
|
1535
|
+
0)
|
|
1536
|
+
gmsh.model.occ.synchronize()
|
|
1537
|
+
|
|
1538
|
+
ins_group.lines[line_name + "_A"] = self.occ.addLine(ins_pnt[pnt_tag_name], X)
|
|
1539
|
+
ins_group.lines[line_name + "_B"] = self.occ.addLine(X, ins_pnt_prev[pnt_tag_name_opposite])
|
|
1540
|
+
|
|
1541
|
+
ordered_lines[group_nr].append(
|
|
1542
|
+
[line_name+"_A", count + count_rest -1 if case == 'beg' else count + count_rest, ins_group.lines[line_name+"_A"]])
|
|
1543
|
+
ordered_lines[group_nr].append(
|
|
1544
|
+
[line_name+"_B", count + count_rest if case == 'beg' else count + count_rest -1, ins_group.lines[line_name+"_B"]])
|
|
1545
|
+
|
|
1546
|
+
""" # original code
|
|
1415
1547
|
line_name = 'mid_layer_' + mid_l_name + ('b' if i == 1 else '') + ('_l' if case == 'beg' else '_h')
|
|
1416
1548
|
ins_group.lines[line_name] = self.occ.addLine(ins_pnt[pnt_tag_name], ins_pnt_prev[pnt_tag_name_opposite])
|
|
1417
1549
|
ordered_lines[group_nr].append([line_name, count + count_rest, ins_group.lines[line_name]])
|
|
1550
|
+
"""
|
|
1418
1551
|
break
|
|
1419
1552
|
# Create all edges of the first block sticking out completely todo: might have to be extended to multiple blocks
|
|
1420
1553
|
if current_blk != first_block and current_blk not in past_blocks:
|
|
@@ -1490,11 +1623,430 @@ class Geometry:
|
|
|
1490
1623
|
else:
|
|
1491
1624
|
ins_group.areas[area_name].loop = self.occ.addCurveLoop([ins_group.lines[line] for line in [x[0] for x in ordered_lines[group_nr]]])
|
|
1492
1625
|
|
|
1626
|
+
def constructThinShells_poles(self):
|
|
1627
|
+
ts_layer = self.md.geometries.thin_shells.pole_layers
|
|
1628
|
+
ts_av_ins_thick = self.md.geometries.thin_shells.ins_thickness.poles
|
|
1629
|
+
|
|
1630
|
+
def _construct_thin_shell_corners_to_line(pnt1, pnt2, pole_line, name):
|
|
1631
|
+
# use gmsh to calculate distance to a line
|
|
1632
|
+
coord_a = gmsh.model.getClosestPoint(1, pole_line, coord=(pnt1[0], pnt1[1], 0))[0]
|
|
1633
|
+
coord_b = gmsh.model.getClosestPoint(1, pole_line, coord=(pnt2[0], pnt2[1], 0))[0]
|
|
1634
|
+
# draw new point at half the distance between iH and coord_a
|
|
1635
|
+
new_i = self.occ.addPoint((pnt1[0] + coord_a[0]) / 2, (pnt1[1] + coord_a[1]) / 2, 0)
|
|
1636
|
+
new_o = self.occ.addPoint((pnt2[0] + coord_b[0]) / 2, (pnt2[1] + coord_b[1]) / 2, 0)
|
|
1637
|
+
|
|
1638
|
+
ts_layer[name] = dM.Region()
|
|
1639
|
+
|
|
1640
|
+
self.occ.synchronize()
|
|
1641
|
+
ts_layer[name].lines['1'] = self.occ.addLine(new_i, new_o)
|
|
1642
|
+
self.occ.synchronize()
|
|
1643
|
+
|
|
1644
|
+
cond_name = next(iter(self.data.conductors.keys()))
|
|
1645
|
+
other_material = 0.5*(self.data.conductors[cond_name].cable.th_insulation_along_height+self.data.conductors[cond_name].cable.th_insulation_along_width)#todo, better select which one
|
|
1646
|
+
# distance -> Average between coord_a and iH and coord_b and oH AND remove the G10 thickness
|
|
1647
|
+
ts_av_ins_thick[name] = float(0.5 * (np.sqrt((pnt1[0] - coord_a[0]) ** 2 + (pnt1[1] - coord_a[1]) ** 2) +
|
|
1648
|
+
np.sqrt((pnt2[0] - coord_b[0]) ** 2 + (pnt2[1] - coord_b[1]) ** 2))) - other_material
|
|
1649
|
+
return
|
|
1650
|
+
|
|
1651
|
+
def _find_line_closest_to_points(pnt1, pnt2, line_list_tags):
|
|
1652
|
+
"""
|
|
1653
|
+
Should work for any (pole) geometry. Given the half turn corner points pnt1 = [x1, y1], pnt2 = [x2, y2],
|
|
1654
|
+
and a list of pole line tags, we need to select which one is on the opposite side to construct the thin shell line
|
|
1655
|
+
thus, we search the closest line(s) to each corner point and then take the intersection of those two sets
|
|
1656
|
+
"""
|
|
1657
|
+
closest_lines = {0: [], 1: []}
|
|
1658
|
+
for i, pnt in enumerate([pnt1, pnt2]):
|
|
1659
|
+
min_dist = float('inf')
|
|
1660
|
+
for line_tag in line_list_tags:
|
|
1661
|
+
tag1, tag2 = gmsh.model.getAdjacencies(1, line_tag)[1]
|
|
1662
|
+
start_pnt = gmsh.model.getValue(0, tag1, [])
|
|
1663
|
+
end_pnt = gmsh.model.getValue(0, tag2, [])
|
|
1664
|
+
v = end_pnt - start_pnt
|
|
1665
|
+
w = pnt - start_pnt
|
|
1666
|
+
c1 = np.dot(w, v)
|
|
1667
|
+
c2 = np.dot(v, v)
|
|
1668
|
+
# avoid extending the line
|
|
1669
|
+
if c1 <= 0:
|
|
1670
|
+
dist = np.linalg.norm(pnt - start_pnt) # Closest to p1
|
|
1671
|
+
elif c2 <= c1:
|
|
1672
|
+
dist = np.linalg.norm(pnt - end_pnt) # Closest to p2
|
|
1673
|
+
else:
|
|
1674
|
+
b = c1 / c2
|
|
1675
|
+
Pb = start_pnt + b * v
|
|
1676
|
+
dist = np.linalg.norm(pnt - Pb)
|
|
1677
|
+
|
|
1678
|
+
if np.isclose(min_dist, dist):
|
|
1679
|
+
closest_lines[i].append(line_tag) # add to list
|
|
1680
|
+
elif dist < min_dist:
|
|
1681
|
+
min_dist = dist
|
|
1682
|
+
closest_lines[i] = [line_tag] # overwrite
|
|
1683
|
+
|
|
1684
|
+
# return the intersection of closest lines
|
|
1685
|
+
return list(set(closest_lines[0]).intersection(set(closest_lines[1])))[0] # should be only one line
|
|
1686
|
+
|
|
1687
|
+
def _split_lines_azimuthal_radial(line_tag_list):
|
|
1688
|
+
alines = []
|
|
1689
|
+
rlines = []
|
|
1690
|
+
for tag in line_tag_list:
|
|
1691
|
+
pointTags = gmsh.model.getAdjacencies(1, tag)[1] # get tag
|
|
1692
|
+
p1 = gmsh.model.getValue(0, pointTags[0], [])
|
|
1693
|
+
p2 = gmsh.model.getValue(0, pointTags[1], [])
|
|
1694
|
+
|
|
1695
|
+
dr = np.sqrt(p2[0] ** 2 + p2[1] ** 2) - np.sqrt(p1[0] ** 2 + p1[1] ** 2)
|
|
1696
|
+
dt = (np.arctan2(p2[1], p2[0]) - np.arctan2(p1[1], p1[0])) * np.sqrt(p2[0] ** 2 + p2[1] ** 2) ## convert to length
|
|
1697
|
+
if np.abs(dt)>np.abs(dr):
|
|
1698
|
+
alines.append(tag)
|
|
1699
|
+
else:
|
|
1700
|
+
rlines.append(tag)
|
|
1701
|
+
return alines, rlines
|
|
1702
|
+
|
|
1703
|
+
def _wedge_to_pole_lines():
|
|
1704
|
+
"""
|
|
1705
|
+
These lines do not exist, those that already exist contain the G10 insulation so we need more.
|
|
1706
|
+
"""
|
|
1707
|
+
for _, region in self.md.geometries.thin_shells.mid_layers_aux.items(): #those are the g10 layers, we need to duplicate this line to account for the kapton
|
|
1708
|
+
# obtain the position of the lines and draw more on top of it
|
|
1709
|
+
for name, line_tag in region.lines.items():
|
|
1710
|
+
name = f"p{name}_r" # all radial lines
|
|
1711
|
+
ts_layer[name] = dM.Region()
|
|
1712
|
+
|
|
1713
|
+
point_tag = gmsh.model.getBoundary([(1, line_tag)], oriented=False)
|
|
1714
|
+
p1, p2 = point_tag[0][1], point_tag[1][1]
|
|
1715
|
+
x1, y1, _ = gmsh.model.getValue(0, p1, [])
|
|
1716
|
+
x2, y2, _ = gmsh.model.getValue(0, p2, [])
|
|
1717
|
+
# create new line
|
|
1718
|
+
p1_new = gmsh.model.occ.addPoint(x1, y1, 0.0)
|
|
1719
|
+
p2_new = gmsh.model.occ.addPoint(x2, y2, 0.0)
|
|
1720
|
+
self.occ.synchronize()
|
|
1721
|
+
ts_layer[name].lines['1'] = self.occ.addLine(p1_new, p2_new)
|
|
1722
|
+
self.occ.synchronize()
|
|
1723
|
+
|
|
1724
|
+
# thickness = distance between the wedge and the pole - G10 thickness
|
|
1725
|
+
# how to approximate this thickness? -> use the same thickness as for the ht 108
|
|
1726
|
+
"""
|
|
1727
|
+
cond_name = next(iter(self.data.conductors.keys()))
|
|
1728
|
+
other_material = 0.5 * (
|
|
1729
|
+
self.data.conductors[cond_name].cable.th_insulation_along_height + self.data.conductors[
|
|
1730
|
+
cond_name].cable.th_insulation_along_width)
|
|
1731
|
+
# distance -> Average between coord_a and iH and coord_b and oH AND remove the G10 thickness ?
|
|
1732
|
+
ts_av_ins_thick[name] = ???
|
|
1733
|
+
"""
|
|
1734
|
+
ts_av_ins_thick[name] = ts_av_ins_thick['p108_a'] #@emma hardcoded thickness, use the same as for this ht
|
|
1735
|
+
|
|
1736
|
+
max_layer = max(self.geom.coil.coils[1].poles[1].layers.keys())
|
|
1737
|
+
# first, the lines connecting hts to the pole
|
|
1738
|
+
for coil_nr, coil in self.md.geometries.coil.anticlockwise_order.coils.items(): # coilnr is only 1 here
|
|
1739
|
+
for layer_nr, layer in coil.layers.items(): # we need to add radial TS lines for both layers
|
|
1740
|
+
for nr, blk_order in enumerate(layer):
|
|
1741
|
+
block = self.geom.coil.coils[coil_nr].poles[blk_order.pole].layers[
|
|
1742
|
+
layer_nr].windings[blk_order.winding].blocks[blk_order.block]
|
|
1743
|
+
ht_list = list(self.md.geometries.coil.coils[coil_nr].poles[blk_order.pole].layers[
|
|
1744
|
+
layer_nr].windings[blk_order.winding].blocks[
|
|
1745
|
+
blk_order.block].half_turns.areas.keys())
|
|
1746
|
+
|
|
1747
|
+
blk_index_next = nr + 1 if nr + 1 < len(layer) else 0
|
|
1748
|
+
block_order_next = layer[blk_index_next]
|
|
1749
|
+
block_next = self.geom.coil.coils[coil_nr].poles[block_order_next.pole].layers[
|
|
1750
|
+
layer_nr].windings[block_order_next.winding].blocks[block_order_next.block]
|
|
1751
|
+
ht_list_next = list(self.md.geometries.coil.coils[coil_nr].poles[block_order_next.pole].layers[
|
|
1752
|
+
layer_nr].windings[block_order_next.winding].blocks[
|
|
1753
|
+
block_order_next.block].half_turns.areas.keys())
|
|
1754
|
+
ht_last = int(ht_list[-1])
|
|
1755
|
+
ht_next_first = int(ht_list_next[0])
|
|
1756
|
+
# if winding nr is the same and pole is the same -> pole connection
|
|
1757
|
+
|
|
1758
|
+
if blk_order.pole == block_order_next.pole and blk_order.winding == block_order_next.winding:
|
|
1759
|
+
# Create radial lines for pole connection
|
|
1760
|
+
pole_lines_a, pole_lines_r = _split_lines_azimuthal_radial(
|
|
1761
|
+
[v for qq in self.md.geometries.poles.quadrants.keys() for v in
|
|
1762
|
+
self.md.geometries.poles.quadrants[qq].lines.values()])
|
|
1763
|
+
# todo maybe this can be speed up if we select the correct quadrant
|
|
1764
|
+
if layer_nr != max_layer:
|
|
1765
|
+
# add azimuthal thin shells at the ht side 'o' (normals radial)
|
|
1766
|
+
# Now we consider all the HT's from one block, this might not hold true in general
|
|
1767
|
+
|
|
1768
|
+
for ht in map(int, ht_list):
|
|
1769
|
+
oH = [block.half_turns[ht].corners.bare.oH.x,
|
|
1770
|
+
block.half_turns[ht].corners.bare.oH.y, 0.0]
|
|
1771
|
+
oL = [block.half_turns[ht].corners.bare.oL.x,
|
|
1772
|
+
block.half_turns[ht].corners.bare.oL.y, 0.0]
|
|
1773
|
+
_construct_thin_shell_corners_to_line(
|
|
1774
|
+
oH, oL,
|
|
1775
|
+
_find_line_closest_to_points(oH, oL, pole_lines_a),
|
|
1776
|
+
name=f"p{ht}_r")
|
|
1777
|
+
|
|
1778
|
+
for ht in map(int, ht_list_next):
|
|
1779
|
+
oH = [block_next.half_turns[ht].corners.bare.oH.x,
|
|
1780
|
+
block_next.half_turns[ht].corners.bare.oH.y, 0.0]
|
|
1781
|
+
oL = [block_next.half_turns[ht].corners.bare.oL.x,
|
|
1782
|
+
block_next.half_turns[ht].corners.bare.oL.y, 0.0]
|
|
1783
|
+
_construct_thin_shell_corners_to_line(
|
|
1784
|
+
oH, oL,
|
|
1785
|
+
_find_line_closest_to_points(oH, oL, pole_lines_a),
|
|
1786
|
+
name=f"p{ht}_r")
|
|
1787
|
+
|
|
1788
|
+
iH = [block.half_turns[ht_last].corners.bare.iH.x,
|
|
1789
|
+
block.half_turns[ht_last].corners.bare.iH.y, 0.0]
|
|
1790
|
+
oH = [block.half_turns[ht_last].corners.bare.oH.x,
|
|
1791
|
+
block.half_turns[ht_last].corners.bare.oH.y, 0.0]
|
|
1792
|
+
_construct_thin_shell_corners_to_line(
|
|
1793
|
+
iH, oH,
|
|
1794
|
+
_find_line_closest_to_points(iH, oH, pole_lines_r),
|
|
1795
|
+
name=f"p{ht_last}_a")
|
|
1796
|
+
|
|
1797
|
+
iL = [block_next.half_turns[ht_next_first].corners.bare.iL.x,
|
|
1798
|
+
block_next.half_turns[ht_next_first].corners.bare.iL.y, 0.0]
|
|
1799
|
+
oL = [block_next.half_turns[ht_next_first].corners.bare.oL.x,
|
|
1800
|
+
block_next.half_turns[ht_next_first].corners.bare.oL.y, 0.0]
|
|
1801
|
+
_construct_thin_shell_corners_to_line(
|
|
1802
|
+
iL, oL,
|
|
1803
|
+
_find_line_closest_to_points(iL, oL, pole_lines_r),
|
|
1804
|
+
name=f"p{ht_next_first}_a")
|
|
1805
|
+
# second, the lines connecting wedge to the pole
|
|
1806
|
+
_wedge_to_pole_lines()
|
|
1807
|
+
def constructAdditionalThinShells(self):
|
|
1808
|
+
def _create_lines_ht_alignment(ohx, ohy, olx, oly, ihx, ihy, ilx, ily):
|
|
1809
|
+
def __line_circle_intersection(p1, p2):
|
|
1810
|
+
x1, y1 = p1
|
|
1811
|
+
x2, y2 = p2
|
|
1812
|
+
dx = x2 - x1
|
|
1813
|
+
dy = y2 - y1
|
|
1814
|
+
|
|
1815
|
+
A = dx**2 + dy**2
|
|
1816
|
+
B = 2 * (dx*x1 + dy*y1)
|
|
1817
|
+
C = x1**2 + y1**2 - R**2 # R collar is known from outer scope
|
|
1818
|
+
|
|
1819
|
+
disc = B**2 - 4*A*C
|
|
1820
|
+
if disc < 0:
|
|
1821
|
+
logger.warning(" No intersection between line and circle.")
|
|
1822
|
+
return [] # no intersection
|
|
1823
|
+
else:
|
|
1824
|
+
sqrt_disc = np.sqrt(disc)
|
|
1825
|
+
t1 = (-B + sqrt_disc) / (2*A)
|
|
1826
|
+
t2 = (-B - sqrt_disc) / (2*A)
|
|
1827
|
+
return [(x1 + t1*dx, y1 + t1*dy),
|
|
1828
|
+
(x1 + t2*dx, y1 + t2*dy)]
|
|
1829
|
+
|
|
1830
|
+
mid_bare_o = np.mean([[ohx, ohy], [olx, oly]], axis=0)
|
|
1831
|
+
mid_bare_i = np.mean([[ihx, ihy], [ilx, ily]], axis=0)
|
|
1832
|
+
offsets = np.array([[ohx, ohy], [olx, oly]]) - mid_bare_o
|
|
1833
|
+
quad = 1 if mid_bare_o[0] >= 0 and mid_bare_o[1] >= 0 else (
|
|
1834
|
+
2 if mid_bare_o[0] <= 0 <= mid_bare_o[1] else (3 if mid_bare_o[0] <= 0 and mid_bare_o[1] <= 0 else 4))
|
|
1835
|
+
inters = __line_circle_intersection(mid_bare_o, mid_bare_i)
|
|
1836
|
+
quadrants = {
|
|
1837
|
+
1: lambda x, y: x >= 0 and y >= 0,
|
|
1838
|
+
2: lambda x, y: x <= 0 <= y,
|
|
1839
|
+
3: lambda x, y: x <= 0 and y <= 0,
|
|
1840
|
+
4: lambda x, y: x >= 0 >= y,
|
|
1841
|
+
}
|
|
1842
|
+
check = quadrants[quad]
|
|
1843
|
+
for x, y in inters:
|
|
1844
|
+
if check(x, y): # select the correct intersection based on the quadrant
|
|
1845
|
+
xi, yi = x, y
|
|
1846
|
+
break
|
|
1847
|
+
|
|
1848
|
+
# add a point halfway between (x,y) and (xi, yi)
|
|
1849
|
+
dr = float(np.sqrt((xi - mid_bare_o[0]) ** 2 + (yi - mid_bare_o[1]) ** 2))
|
|
1850
|
+
new_point_coords = [mid_bare_o[0] + 0.5 * (xi - mid_bare_o[0]), mid_bare_o[1] + 0.5 * (yi - mid_bare_o[1])]
|
|
1851
|
+
for offset in offsets:
|
|
1852
|
+
point_tag_final = self.occ.addPoint(new_point_coords[0] + offset[0], new_point_coords[1] + offset[1], 0)
|
|
1853
|
+
self.occ.synchronize()
|
|
1854
|
+
return dr, point_tag_final
|
|
1855
|
+
def _embed_points_to_collar_curve():
|
|
1856
|
+
def __add_point_in_curve(points, curve_tag):
|
|
1857
|
+
return self.occ.fragment([(0, k) for k in points], [(1, curve_tag)], removeObject=True)[0]
|
|
1858
|
+
|
|
1859
|
+
### First, cut the collar line
|
|
1860
|
+
self.occ.synchronize()
|
|
1861
|
+
new_tags = {1: [], 2: [], 3: [], 4: []} # tuples
|
|
1862
|
+
new_point_tags = {1: [], 2: [], 3: [], 4: []} # only tags
|
|
1863
|
+
collar_size = self.data.magnet.mesh.thermal.collar.SizeMin
|
|
1864
|
+
for ts_name, ts in self.md.geometries.thin_shells.collar_layers.items():
|
|
1865
|
+
for name, line in ts.lines.items():
|
|
1866
|
+
coords = gmsh.model.getValue(1, line, [i[0] for i in gmsh.model.getParametrizationBounds(1, line)])
|
|
1867
|
+
t1 = np.arctan2(coords[0], coords[1])
|
|
1868
|
+
t2 = np.arctan2(coords[3], coords[4])
|
|
1869
|
+
quad = t1 // (np.pi / 2) + 1 if t1 > 0 else 4 + t1 // (np.pi / 2) + 1
|
|
1870
|
+
curve_tag = self.md.geometries.collar.inner_boundary_tags[quad][0]
|
|
1871
|
+
start, end = min(t1 % (np.pi / 2), t2 % (np.pi / 2)), max(t1 % (np.pi / 2),
|
|
1872
|
+
t2 % (np.pi / 2)) # ensure start < end
|
|
1873
|
+
tmp_coords = gmsh.model.getValue(1, line, [i[0] for i in gmsh.model.getParametrizationBounds(1, line)])
|
|
1874
|
+
target_size = collar_size
|
|
1875
|
+
elements = max(1, round(Func.points_distance(tmp_coords[:2], tmp_coords[3:-1]) / target_size))+1
|
|
1876
|
+
if elements%2 == 1: elements += 1
|
|
1877
|
+
para_coords = np.linspace(start, end, elements, endpoint=True)
|
|
1878
|
+
|
|
1879
|
+
for u in para_coords:
|
|
1880
|
+
if quad == 1 or quad == 3:
|
|
1881
|
+
u = np.pi / 2 - u ## magic
|
|
1882
|
+
x, y, z = gmsh.model.getValue(1, curve_tag, [u]) # Evaluate point on curve
|
|
1883
|
+
new_point_tags[quad].append(self.occ.addPoint(x, y, z)) # Add point coordinates to the list
|
|
1884
|
+
|
|
1885
|
+
for q in new_point_tags.keys():
|
|
1886
|
+
new_tags[q].extend(__add_point_in_curve(new_point_tags[q], self.md.geometries.collar.inner_boundary_tags[q][0]))
|
|
1887
|
+
# so for occ the old curve no longer exists, but in the objects the tag is still there
|
|
1888
|
+
|
|
1889
|
+
REMOVED_TAGS = [self.md.geometries.collar.inner_boundary_tags[q][0] for q in new_tags.keys()]
|
|
1890
|
+
|
|
1891
|
+
# update boundary tags
|
|
1892
|
+
for quad, taglist in new_tags.items():
|
|
1893
|
+
curvelist = [tag[1] for tag in taglist if tag[0]==1 ] # select the curves only
|
|
1894
|
+
self.md.geometries.collar.inner_boundary_tags[quad] = curvelist # replace
|
|
1895
|
+
for k, new_tag in enumerate(self.md.geometries.collar.inner_boundary_tags[quad]):
|
|
1896
|
+
self.md.geometries.collar.quadrants[quad].lines[str(k)] = new_tag ## adding new lines
|
|
1897
|
+
|
|
1898
|
+
# REDEFINE THE CURVELOOP
|
|
1899
|
+
loop_list = []
|
|
1900
|
+
tmpdict = copy.deepcopy(self.md.geometries.collar.quadrants[quad].lines)
|
|
1901
|
+
for tag_name, tag in tmpdict.items():
|
|
1902
|
+
if tag in self.md.geometries.collar.cooling_tags: # skip holes
|
|
1903
|
+
continue
|
|
1904
|
+
if tag in REMOVED_TAGS:
|
|
1905
|
+
# remove from dictionary
|
|
1906
|
+
del self.md.geometries.collar.quadrants[quad].lines[tag_name]
|
|
1907
|
+
continue
|
|
1908
|
+
loop_list.append(tag)
|
|
1909
|
+
self.occ.synchronize()
|
|
1910
|
+
|
|
1911
|
+
for area in self.md.geometries.collar.quadrants[quad].areas:
|
|
1912
|
+
if area.startswith('arc') and not area.startswith('arch'): #collar region, not the holes
|
|
1913
|
+
self.md.geometries.collar.quadrants[quad].areas[area] = dM.Area(
|
|
1914
|
+
loop=self.occ.addCurveLoop(loop_list))
|
|
1915
|
+
|
|
1916
|
+
# Cut the pole lines
|
|
1917
|
+
"""
|
|
1918
|
+
idea, loop over the thin shell lines, then draw line from the HT corner to the line ends, then intersect it with the pole curve
|
|
1919
|
+
just find intersection and select the shortest one ?
|
|
1920
|
+
|
|
1921
|
+
# not implemented, maybe not necessary
|
|
1922
|
+
"""
|
|
1923
|
+
for quad in [1, 2, 3, 4]:
|
|
1924
|
+
# additionally add the pole area back
|
|
1925
|
+
for area in self.md.geometries.poles.quadrants[quad].areas:
|
|
1926
|
+
if area.startswith('arp'):
|
|
1927
|
+
self.md.geometries.poles.quadrants[quad].areas[area] = dM.Area(
|
|
1928
|
+
loop=self.occ.addCurveLoop(list(self.md.geometries.poles.quadrants[quad].lines.values())))
|
|
1929
|
+
|
|
1930
|
+
self.occ.synchronize()
|
|
1931
|
+
|
|
1932
|
+
# point still needs to be added
|
|
1933
|
+
ts_layer = self.md.geometries.thin_shells.collar_layers
|
|
1934
|
+
ts_av_ins_thick = self.md.geometries.thin_shells.ins_thickness.collar
|
|
1935
|
+
collar_tag_dict = self.md.geometries.collar.inner_boundary_tags
|
|
1936
|
+
enforce_TSA_mapping_collar = self.data.magnet.mesh.thermal.collar.Enforce_TSA_mapping # if True, cut the collar curve into segments, otherwise use the whole curve
|
|
1937
|
+
|
|
1938
|
+
center = self.occ.addPoint(0, 0, 0)
|
|
1939
|
+
collar_x, collar_y, _ = gmsh.model.getValue(1, collar_tag_dict[1][0], [0])
|
|
1940
|
+
R = np.sqrt(collar_x ** 2 + collar_y ** 2) # radius of collar curve
|
|
1941
|
+
|
|
1942
|
+
alignment = ['radial', 'ht'][1] # pick ht alignment
|
|
1943
|
+
|
|
1944
|
+
# COLLAR
|
|
1945
|
+
for pid, pole in self.geom.coil.coils[1].poles.items():
|
|
1946
|
+
layer_num = max(pole.layers.keys()) # outside layer
|
|
1947
|
+
for wid, winding in pole.layers[layer_num].windings.items():
|
|
1948
|
+
for block_idx in winding.blocks.keys():
|
|
1949
|
+
block = winding.blocks[block_idx]
|
|
1950
|
+
ht_nr_area = list(self.md.geometries.coil.coils[1].poles[pid].layers[layer_num].windings[wid].blocks[block_idx].half_turns.areas.keys())
|
|
1951
|
+
i = 0
|
|
1952
|
+
for ht_nr, ht in block.half_turns.items(): #ht_idx is not the same as number
|
|
1953
|
+
ht_old = ht_nr_area[i]
|
|
1954
|
+
i+=1
|
|
1955
|
+
dr = 0.
|
|
1956
|
+
if alignment == 'radial':
|
|
1957
|
+
for (x, y), (x1, y1) in zip([[ht.corners.bare.oH.x, ht.corners.bare.oH.y], [ht.corners.bare.oL.x, ht.corners.bare.oL.y]],
|
|
1958
|
+
[[ht.corners.bare.iH.x, ht.corners.bare.iH.y], [ht.corners.bare.iL.x, ht.corners.bare.iL.y]]): # uses the bare coordinates of the HT
|
|
1959
|
+
t = np.arctan2(y, x)
|
|
1960
|
+
quad = t // (np.pi / 2) + 1 if t > 0 else 4 + t // (np.pi / 2) + 1
|
|
1961
|
+
collar_idx = collar_tag_dict[quad][0] # get the (debug: ONE) collar curve tag for the quadrant
|
|
1962
|
+
|
|
1963
|
+
dr_prev = dr
|
|
1964
|
+
|
|
1965
|
+
dummy = self.occ.addPoint(x, y, 0)
|
|
1966
|
+
dr = self.occ.get_distance(0, dummy, 1, collar_idx)[0]
|
|
1967
|
+
self.occ.synchronize()
|
|
1968
|
+
self.occ.remove([(0, dummy)])
|
|
1969
|
+
point_tag_final = self.occ.addPoint(x + 0.5 * dr * np.cos(t), y + 0.5 * dr * np.sin(t), 0)
|
|
1970
|
+
|
|
1971
|
+
elif alignment == 'ht': # distance to the next half turn (more relevant for coil to coil distances)
|
|
1972
|
+
dr, point_tag_final = _create_lines_ht_alignment(ohx = ht.corners.bare.oH.x, ohy=ht.corners.bare.oH.y,
|
|
1973
|
+
olx = ht.corners.bare.oL.x, oly=ht.corners.bare.oL.y,
|
|
1974
|
+
ihx = ht.corners.bare.iH.x, ihy=ht.corners.bare.iH.y,
|
|
1975
|
+
ilx = ht.corners.bare.iL.x, ily=ht.corners.bare.iL.y)
|
|
1976
|
+
# save the line
|
|
1977
|
+
name = f'{ht_nr}_x'
|
|
1978
|
+
ts_layer[name] = dM.Region()
|
|
1979
|
+
self.occ.synchronize()
|
|
1980
|
+
ts_layer[name].lines['1'] = self.occ.addLine(point_tag_final-1, point_tag_final)
|
|
1981
|
+
cond_name = next(iter(self.data.conductors.keys()))
|
|
1982
|
+
if alignment == 'radial':
|
|
1983
|
+
ts_av_ins_thick[name] = 0.5*(dr+dr_prev) - self.data.conductors[cond_name].cable.th_insulation_along_width # approx thickness, average - insulation
|
|
1984
|
+
elif alignment == 'ht':
|
|
1985
|
+
ts_av_ins_thick[name] = dr - self.data.conductors[cond_name].cable.th_insulation_along_width
|
|
1986
|
+
self.occ.synchronize()
|
|
1987
|
+
|
|
1988
|
+
# WEDGES
|
|
1989
|
+
if self.data.magnet.geometry.thermal.with_wedges:
|
|
1990
|
+
for wedge_idx, wedge in self.md.geometries.wedges.coils[1].layers[max(self.md.geometries.wedges.coils[1].layers.keys())].wedges.items():
|
|
1991
|
+
dr=0.
|
|
1992
|
+
if alignment == 'radial':
|
|
1993
|
+
raise NotImplementedError("Wedge radial alignment not implemented")
|
|
1994
|
+
|
|
1995
|
+
elif alignment == 'ht': # distance to the next half turn (more relevant for coil to coil distances)
|
|
1996
|
+
ohx, ohy, _ = gmsh.model.getValue(0, wedge.points['oh'], [])
|
|
1997
|
+
olx, oly, _ = gmsh.model.getValue(0, wedge.points['ol'], [])
|
|
1998
|
+
ihx, ihy, _ = gmsh.model.getValue(0, wedge.points['ih'], [])
|
|
1999
|
+
ilx, ily, _ = gmsh.model.getValue(0, wedge.points['il'], [])
|
|
2000
|
+
dr, point_tag_final = _create_lines_ht_alignment(ohx=ohx, ohy=ohy, olx=olx, oly=oly,
|
|
2001
|
+
ihx=ihx, ihy=ihy, ilx=ilx, ily=ily)
|
|
2002
|
+
|
|
2003
|
+
# find the smallest thickness
|
|
2004
|
+
dr_b = 0.0
|
|
2005
|
+
for x, y in [[ohx, ohy],
|
|
2006
|
+
[olx, oly]]:
|
|
2007
|
+
dummy = self.occ.addPoint(x, y, 0)
|
|
2008
|
+
dr_a = dr_b
|
|
2009
|
+
t = np.arctan2(y, x)
|
|
2010
|
+
quad = t // (np.pi / 2) + 1 if t > 0 else 4 + t // (np.pi / 2) + 1
|
|
2011
|
+
collar_idx = collar_tag_dict[quad][0]
|
|
2012
|
+
dr_b = self.occ.get_distance(0, dummy, 1, collar_idx)[0]
|
|
2013
|
+
self.occ.remove([(0, dummy)])
|
|
2014
|
+
|
|
2015
|
+
# save line
|
|
2016
|
+
name = f'w{wedge_idx}_x'
|
|
2017
|
+
ts_layer[name] = dM.Region()
|
|
2018
|
+
ts_layer[name].lines['1'] = self.occ.addLine(point_tag_final - 1, point_tag_final) # make index line trivial (no distinction)
|
|
2019
|
+
|
|
2020
|
+
cond_name = next(iter(self.data.conductors.keys()))
|
|
2021
|
+
#### ts_av_ins_thick[name] = dr - self.data.conductors[cond_name].cable.th_insulation_along_width
|
|
2022
|
+
# This overshoots the thickness. The wedges are curved and the center between the corners (straight line) is further away from the collar than the curved wedge boundary.
|
|
2023
|
+
# Maybe one could use gmsh to obtain the distance between the outer curve and the collar, but this should be close to taking the minimum distance of the cornerpoints as both curves are
|
|
2024
|
+
# circle segments of (approximately) two concentric circles around the centre
|
|
2025
|
+
|
|
2026
|
+
logger = logging.getLogger('FiQuS')
|
|
2027
|
+
logger.warning("Using alternative wedge insulation thickness approximation ")
|
|
2028
|
+
ts_av_ins_thick[name] = min(dr_a,dr_b)- self.data.conductors[cond_name].cable.th_insulation_along_width # approx thickness, average - insulation
|
|
2029
|
+
|
|
2030
|
+
# POLES
|
|
2031
|
+
if 'poles' in self.data.magnet.geometry.thermal.areas:
|
|
2032
|
+
# generate additional thin shell lines
|
|
2033
|
+
self.constructThinShells_poles()
|
|
2034
|
+
|
|
2035
|
+
#gmsh.fltk.run() constructed correctly
|
|
2036
|
+
|
|
2037
|
+
if enforce_TSA_mapping_collar:
|
|
2038
|
+
self.occ.synchronize()
|
|
2039
|
+
_embed_points_to_collar_curve() ## both coils and wedges
|
|
2040
|
+
|
|
2041
|
+
self.occ.remove([(0, center)]) # remove center point
|
|
2042
|
+
self.occ.synchronize()
|
|
1493
2043
|
def constructThinShells(self, with_wedges):
|
|
2044
|
+
# default
|
|
1494
2045
|
ins_th = self.md.geometries.thin_shells.ins_thickness
|
|
1495
2046
|
mid_pole_ts = self.md.geometries.thin_shells.mid_poles
|
|
1496
2047
|
mid_winding_ts = self.md.geometries.thin_shells.mid_windings
|
|
1497
2048
|
mid_turn_ts = self.md.geometries.thin_shells.mid_turn_blocks
|
|
2049
|
+
# not default
|
|
1498
2050
|
mid_layer_ts = self.md.geometries.thin_shells.mid_layers_ht_to_ht
|
|
1499
2051
|
mid_layer_ts_aux = self.md.geometries.thin_shells.mid_layers_aux
|
|
1500
2052
|
|
|
@@ -1520,6 +2072,7 @@ class Geometry:
|
|
|
1520
2072
|
oH = [block.half_turns[ht_last].corners.bare.oH.x, block.half_turns[ht_last].corners.bare.oH.y]
|
|
1521
2073
|
oL = [block_next.half_turns[ht_next_first].corners.bare.oL.x, block_next.half_turns[ht_next_first].corners.bare.oL.y]
|
|
1522
2074
|
ts_name = str(blk_order.block) + '_' + str(block_order_next.block)
|
|
2075
|
+
|
|
1523
2076
|
for ts, th, condition in zip([mid_pole_ts, mid_winding_ts], [ins_th.mid_pole, ins_th.mid_winding],
|
|
1524
2077
|
# ['_ly' + str(layer_nr), '_wd' + str(blk_order.winding) + '_wd' + str(block_order_next.winding)],
|
|
1525
2078
|
[self.geom.coil.coils[coil_nr].type == 'cos-theta' and block_order_next.pole != blk_order.pole,
|
|
@@ -1596,7 +2149,18 @@ class Geometry:
|
|
|
1596
2149
|
line_name = pnt_current[:-1] + '_' + ordered_pnts[iter_nr][0][:-1]
|
|
1597
2150
|
else:
|
|
1598
2151
|
line_name = pnt_current[:-1] + '_' + pnt_next[:-1]
|
|
1599
|
-
|
|
2152
|
+
|
|
2153
|
+
# TODO look into why this exception handling is needed for SMC magnet with ESC coils - the following meshing stage does not work
|
|
2154
|
+
try:
|
|
2155
|
+
tag = self.occ.addLine(pnt[2], ordered_pnts[nr + 1][2])
|
|
2156
|
+
ts.mid_layers.lines[line_name] = tag
|
|
2157
|
+
except Exception as e:
|
|
2158
|
+
ts.mid_layers.lines[line_name] = tag # this will be the last tag, i.e. from previously created line
|
|
2159
|
+
x1, y1, z1 = gmsh.model.occ.getBoundingBox(1, pnt[2])[:3]
|
|
2160
|
+
x2, y2, z2 = gmsh.model.occ.getBoundingBox(1, ordered_pnts[nr + 1][2])[:3]
|
|
2161
|
+
distance = np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2 + (z2 - z1) ** 2)
|
|
2162
|
+
logger.info(f"{e} {line_name} between point tags {pnt[2], ordered_pnts[nr + 1][2]} and distance between points {distance}")
|
|
2163
|
+
|
|
1600
2164
|
if ts_name in mid_layer_ts_aux:
|
|
1601
2165
|
aux_pnt = list(mid_layer_ts_aux[ts_name].points.keys())[0]
|
|
1602
2166
|
other_pnt = ordered_pnts[0 if aux_pnt[-1] == 'l' else -1]
|
|
@@ -1736,26 +2300,28 @@ class Geometry:
|
|
|
1736
2300
|
"""
|
|
1737
2301
|
iron = self.geom.iron
|
|
1738
2302
|
gm = self.md.geometries
|
|
1739
|
-
|
|
1740
|
-
else self.data.magnet.geometry.thermal
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
for quadrant, qq in gm.
|
|
2303
|
+
geometry_setting = self.data.magnet.geometry.electromagnetics if run_type == 'EM' \
|
|
2304
|
+
else self.data.magnet.geometry.thermal
|
|
2305
|
+
|
|
2306
|
+
with_wedges = geometry_setting.with_wedges
|
|
2307
|
+
|
|
2308
|
+
inv_nc = {v: k for k, v in self.nc.items()} #invert naming convention
|
|
2309
|
+
for a in geometry_setting.areas: # a in ['iron_yoke', 'collar', ...]:
|
|
2310
|
+
for quadrant, qq in getattr(gm, a).quadrants.items():
|
|
1747
2311
|
for area_name, area in qq.areas.items():
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
2312
|
+
identifier = next((k for k in self.inv_nc.keys() if (k in area_name[2:])), None)#re.sub(r'\d+', '',area_name[2:])
|
|
2313
|
+
if a == inv_nc.get(identifier, None): # ensure it is part of the iron yoke or collar (iron, collar)
|
|
2314
|
+
build = True
|
|
2315
|
+
loops = [area.loop]
|
|
2316
|
+
for hole_key, hole in iron.hyper_holes.items():
|
|
2317
|
+
if area_name == hole.areas[1]:
|
|
2318
|
+
loops.append(qq.areas[hole.areas[0]].loop)
|
|
2319
|
+
elif area_name == hole.areas[0]: #skip holes
|
|
2320
|
+
area.surface = self.occ.addPlaneSurface(loops) # also build the holes. An existing curveloop without area is very annoying
|
|
2321
|
+
build = False
|
|
2322
|
+
if build:
|
|
2323
|
+
area.surface = self.occ.addPlaneSurface(loops)
|
|
2324
|
+
getattr(self.md.domains.groups_entities, a)[iron.hyper_areas[area_name].material].append(area.surface) ## save the material
|
|
1759
2325
|
|
|
1760
2326
|
# Build coil domains
|
|
1761
2327
|
for coil_nr, coil in gm.coil.coils.items():
|
|
@@ -1774,7 +2340,7 @@ class Geometry:
|
|
|
1774
2340
|
wedge.areas[str(wedge_nr)].surface = self.occ.addPlaneSurface([wedge.areas[str(wedge_nr)].loop])
|
|
1775
2341
|
|
|
1776
2342
|
# Build insulation domains
|
|
1777
|
-
if run_type == 'TH' and not
|
|
2343
|
+
if run_type == 'TH' and not geometry_setting.use_TSA:
|
|
1778
2344
|
for coil_nr, coil in gm.insulation.coils.items():
|
|
1779
2345
|
for group_nr, group in coil.group.items():
|
|
1780
2346
|
holes = []
|
|
@@ -1794,10 +2360,10 @@ class Geometry:
|
|
|
1794
2360
|
|
|
1795
2361
|
# Create and build air far field
|
|
1796
2362
|
if run_type == 'EM':
|
|
1797
|
-
if
|
|
2363
|
+
if 'iron_yoke' in geometry_setting.areas:
|
|
1798
2364
|
for i in iron.key_points:
|
|
1799
|
-
gm.
|
|
1800
|
-
greatest_radius = gm.
|
|
2365
|
+
gm.iron_yoke.max_radius = max(gm.iron_yoke.max_radius, max(iron.key_points[i].x, iron.key_points[i].y)) # this also contains other regions, e.g. collar but this has no effect
|
|
2366
|
+
greatest_radius = gm.iron_yoke.max_radius
|
|
1801
2367
|
else: # no iron yoke data available
|
|
1802
2368
|
for coil_nr, coil in self.geom.coil.coils.items():
|
|
1803
2369
|
for pole_nr, pole in coil.poles.items():
|
|
@@ -1807,8 +2373,8 @@ class Geometry:
|
|
|
1807
2373
|
abs(pole.layers[len(pole.layers)].windings[first_winding].blocks[first_block].block_corners.oL.y),
|
|
1808
2374
|
gm.coil.max_radius)
|
|
1809
2375
|
greatest_radius = gm.coil.max_radius
|
|
1810
|
-
radius_in = greatest_radius * (2.5 if
|
|
1811
|
-
radius_out = greatest_radius * (3.2 if
|
|
2376
|
+
radius_in = greatest_radius * (2.5 if 'iron_yoke' in geometry_setting.areas else 6)
|
|
2377
|
+
radius_out = greatest_radius * (3.2 if 'iron_yoke' in geometry_setting.areas else 8)
|
|
1812
2378
|
air_inf_center_x, air_inf_center_y = 0, 0
|
|
1813
2379
|
for coil_nr, coil in self.md.geometries.coil.coils.items():
|
|
1814
2380
|
air_inf_center_x += coil.bore_center.x
|
|
@@ -1859,8 +2425,8 @@ class Geometry:
|
|
|
1859
2425
|
# self.md.domains.groups_entities.air_inf = [gm.air_inf.areas['outer'].surface]
|
|
1860
2426
|
gm.air_inf.areas['inner'].surface = self.occ.addPlaneSurface([gm.air_inf.areas['inner'].loop])
|
|
1861
2427
|
|
|
1862
|
-
|
|
1863
|
-
#
|
|
2428
|
+
self.occ.synchronize()
|
|
2429
|
+
#self.gu.launch_interactive_GUI()
|
|
1864
2430
|
|
|
1865
2431
|
def fragment(self):
|
|
1866
2432
|
"""
|
|
@@ -1869,9 +2435,14 @@ class Geometry:
|
|
|
1869
2435
|
# Collect surfaces to be subtracted by background air
|
|
1870
2436
|
holes = []
|
|
1871
2437
|
|
|
1872
|
-
#
|
|
1873
|
-
|
|
1874
|
-
|
|
2438
|
+
# iron yoke and collar
|
|
2439
|
+
group_keys = self.nc.keys()
|
|
2440
|
+
|
|
2441
|
+
for key in group_keys:
|
|
2442
|
+
group = getattr(self.md.domains.groups_entities, key)
|
|
2443
|
+
for _, surfaces in group.items():
|
|
2444
|
+
holes.extend([(2, s) for s in surfaces])
|
|
2445
|
+
|
|
1875
2446
|
# Coils
|
|
1876
2447
|
for coil_nr, coil in self.md.geometries.coil.coils.items():
|
|
1877
2448
|
for pole_nr, pole in coil.poles.items():
|
|
@@ -1904,7 +2475,6 @@ class Geometry:
|
|
|
1904
2475
|
self.md.domains.groups_entities.air.append(e[1])
|
|
1905
2476
|
|
|
1906
2477
|
def updateTags(self, run_type, symmetry):
|
|
1907
|
-
|
|
1908
2478
|
# Update half turn line tags
|
|
1909
2479
|
for coil_nr, coil in self.md.geometries.coil.coils.items():
|
|
1910
2480
|
for pole_nr, pole in coil.poles.items():
|
|
@@ -1921,7 +2491,108 @@ class Geometry:
|
|
|
1921
2491
|
hts.lines[ht_nr + 'o'] = first_tag + 2
|
|
1922
2492
|
hts.lines[ht_nr + 'h'] = first_tag + 3
|
|
1923
2493
|
|
|
1924
|
-
# Update
|
|
2494
|
+
# Update collar tags
|
|
2495
|
+
if run_type == "TH" and 'collar' in self.data.magnet.geometry.thermal.areas:
|
|
2496
|
+
for quad, old_tags in self.md.geometries.collar.quadrants.items():
|
|
2497
|
+
self.md.geometries.collar.inner_boundary_tags[quad] = [] # reset the inner boundary tags
|
|
2498
|
+
new_tags = []
|
|
2499
|
+
for name, area in self.md.geometries.collar.quadrants[quad].areas.items(): # arcol contains the boundaries of the holes too
|
|
2500
|
+
if not re.match(r"^ar.h", name):
|
|
2501
|
+
# the issue is that you don't know which line is which, e.g. which is the inner collar line
|
|
2502
|
+
new_tags.extend([int(k) for k in gmsh.model.getAdjacencies(2, area.surface)[1]])
|
|
2503
|
+
|
|
2504
|
+
for k, name in enumerate(self.md.geometries.collar.quadrants[quad].lines.keys()):
|
|
2505
|
+
self.md.geometries.collar.quadrants[quad].lines[name] = new_tags[k]
|
|
2506
|
+
|
|
2507
|
+
# Update inner collar tags
|
|
2508
|
+
collar_lines = [self.md.geometries.collar.quadrants[quad].lines[name] for name in self.md.geometries.collar.quadrants[quad].lines.keys()]
|
|
2509
|
+
closest_dist = 1000.
|
|
2510
|
+
closest_lines = []
|
|
2511
|
+
max_dist = 0.0
|
|
2512
|
+
max_lines = []
|
|
2513
|
+
# We assume that the middle of the curve of the collar is the closest one to the centre (0,0)
|
|
2514
|
+
for tag in collar_lines:
|
|
2515
|
+
##x, y, _ = gmsh.model.getValue(1, tag, [0.0]) # pick one point on the line
|
|
2516
|
+
curve_type = gmsh.model.getType(1, tag)
|
|
2517
|
+
if curve_type == 'Line': # find the middle of the line
|
|
2518
|
+
tag1, tag2 = gmsh.model.getAdjacencies(1, tag)[1]
|
|
2519
|
+
x1, y1, z1 = gmsh.model.getValue(0, tag1, [])
|
|
2520
|
+
x2, y2, z2 = gmsh.model.getValue(0, tag2, [])
|
|
2521
|
+
x = 0.5 * (x1 + x2)
|
|
2522
|
+
y = 0.5 * (y1 + y2)
|
|
2523
|
+
# take the average of the end points
|
|
2524
|
+
dist = np.sqrt(x ** 2 + y ** 2)
|
|
2525
|
+
elif curve_type == "Circle": # use any point on the circle (same distance because concentric with origin)
|
|
2526
|
+
x, y, z = gmsh.model.getValue(1, tag, [0.5])
|
|
2527
|
+
dist = np.sqrt(x ** 2 + y ** 2)
|
|
2528
|
+
|
|
2529
|
+
if dist < closest_dist+1e-10:
|
|
2530
|
+
if dist < closest_dist-1e-10: # clear if new min is found
|
|
2531
|
+
closest_dist = dist
|
|
2532
|
+
closest_lines = []
|
|
2533
|
+
closest_lines.append(tag)
|
|
2534
|
+
if dist > max_dist-1e-10:
|
|
2535
|
+
if dist > max_dist+1e-10: # clear if new max is found
|
|
2536
|
+
max_dist = dist
|
|
2537
|
+
max_lines = []
|
|
2538
|
+
max_lines.append(tag)
|
|
2539
|
+
self.md.geometries.collar.inner_boundary_tags[quad] = closest_lines
|
|
2540
|
+
self.md.geometries.collar.outer_boundary_tags[quad] = max_lines
|
|
2541
|
+
|
|
2542
|
+
# outer collar tags, does not work because geom.iron has the old tags and this is not accurate anymore
|
|
2543
|
+
"""
|
|
2544
|
+
outer = [old_tags.lines[name] for name in old_tags.lines.keys() if
|
|
2545
|
+
self.geom.iron.hyper_lines[name].type == 'line']
|
|
2546
|
+
logger.info("outer tags", outer)
|
|
2547
|
+
self.md.geometries.collar.outer_boundary_tags[quad] = outer
|
|
2548
|
+
"""
|
|
2549
|
+
|
|
2550
|
+
# concerning the TSL, it seems impossible to get the tags of the lines, as they are not adjacent to a surface nor (yet) grouped in a physical group
|
|
2551
|
+
# only way to ensure equal tags is by creating them in the same way as gmsh orders the lines. (e.g. all at the start or all at the end would work)
|
|
2552
|
+
# we cannot swap order... soo lets hope that just a shift in numbers is sufficient
|
|
2553
|
+
if run_type == 'TH' and self.data.magnet.geometry.thermal.use_TSA_new and self.data.magnet.mesh.thermal.collar.Enforce_TSA_mapping:
|
|
2554
|
+
shift = len(self.md.geometries.collar.inner_boundary_tags[1])*4 -4
|
|
2555
|
+
|
|
2556
|
+
if 'poles' in self.data.magnet.geometry.thermal.areas:
|
|
2557
|
+
shift -= (1*4) # we shifted too much
|
|
2558
|
+
|
|
2559
|
+
## update TSA collar lines
|
|
2560
|
+
for _, ts in self.md.geometries.thin_shells.collar_layers.items():
|
|
2561
|
+
ts.lines['1'] += shift
|
|
2562
|
+
for _, ts in self.md.geometries.thin_shells.pole_layers.items():
|
|
2563
|
+
ts.lines['1'] += shift
|
|
2564
|
+
|
|
2565
|
+
atts = ['mid_layers_ht_to_ht', 'mid_layers_wdg_to_ht', 'mid_layers_ht_to_wdg', 'mid_layers_wdg_to_wdg', 'mid_poles', 'mid_windings', 'mid_turn_blocks' , 'mid_wedge_turn']
|
|
2566
|
+
for at in atts:
|
|
2567
|
+
for _, ts_region in getattr(self.md.geometries.thin_shells, at).items():
|
|
2568
|
+
try: ts_region = ts_region.mid_layers
|
|
2569
|
+
except AttributeError: pass
|
|
2570
|
+
for key in ts_region.lines.keys():
|
|
2571
|
+
ts_region.lines[key] += shift
|
|
2572
|
+
|
|
2573
|
+
# Update coil cooling tags
|
|
2574
|
+
### no consistent way to get the tags of the cooling lines, so we assume that they are ordered in the same way as gmsh orders the lines
|
|
2575
|
+
if self.data.magnet.solve.thermal.collar_cooling.enabled:
|
|
2576
|
+
tags =[]
|
|
2577
|
+
## if we want all cooling holes
|
|
2578
|
+
if self.data.magnet.solve.thermal.collar_cooling.which == 'all':
|
|
2579
|
+
for quad, region in self.md.geometries.collar.quadrants.items():
|
|
2580
|
+
for name, area in region.areas.items():
|
|
2581
|
+
if re.match(r"^ar.h", name):
|
|
2582
|
+
tags.extend([int(k) for k in gmsh.model.getAdjacencies(2, area.surface)[1]])
|
|
2583
|
+
else:
|
|
2584
|
+
nr_applied_cooling = self.data.magnet.solve.thermal.collar_cooling.which
|
|
2585
|
+
nr = 1
|
|
2586
|
+
for _, quad_data in self.md.geometries.collar.quadrants.items():
|
|
2587
|
+
for name, area in quad_data.areas.items():
|
|
2588
|
+
if re.match(r"^ar.h", name):
|
|
2589
|
+
if nr in nr_applied_cooling:
|
|
2590
|
+
tags.extend([int(k) for k in gmsh.model.getAdjacencies(2, area.surface)[1]])
|
|
2591
|
+
nr += 1
|
|
2592
|
+
self.md.geometries.collar.cooling_tags = tags
|
|
2593
|
+
|
|
2594
|
+
|
|
2595
|
+
# Update insulation line tags
|
|
1925
2596
|
if run_type == 'TH' and not self.data.magnet.geometry.thermal.use_TSA:
|
|
1926
2597
|
pass # todo
|
|
1927
2598
|
|
|
@@ -1984,5 +2655,31 @@ class Geometry:
|
|
|
1984
2655
|
corner_max = [tol, y_coord + tol, tol]
|
|
1985
2656
|
self.md.domains.groups_entities.symmetric_boundaries.y = _get_bnd_lines()
|
|
1986
2657
|
|
|
1987
|
-
|
|
1988
|
-
|
|
2658
|
+
def move_keypoints(self, keypoints, displacement, keypoint_names=None):
|
|
2659
|
+
if keypoint_names is None:
|
|
2660
|
+
keypoint_names = []
|
|
2661
|
+
for name, hole in self.geom.iron.hyper_areas.items():
|
|
2662
|
+
if not 'ch' in name: # ch -> collar hole
|
|
2663
|
+
continue
|
|
2664
|
+
line_names = hole.lines
|
|
2665
|
+
keypoint_names.append(list(set([getattr(self.geom.iron.hyper_lines[line], kp_name)
|
|
2666
|
+
for line in line_names for kp_name in ['kp1', 'kp2', 'kp3']])))
|
|
2667
|
+
if type(displacement) == list:
|
|
2668
|
+
list_displacement = displacement
|
|
2669
|
+
elif str(displacement) == "0":
|
|
2670
|
+
list_displacement = [[0.0, 0.0], [0.0, 0.0]]
|
|
2671
|
+
elif str(displacement) == "1":
|
|
2672
|
+
list_displacement = [[0.004, -0.015], [0.03, -0.025]]
|
|
2673
|
+
elif str(displacement) == "2":
|
|
2674
|
+
list_displacement = [[0.004, -0.015], [0.0, 0.0]]
|
|
2675
|
+
elif str(displacement) == "3":
|
|
2676
|
+
list_displacement = [[-0.035, 0.045], [-0.004, -0.0015]]
|
|
2677
|
+
else:
|
|
2678
|
+
raise ValueError("displacement_type not recognized")
|
|
2679
|
+
for i, hole in enumerate(keypoint_names):
|
|
2680
|
+
for name in hole:
|
|
2681
|
+
if name is None:
|
|
2682
|
+
continue
|
|
2683
|
+
keypoints[name].x += list_displacement[i][0]
|
|
2684
|
+
keypoints[name].y += list_displacement[i][1]
|
|
2685
|
+
return keypoints
|