fiqus 2024.6.0__py3-none-any.whl → 2024.12.0__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.
Files changed (69) hide show
  1. fiqus/MainFiQuS.py +290 -134
  2. fiqus/data/DataConductor.py +301 -301
  3. fiqus/data/DataFiQuS.py +128 -84
  4. fiqus/data/DataFiQuSCCT.py +150 -150
  5. fiqus/data/DataFiQuSConductor.py +84 -84
  6. fiqus/data/DataFiQuSConductorAC_Strand.py +565 -565
  7. fiqus/data/DataFiQuSMultipole.py +716 -42
  8. fiqus/data/DataFiQuSPancake3D.py +737 -278
  9. fiqus/data/DataMultipole.py +180 -15
  10. fiqus/data/DataRoxieParser.py +90 -51
  11. fiqus/data/DataSettings.py +121 -0
  12. fiqus/data/DataWindingsCCT.py +37 -37
  13. fiqus/data/RegionsModelFiQuS.py +18 -6
  14. fiqus/geom_generators/GeometryCCT.py +905 -905
  15. fiqus/geom_generators/GeometryConductorAC_Strand.py +1391 -1391
  16. fiqus/geom_generators/GeometryMultipole.py +1827 -227
  17. fiqus/geom_generators/GeometryPancake3D.py +316 -117
  18. fiqus/geom_generators/GeometryPancake3DUtils.py +549 -0
  19. fiqus/getdp_runners/RunGetdpCCT.py +4 -4
  20. fiqus/getdp_runners/RunGetdpConductorAC_Strand.py +201 -201
  21. fiqus/getdp_runners/RunGetdpMultipole.py +115 -42
  22. fiqus/getdp_runners/RunGetdpPancake3D.py +28 -6
  23. fiqus/mains/MainCCT.py +2 -2
  24. fiqus/mains/MainConductorAC_Strand.py +132 -132
  25. fiqus/mains/MainMultipole.py +113 -62
  26. fiqus/mains/MainPancake3D.py +63 -23
  27. fiqus/mesh_generators/MeshCCT.py +209 -209
  28. fiqus/mesh_generators/MeshConductorAC_Strand.py +656 -656
  29. fiqus/mesh_generators/MeshMultipole.py +1243 -181
  30. fiqus/mesh_generators/MeshPancake3D.py +275 -192
  31. fiqus/parsers/ParserCOND.py +825 -0
  32. fiqus/parsers/ParserDAT.py +16 -16
  33. fiqus/parsers/ParserGetDPOnSection.py +212 -212
  34. fiqus/parsers/ParserGetDPTimeTable.py +134 -134
  35. fiqus/parsers/ParserMSH.py +53 -53
  36. fiqus/parsers/ParserPOS.py +214 -214
  37. fiqus/parsers/ParserRES.py +142 -142
  38. fiqus/plotters/PlotPythonCCT.py +133 -133
  39. fiqus/plotters/PlotPythonConductorAC.py +855 -840
  40. fiqus/plotters/PlotPythonMultipole.py +18 -18
  41. fiqus/post_processors/PostProcessCCT.py +440 -440
  42. fiqus/post_processors/PostProcessConductorAC.py +49 -49
  43. fiqus/post_processors/PostProcessMultipole.py +353 -229
  44. fiqus/post_processors/PostProcessPancake3D.py +8 -13
  45. fiqus/pre_processors/PreProcessCCT.py +175 -175
  46. fiqus/pro_assemblers/ProAssembler.py +14 -6
  47. fiqus/pro_material_functions/ironBHcurves.pro +246 -246
  48. fiqus/pro_templates/combined/CCT_template.pro +274 -274
  49. fiqus/pro_templates/combined/ConductorAC_template.pro +1025 -1025
  50. fiqus/pro_templates/combined/Multipole_template.pro +1694 -126
  51. fiqus/pro_templates/combined/Pancake3D_template.pro +2294 -1103
  52. fiqus/pro_templates/combined/TSA_materials.pro +162 -0
  53. fiqus/pro_templates/combined/materials.pro +36 -18
  54. fiqus/utils/Utils.py +508 -110
  55. fiqus/utils/update_data_settings.py +33 -0
  56. fiqus-2024.12.0.dist-info/METADATA +130 -0
  57. fiqus-2024.12.0.dist-info/RECORD +84 -0
  58. {fiqus-2024.6.0.dist-info → fiqus-2024.12.0.dist-info}/WHEEL +1 -1
  59. tests/test_FiQuS.py +1 -1
  60. tests/test_geometry_generators.py +101 -2
  61. tests/test_mesh_generators.py +154 -1
  62. tests/test_solvers.py +115 -21
  63. tests/utils/fiqus_test_classes.py +85 -21
  64. tests/utils/generate_reference_files_ConductorAC.py +57 -57
  65. tests/utils/generate_reference_files_Pancake3D.py +4 -5
  66. tests/utils/helpers.py +97 -97
  67. fiqus-2024.6.0.dist-info/METADATA +0 -103
  68. fiqus-2024.6.0.dist-info/RECORD +0 -79
  69. {fiqus-2024.6.0.dist-info → fiqus-2024.12.0.dist-info}/top_level.txt +0 -0
@@ -1,44 +1,57 @@
1
1
  import os
2
2
  import gmsh
3
- from numpy import square as square
4
- from numpy import sqrt as sqrt
3
+ import copy
4
+ import numpy as np
5
+ import json
6
+ import logging
5
7
 
6
8
  from fiqus.utils.Utils import GmshUtils
7
9
  from fiqus.utils.Utils import FilesAndFolders as Util
10
+ from fiqus.utils.Utils import GeometricFunctions as Func
8
11
  from fiqus.data import DataFiQuS as dF
9
12
  from fiqus.data import DataMultipole as dM
10
- from fiqus.data.RegionsModelFiQuS import RegionsModel as rM
11
- from fiqus.data import RegionsModelFiQuS as Reg_Mod_FiQ
13
+ from fiqus.data import RegionsModelFiQuS as rM
12
14
 
15
+ logger = logging.getLogger(__name__)
13
16
 
14
17
  class Mesh:
15
- def __init__(self, data: dF.FDM() = None, sett: dF.FiQuSSettings() = None, mesh_folder: str = None,
18
+ def __init__(self, data: dF.FDM() = None, mesh_folder: str = None,
16
19
  verbose: bool = False):
17
20
  """
18
21
  Class to generate mesh
19
22
  :param data: FiQuS data model
20
- :param sett: settings data model
21
23
  :param verbose: If True more information is printed in python console.
22
24
  """
23
25
  self.data: dF.FDM() = data
24
- self.set: dF.FiQuSSettings() = sett
25
26
  self.mesh_folder = mesh_folder
26
27
  self.verbose: bool = verbose
27
28
 
28
29
  self.md = dM.MultipoleData()
29
- self.rm = rM()
30
+ self.rc = dM.MultipoleRegionCoordinate()
31
+ self.rm = rM.RegionsModel()
32
+ self.strands = None
30
33
 
31
34
  self.gu = GmshUtils(self.mesh_folder, self.verbose)
32
- self.gu.initialize()
35
+ self.gu.initialize(verbosity_Gmsh=self.data.run.verbosity_Gmsh)
33
36
  self.occ = gmsh.model.occ
34
37
  self.mesh = gmsh.model.mesh
35
38
 
36
39
  self.brep_iron_curves = {1: set(), 2: set(), 3: set(), 4: set()}
37
40
  self.mesh_parameters = dict.fromkeys(['SJ', 'SICN', 'SIGE', 'Gamma', 'nodes'])
38
- self.geom_folder = os.path.dirname(self.mesh_folder)
39
- self.model_file = f"{os.path.join(self.mesh_folder, self.data.general.magnet_name)}.msh"
41
+ self.geom_files = os.path.join(os.path.dirname(self.mesh_folder), self.data.general.magnet_name)
42
+ self.model_file = os.path.join(self.mesh_folder, self.data.general.magnet_name)
43
+
44
+ # Insulation sequence involving cable insulation only (turn-to-turn, outlying conductor edge)
45
+ self.ins_type_cond = {}
46
+ # Insulation sequence involving quench heaters (outlying or mid-layer/pole)
47
+ qh_keys = {key: {} for key in range(1, self.data.quench_protection.quench_heaters.N_strips + 1)}
48
+ self.ins_type_qh = {'internal_double': {}, 'internal': copy.deepcopy(qh_keys), 'external': copy.deepcopy(qh_keys)}
49
+ # Insulation sequence between blocks (layer-to-layer, pole-to-pole)
50
+ self.ins_type = {'mid_pole': {}, 'mid_winding': {}, 'mid_layer': {}, 'aux': {}}
51
+ self.qh_data, self.wedge_cond = {}, {}
40
52
 
41
53
  self.colors = {'wedges': [86, 180, 233], # sky blue
54
+ 'insul': [119, 136, 153], # light slate grey
42
55
  'half_turns_pos': [213, 94, 0], # vermilion
43
56
  'half_turns_neg': [255, 136, 42], # light vermilion
44
57
  'air': [240, 228, 66], # yellow
@@ -55,6 +68,12 @@ class Mesh:
55
68
  'BHiron6': [0, 114, 178], # blue
56
69
  'BHiron7': [204, 121, 167]} # reddish purple
57
70
 
71
+ def clear(self):
72
+ self.md = dM.MultipoleData()
73
+ self.rc = dM.MultipoleRegionCoordinate()
74
+ self.rm = rM.RegionsModel()
75
+ gmsh.clear()
76
+
58
77
  def ending_step(self, gui: bool = False):
59
78
  if gui:
60
79
  self.gu.launch_interactive_GUI()
@@ -62,210 +81,1145 @@ class Mesh:
62
81
  gmsh.clear()
63
82
  gmsh.finalize()
64
83
 
65
- def loadAuxiliaryFile(self):
66
- self.md = Util.read_data_from_yaml(f"{os.path.join(self.geom_folder, self.data.general.magnet_name)}.aux", dM.MultipoleData)
84
+ def loadStrandPositions(self, run_type):
85
+ self.strands = json.load(open(f"{self.geom_files}_{run_type}.strs"))
67
86
 
68
- def updateAuxiliaryFile(self):
69
- md2 = Util.read_data_from_yaml(f"{os.path.join(self.geom_folder, self.data.general.magnet_name)}.aux", dM.MultipoleData)
70
- md2.domains.physical_groups = self.md.domains.physical_groups
71
- Util.write_data_to_yaml(f"{os.path.join(self.geom_folder, self.data.general.magnet_name)}.aux", md2.dict())
87
+ def loadAuxiliaryFile(self, run_type):
88
+ self.md = Util.read_data_from_yaml(f"{self.geom_files}_{run_type}.aux", dM.MultipoleData)
72
89
 
73
- def saveMeshFile(self):
74
- gmsh.write(self.model_file)
90
+ def updateAuxiliaryFile(self, run_type):
91
+ Util.write_data_to_yaml(f'{self.model_file}_{run_type}.aux', self.md.dict())
92
+ # md2 = Util.read_data_from_yaml(f"{self.geom_files}.aux", dM.MultipoleData)
93
+ # md2.domains.physical_groups = self.md.domains.physical_groups
94
+ # Util.write_data_to_yaml(f"{self.geom_files}.aux", md2.dict())
75
95
 
76
- def saveRegionFile(self):
77
- Util.write_data_to_yaml(f"{os.path.join(self.mesh_folder, self.data.general.magnet_name)}.reg", self.rm.dict())
96
+ def saveMeshFile(self, run_type):
97
+ gmsh.write(f'{self.model_file}_{run_type}.msh')
98
+
99
+ def saveRegionFile(self, run_type):
100
+ Util.write_data_to_yaml(f'{self.model_file}_{run_type}.reg', self.rm.dict())
101
+
102
+ def saveRegionCoordinateFile(self, run_type):
103
+ Util.write_data_to_yaml(f'{self.model_file}_{run_type}.reco', self.rc.dict())
78
104
 
79
105
  def getIronCurvesTags(self):
80
- if self.data.magnet.geometry.with_iron_yoke:
81
- for quadrant, qq in self.md.geometries.iron.quadrants.items():
82
- for area_name, area in qq.areas.items():
83
- if area.surface:
84
- self.brep_iron_curves[quadrant] |= set(gmsh.model.getAdjacencies(2, area.surface)[1])
85
-
86
- def defineMesh(self):
87
- curves_list = []
88
- for coil_nr, coil in self.md.geometries.coil.coils.items():
89
- for pole_nr, pole in coil.poles.items():
90
- for layer_nr, layer in pole.layers.items():
91
- for winding_nr, winding in layer.windings.items():
92
- for block_key, block in winding.blocks.items():
93
- hts = block.half_turns
94
- for i in range(len(hts.areas) - 1):
95
- curves_list.append(self.occ.copy([(1, hts.lines[str(i + 1) + 'r'])])[0][1])
96
- curves_list.append(self.occ.copy([(1, hts.lines['end'])])[0][1])
106
+ for quadrant, qq in self.md.geometries.iron.quadrants.items():
107
+ for _, area in qq.areas.items():
108
+ if area.surface:
109
+ self.brep_iron_curves[quadrant] |= set(gmsh.model.getAdjacencies(2, area.surface)[1])
97
110
 
111
+ def defineMesh(self, geometry, mesh, run_type):
112
+ thresholds = []
98
113
  self.occ.synchronize()
99
- if not self.data.magnet.mesh.default_mesh:
100
- curve_list_iron = []
101
- if self.data.magnet.geometry.with_iron_yoke:
102
- # if self.from_brep:
103
- for quadrant, qq in self.brep_iron_curves.items():
104
- for line in qq:
105
- curve_list_iron.append(self.occ.copy([(1, line)])[0][1])
106
- # else:
107
- # for quadrant, qq in self.md.geometry.iron.quadrants.items():
108
- # for line_name, line in qq.lines.items():
109
- # curve_list_iron.append(self.occ.copy([(1, line)])[0][1])
110
-
111
- size_min = self.data.magnet.mesh.mesh_coil.SizeMin
112
- size_max = self.data.magnet.mesh.mesh_coil.SizeMax
113
- dist_min = self.data.magnet.mesh.mesh_coil.DistMin
114
- dist_max = self.data.magnet.mesh.mesh_coil.DistMax # iron max_radius
115
-
116
- list_aux_type = "CurvesList"
117
- list_aux = curve_list_iron
118
- size_min_aux = self.data.magnet.mesh.mesh_iron.SizeMin
119
- size_max_aux = self.data.magnet.mesh.mesh_iron.SizeMax
120
- dist_min_aux = self.data.magnet.mesh.mesh_iron.DistMin
121
- dist_max_aux = self.data.magnet.mesh.mesh_iron.DistMax
122
114
 
123
- else:
124
- min_height = 1.
125
- for name, cond in self.set.Model_Data_GS.conductors.items():
126
- min_height = min(min_height, cond.cable.bare_cable_height_mean)
127
-
128
- bore_centers = [self.occ.addPoint(coil.bore_center.x, coil.bore_center.y, 0.)
129
- for coil_nr, coil in self.md.geometries.coil.coils.items()]
130
- point = gmsh.model.getValue(0, self.md.geometries.coil.coils[1].poles[1].layers[1].windings[1].blocks[
131
- 1].half_turns.points['1i'], [])
132
- min_dist = sqrt(square(point[0] - self.md.geometries.coil.coils[1].bore_center.x) +
133
- square(point[1] - self.md.geometries.coil.coils[1].bore_center.y))
134
-
135
- size_min = min_height / 2
136
- size_max = min_height * 20
137
- dist_min = min_height * 2
138
- dist_max = self.md.geometries.iron.max_radius
139
-
140
- list_aux_type = "PointsList"
141
- list_aux = bore_centers
142
- size_min_aux = min_height / 2
143
- size_max_aux = min_height * 20
144
- dist_min_aux = min_dist
145
- dist_max_aux = min_dist
115
+ if mesh.conductors.field.enabled:
116
+ distance_conductors = self.mesh.field.add("Distance")
117
+ self.mesh.field.setNumbers(distance_conductors, "CurvesList",
118
+ [line for coil_nr, coil in self.md.geometries.coil.anticlockwise_order.coils.items() for layer_nr, layer in coil.layers.items()
119
+ for _, block_order in enumerate(layer) for _, line in self.md.geometries.coil.coils[coil_nr].poles[block_order.pole].layers[
120
+ layer_nr].windings[block_order.winding].blocks[block_order.block].half_turns.lines.items()]
121
+ )
122
+ self.mesh.field.setNumber(distance_conductors, "Sampling", 100)
146
123
 
147
- self.occ.synchronize()
124
+ threshold_conductors = self.mesh.field.add("Threshold")
125
+ self.mesh.field.setNumber(threshold_conductors, "InField", distance_conductors)
126
+ self.mesh.field.setNumber(threshold_conductors, "SizeMin", mesh.conductors.field.SizeMin)
127
+ self.mesh.field.setNumber(threshold_conductors, "SizeMax", mesh.conductors.field.SizeMax)
128
+ self.mesh.field.setNumber(threshold_conductors, "DistMin", mesh.conductors.field.DistMin)
129
+ self.mesh.field.setNumber(threshold_conductors, "DistMax", mesh.conductors.field.DistMax)
130
+ self.mesh.field.setNumber(threshold_conductors, "StopAtDistMax", 1)
131
+ thresholds.append(threshold_conductors)
132
+
133
+ if mesh.wedges.field.enabled:
134
+ distance_wedges = self.mesh.field.add("Distance")
135
+ self.mesh.field.setNumbers(distance_wedges, "CurvesList",
136
+ [line for _, coil in self.md.geometries.wedges.coils.items() for _, layer in coil.layers.items() for _, wdg in layer.wedges.items() for _, line in wdg.lines.items()]
137
+ )
138
+ self.mesh.field.setNumber(distance_wedges, "Sampling", 100)
139
+
140
+ # raise Exception(f"cannot set threshold for wedges field: {[line for _, coil in self.md.geometries.wedges.coils.items() for _, layer in coil.layers.items() for _, wdg in layer.wedges.items() for _, line in wdg.lines.items()]}")
141
+
142
+ threshold_wedges = self.mesh.field.add("Threshold")
143
+ self.mesh.field.setNumber(threshold_wedges, "InField", distance_wedges)
144
+ self.mesh.field.setNumber(threshold_wedges, "SizeMin", mesh.wedges.field.SizeMin)
145
+ self.mesh.field.setNumber(threshold_wedges, "SizeMax", mesh.wedges.field.SizeMax)
146
+ self.mesh.field.setNumber(threshold_wedges, "DistMin", mesh.wedges.field.DistMin)
147
+ self.mesh.field.setNumber(threshold_wedges, "DistMax", mesh.wedges.field.DistMax)
148
+ self.mesh.field.setNumber(threshold_wedges, "StopAtDistMax", 1)
149
+ thresholds.append(threshold_wedges)
150
+
151
+ if geometry.with_iron_yoke:
152
+ distance_iron = self.mesh.field.add("Distance")
153
+ self.mesh.field.setNumbers(distance_iron, "CurvesList", [line for _, qq in self.brep_iron_curves.items() for line in qq])
154
+ self.mesh.field.setNumber(distance_iron, "Sampling", 100)
155
+
156
+ if mesh.iron_field.enabled:
148
157
 
149
- distance_coil = self.mesh.field.add("Distance")
150
- self.mesh.field.setNumbers(distance_coil, "CurvesList", curves_list)
151
- self.mesh.field.setNumber(distance_coil, "Sampling", 100)
158
+ threshold_iron = self.mesh.field.add("Threshold")
159
+ self.mesh.field.setNumber(threshold_iron, "InField", distance_iron)
160
+ self.mesh.field.setNumber(threshold_iron, "SizeMin", mesh.iron_field.SizeMin)
161
+ self.mesh.field.setNumber(threshold_iron, "SizeMax", mesh.iron_field.SizeMax)
162
+ self.mesh.field.setNumber(threshold_iron, "DistMin", mesh.iron_field.DistMin)
163
+ self.mesh.field.setNumber(threshold_iron, "DistMax", mesh.iron_field.DistMax)
164
+ # the iron field is typically rather coarse so that 'overwriting' other fields is not a problem
165
+ # its distMax however would need to be large for StopAtDistMax to be effective
166
+ #self.mesh.field.setNumber(threshold_iron, "StopAtDistMax", 1)
167
+ thresholds.append(threshold_iron)
152
168
 
153
- threshold = self.mesh.field.add("Threshold")
154
- self.mesh.field.setNumber(threshold, "InField", distance_coil)
155
- self.mesh.field.setNumber(threshold, "SizeMin", size_min)
156
- self.mesh.field.setNumber(threshold, "SizeMax", size_max)
157
- self.mesh.field.setNumber(threshold, "DistMin", dist_min)
158
- self.mesh.field.setNumber(threshold, "DistMax", dist_max)
169
+ if run_type == 'EM' and mesh.bore_field.enabled:
159
170
 
160
- distance_aux = self.mesh.field.add("Distance")
161
- self.mesh.field.setNumbers(distance_aux, list_aux_type, list_aux)
162
- self.mesh.field.setNumber(distance_aux, "Sampling", 100)
171
+ distance_bore = self.mesh.field.add("Distance")
172
+ self.mesh.field.setNumbers(distance_bore, "PointsList", [pnt for pnt_name, pnt in self.md.geometries.air.points.items() if 'bore' in pnt_name])
173
+ self.mesh.field.setNumber(distance_bore, "Sampling", 100)
163
174
 
164
- threshold_aux = self.mesh.field.add("Threshold")
165
- self.mesh.field.setNumber(threshold_aux, "InField", distance_aux)
166
- self.mesh.field.setNumber(threshold_aux, "SizeMin", size_min_aux)
167
- self.mesh.field.setNumber(threshold_aux, "SizeMax", size_max_aux)
168
- self.mesh.field.setNumber(threshold_aux, "DistMin", dist_min_aux)
169
- self.mesh.field.setNumber(threshold_aux, "DistMax", dist_max_aux)
175
+ threshold_bore = self.mesh.field.add("Threshold")
176
+ self.mesh.field.setNumber(threshold_bore, "InField", distance_bore)
177
+ self.mesh.field.setNumber(threshold_bore, "SizeMin", mesh.bore_field.SizeMin)
178
+ self.mesh.field.setNumber(threshold_bore, "SizeMax", mesh.bore_field.SizeMax)
179
+ self.mesh.field.setNumber(threshold_bore, "DistMin", mesh.bore_field.DistMin)
180
+ self.mesh.field.setNumber(threshold_bore, "DistMax", mesh.bore_field.DistMax)
181
+ self.mesh.field.setNumber(threshold_bore, "StopAtDistMax", 1)
182
+ thresholds.append(threshold_bore)
183
+
184
+ insulation_mesh_fields = []
185
+ if run_type == 'TH':
186
+ for coil_nr, coil in self.md.geometries.insulation.coils.items():
187
+ for _, group in coil.group.items():
188
+ # Areas
189
+ for area_name, area in group.ins.areas.items():
190
+ if area_name.isdigit():
191
+ insulation_mesh_fields.append(self.mesh.field.add("Constant"))
192
+ insulation_mesh_field = insulation_mesh_fields[-1]
193
+ self.mesh.field.setNumbers(insulation_mesh_field, "SurfacesList", [area.surface])
194
+ self.mesh.field.setNumber(insulation_mesh_field, "VIn", mesh.insulation.global_size)
170
195
 
171
196
  background = self.mesh.field.add("Min")
172
- self.mesh.field.setNumbers(background, "FieldsList", [threshold, threshold_aux] if (
173
- self.data.magnet.geometry.with_iron_yoke and not self.data.magnet.mesh.default_mesh
174
- or self.data.magnet.mesh.default_mesh) else [threshold])
197
+ self.mesh.field.setNumbers(background, "FieldsList", thresholds + insulation_mesh_fields)
175
198
  self.mesh.field.setAsBackgroundMesh(background)
176
199
 
177
- def fragment(self):
178
- """
179
- Fragment and group air domains
180
- """
181
- holes = []
182
- for group_name, surfaces in self.md.domains.groups_surfaces.items():
183
- if group_name != 'air_inf':
184
- holes.extend([(2, s) for s in surfaces])
185
- fragmented = self.occ.fragment([(2, self.md.geometries.air_inf.areas['inner'].surface)], holes)[1]
186
- self.md.domains.groups_surfaces['air'] = []
187
- existing_domains = [e[0][1] for e in fragmented[1:]]
188
- for e in fragmented[0]:
189
- if e[1] not in existing_domains:
190
- self.md.domains.groups_surfaces['air'].append(e[1])
200
+ # Apply transfinite curves and potentially surfaces to conductors and wedges
201
+ if mesh.conductors.transfinite.enabled_for in ['curves', 'curves_and_surfaces']:
202
+ for coil_nr, coil in self.md.geometries.coil.anticlockwise_order.coils.items():
203
+ for layer_nr, layer in coil.layers.items():
204
+ for _, block_order in enumerate(layer):
205
+ winding = self.md.geometries.coil.coils[coil_nr].poles[block_order.pole].layers[
206
+ layer_nr].windings[block_order.winding]
207
+ cable = self.data.conductors[winding.conductor_name].cable
208
+ for line_key, line in winding.blocks[block_order.block].half_turns.lines.items():
209
+ if mesh.dict().get('isothermal_conductors', False):
210
+ elements = 1
211
+ elif any([i in line_key for i in ['i', 'o']]):
212
+ elements = max(1, round(cable.bare_cable_height_mean / mesh.conductors.transfinite.curve_target_size_height))
213
+ elif any([i in line_key for i in ['l', 'h']]):
214
+ elements = max(1, round(cable.bare_cable_width / mesh.conductors.transfinite.curve_target_size_width))
215
+
216
+ self.mesh.setTransfiniteCurve(line, elements)
217
+ if mesh.conductors.transfinite.enabled_for=='curves_and_surfaces' or mesh.dict().get('isothermal_conductors', False):
218
+ for _, area in winding.blocks[block_order.block].half_turns.areas.items():
219
+ self.mesh.setTransfiniteSurface(area.surface)
220
+ self.mesh.setRecombine(2, area.surface)
221
+
222
+ if 'insulation' in mesh.dict() and 'TSA' in mesh.dict()["insulation"]:
223
+ # Apply transfinite curves to thin shell lines
224
+ if geometry.use_TSA:
225
+ gts = self.md.geometries.thin_shells
226
+ conductor_target_sizes = {'width': mesh.conductors.transfinite.curve_target_size_width, 'height': mesh.conductors.transfinite.curve_target_size_height}
227
+ wedge_target_sizes = {'width': mesh.wedges.transfinite.curve_target_size_width, 'height': mesh.wedges.transfinite.curve_target_size_height}
228
+ for ts_group, side in zip([gts.mid_layers_ht_to_ht, gts.mid_layers_ht_to_wdg, gts.mid_layers_wdg_to_ht, gts.mid_layers_wdg_to_wdg,
229
+ gts.mid_poles, gts.mid_windings, gts.mid_turn_blocks, gts.mid_wedge_turn, gts.mid_layers_aux],
230
+ ['height', 'height', 'height', 'height', 'width', 'width', 'width', 'width', 'height']):
231
+ for ts_name, ts in ts_group.items():
232
+ for _, line in ts.lines.items() if isinstance(ts, dM.Region) else ts.mid_layers.lines.items():
233
+ if mesh.isothermal_conductors or mesh.isothermal_wedges:
234
+ elements = 1
235
+ else:
236
+ coords = gmsh.model.getValue(1, line, [i[0] for i in gmsh.model.getParametrizationBounds(1, line)])
237
+ target_size = wedge_target_sizes[side] if ts_name.count('w') == 2 else conductor_target_sizes[side]
238
+ elements = max(1, round(Func.points_distance(coords[:2], coords[3:-1]) / target_size))
191
239
 
192
- self.occ.synchronize()
240
+ # it's a wedge
241
+ if ts_name.count('w') == 2 and mesh.wedges.transfinite.enabled_for in ['curves', 'curves_and_surfaces']:
242
+ self.mesh.setTransfiniteCurve(line, elements)
243
+ elif ts_name.count('w') != 2 and mesh.conductors.transfinite.enabled_for in ['curves', 'curves_and_surfaces']:
244
+ self.mesh.setTransfiniteCurve(line, elements)
245
+ # COMMENTED since this overwrites also the cable transfinite meshes and in general,
246
+ # restricting the insulation boundaries to be transfinite seems very restrictive due to their complex geometry
247
+ # Apply transfinite curves to insulation boundaries
248
+ # else:
249
+ # for coil_nr, coil in self.md.geometries.insulation.coils.items():
250
+ # for group_nr, group in coil.group.items():
251
+ # cable_height = self.data.conductors[self.md.geometries.coil.coils[coil_nr].poles[
252
+ # group.blocks[0][0]].layers[group.blocks[0][1]].windings[group.blocks[0][2]].conductor_name].cable.bare_cable_height_mean
253
+ # for line in [bnd[1] for bnd in gmsh.model.getBoundary(
254
+ # [(2, list(group.ins.areas.values())[0].surface)], # +
255
+ # # [(2, ht.surface) for blk_order in group.blocks for ht in
256
+ # # self.md.geometries.coil.coils[coil_nr].poles[blk_order[0]].layers[blk_order[1]].windings[blk_order[2]].blocks[blk_order[3]].half_turns.areas.values()] +
257
+ # # [(2, self.md.geometries.wedges.coils[coil_nr].layers[wdg_order[0]].wedges[wdg_order[1]].areas[str(wdg_order[1])].surface) for wdg_order in group.wedges],
258
+ # combined=True, oriented=False)]:
259
+ # pnts = gmsh.model.getAdjacencies(1, line)[1]
260
+ # length = Func.points_distance(gmsh.model.getValue(0, pnts[0], []), gmsh.model.getValue(0, pnts[1], []))
261
+ # self.mesh.setTransfiniteCurve(line,max(1, round(length / (mesh.conductor_target_sizes.width if length > 2 * cable_height else mesh.conductor_target_sizes.height))))
262
+
263
+ # Apply transfinite curves to wedges
264
+ if mesh.wedges.transfinite.enabled_for in ['curves', 'curves_and_surfaces']:
265
+ if geometry.with_wedges:
266
+ for coil_nr, coil in self.md.geometries.wedges.coils.items():
267
+ for layer_nr, layer in coil.layers.items():
268
+ for _, wedge in layer.wedges.items():
269
+ pnts = gmsh.model.getAdjacencies(1, wedge.lines['i'])[1]
270
+ inner_height = Func.points_distance(gmsh.model.getValue(0, pnts[0], []), gmsh.model.getValue(0, pnts[1], []))
271
+ pnts = gmsh.model.getAdjacencies(1, wedge.lines['l'])[1]
272
+ width = Func.points_distance(gmsh.model.getValue(0, pnts[0], []), gmsh.model.getValue(0, pnts[1], []))
273
+ pnts = gmsh.model.getAdjacencies(1, wedge.lines['o'])[1]
274
+ outer_height = Func.points_distance(gmsh.model.getValue(0, pnts[0], []), gmsh.model.getValue(0, pnts[1], []))
275
+ for line_key, line in wedge.lines.items():
276
+ if mesh.dict().get('isothermal_wedges', False):
277
+ elements = 1
278
+ elif 'i' in line_key:
279
+ elements = max(1, round(inner_height / mesh.wedges.transfinite.curve_target_size_height))
280
+ elif 'o' in line_key:
281
+ elements = max(1, round((inner_height if mesh.wedges.transfinite.enabled_for in ['curves', 'curves_and_surfaces']
282
+ else outer_height) / mesh.wedges.transfinite.curve_target_size_height))
283
+ elif any([i in line_key for i in ['l', 'h']]):
284
+ elements = max(1, round(width / mesh.wedges.transfinite.curve_target_size_width))
285
+ if mesh.wedges.transfinite.enabled_for in ['curves', 'curves_and_surfaces']:
286
+ self.mesh.setTransfiniteCurve(line, elements)
287
+ if mesh.wedges.transfinite.enabled_for=='curves_and_surfaces' or mesh.dict().get('isothermal_wedges', False):
288
+ self.mesh.setTransfiniteSurface(list(wedge.areas.values())[0].surface)
289
+ self.mesh.setRecombine(2, list(wedge.areas.values())[0].surface)
193
290
 
194
- def createPhysicalGroups(self):
291
+ def createPhysicalGroups(self, geometry):
195
292
  """
196
293
  Creates physical groups by grouping the mirrored entities according to the Roxie domains
197
294
  """
295
+ offset: int = 1 if 'symmetry' in geometry.dict() else int(1e6)
296
+ pg_tag = offset
297
+
298
+ # Create physical groups of iron yoke regions and block insulation
198
299
  pg = self.md.domains.physical_groups
199
- for group_name, surfaces in self.md.domains.groups_surfaces.items():
200
- pg.surfaces[group_name] = gmsh.model.addPhysicalGroup(2, surfaces)
201
- gmsh.model.setPhysicalName(2, pg.surfaces[group_name], group_name)
202
- if group_name[:5] == 'block':
203
- color = self.colors['half_turns_pos'] if group_name[-3:] == 'pos' else self.colors['half_turns_neg']
204
- elif group_name[:5] == 'wedge':
205
- color = self.colors['wedges']
206
- else:
207
- color = self.colors[group_name]
300
+ for group_name, surfaces in self.md.domains.groups_entities.iron.items():
301
+ pg.iron.surfaces[group_name] = gmsh.model.addPhysicalGroup(2, surfaces, pg_tag)
302
+ gmsh.model.setPhysicalName(2, pg.iron.surfaces[group_name], group_name)
303
+ color = self.colors[group_name]
208
304
  gmsh.model.setColor([(2, i) for i in surfaces], color[0], color[1], color[2])
305
+ pg_tag += 1
306
+
307
+ # Create the physical group of air infinite
308
+ if 'symmetry' in geometry.dict():
309
+ gmsh.model.setPhysicalName(0, gmsh.model.addPhysicalGroup(
310
+ 0, [pnt for pnt_name, pnt in self.md.geometries.air.points.items() if 'bore_field' in pnt_name], pg_tag), 'bore_centers')
311
+ pg_tag += 1
312
+ pg.air_inf_bnd = gmsh.model.addPhysicalGroup(1, [self.md.geometries.air_inf.lines['outer']], pg_tag)
313
+ gmsh.model.setPhysicalName(1, pg.air_inf_bnd, 'air_inf_bnd')
314
+ pg_tag += 1
315
+ pg.air_inf = gmsh.model.addPhysicalGroup(2, [self.md.geometries.air_inf.areas['outer'].surface], pg_tag)
316
+ gmsh.model.setPhysicalName(2, pg.air_inf, 'air_inf')
317
+ gmsh.model.setColor([(2, self.md.geometries.air_inf.areas['outer'].surface)], self.colors['air_inf'][0], self.colors['air_inf'][1], self.colors['air_inf'][2])
318
+ pg_tag += 1
319
+ pg.air = gmsh.model.addPhysicalGroup(2, self.md.domains.groups_entities.air, pg_tag)
320
+ gmsh.model.setPhysicalName(2, pg.air, 'air')
321
+ gmsh.model.setColor([(2, i) for i in self.md.domains.groups_entities.air], self.colors['air'][0], self.colors['air'][1], self.colors['air'][2])
322
+ pg_tag += 1
323
+
324
+ # Create physical groups of half turns
325
+ lyr_list_group = []
326
+ for coil_nr, coil in self.md.geometries.coil.anticlockwise_order.coils.items():
327
+ lyr_list_group.extend(['cl' + str(coil_nr) + 'ly' + str(lyr) for lyr in list(coil.layers.keys())])
328
+ for layer_nr, layer in coil.layers.items():
329
+ ht_list_group = []
330
+ for nr, block_order in enumerate(layer):
331
+ wnd = self.md.geometries.coil.coils[coil_nr].poles[block_order.pole].layers[
332
+ layer_nr].windings[block_order.winding]
333
+ block = wnd.blocks[block_order.block]
334
+ block_nr = block_order.block
335
+ pg.blocks[block_nr] = dM.PoweredBlock()
336
+ blk_pg = pg.blocks[block_nr]
337
+ blk_pg.current_sign = block.current_sign
338
+ blk_pg.conductor = wnd.conductor_name
339
+ color = self.colors['half_turns_pos'] if block.current_sign > 0 else self.colors['half_turns_neg']
340
+ ht_list = list(block.half_turns.areas.keys())
341
+ ht_list_group.extend(ht_list)
342
+ if nr + 1 < len(layer):
343
+ if layer[nr + 1].pole == block_order.pole and layer[nr + 1].winding != block_order.winding:
344
+ ht_list_group.append('w' + str(nr))
345
+ # Create 2D physical groups of half turns
346
+ for ht_key, ht in block.half_turns.areas.items():
347
+ blk_pg.half_turns[int(ht_key)] = dM.PoweredGroup()
348
+ ht_pg = blk_pg.half_turns[int(ht_key)]
349
+ # Create physical group and assign name and color
350
+ ht_pg.tag = gmsh.model.addPhysicalGroup(2, [ht.surface], pg_tag)
351
+ gmsh.model.setPhysicalName(2, ht_pg.tag, ht_key)
352
+ gmsh.model.setColor([(2, ht.surface)], color[0], color[1], color[2])
353
+ pg_tag += 1
354
+ # Assign thin-shell group
355
+ # the check for reversed block coil is not tested well
356
+ if geometry.dict().get('correct_block_coil_tsa_checkered_scheme', False) and self.md.geometries.coil.coils[coil_nr].type == 'reversed-block-coil':
357
+ azimuthal = 'a1' if list(wnd.blocks.keys()).index(block_nr) % 2 == 0 else 'a2'
358
+ else:
359
+ azimuthal = 'a1' if lyr_list_group.index('cl' + str(coil_nr) + 'ly' + str(layer_nr)) % 2 == 0 else 'a2'
360
+ radial = 'r1' if ht_list_group.index(ht_key) % 2 == 0 else 'r2'
361
+ ht_pg.group = radial + '_' + azimuthal
362
+ if geometry.dict().get('use_TSA', False):
363
+ # Create 1D physical groups of thin shells
364
+ for ht_line_key, ht_line in block.half_turns.lines.items():
365
+ ht_nr = ht_line_key[:-1]
366
+ # Create half turn line groups
367
+ line_pg = gmsh.model.addPhysicalGroup(1, [ht_line], pg_tag)
368
+ gmsh.model.setPhysicalName(1, line_pg, ht_line_key)
369
+ color = [0, 0, 0] if blk_pg.half_turns[int(ht_nr)].group[:2] == 'r1' else [150, 150, 150]
370
+ gmsh.model.setColor([(1, ht_line)], color[0], color[1], color[2])
371
+ pg_tag += 1
372
+ # Store thin shell tags
373
+ blk_pg.half_turns[int(ht_nr)].lines[ht_line_key[-1]] = line_pg
374
+
375
+ # Create points region for projection
376
+ if 'use_TSA' in geometry.dict():
377
+ self.md.domains.physical_groups.half_turns_points = gmsh.model.addPhysicalGroup(
378
+ 0, [gmsh.model.getAdjacencies(1, gmsh.model.getAdjacencies(2, ht.surface)[1][0])[1][0]
379
+ for coil_nr, coil in self.md.geometries.coil.anticlockwise_order.coils.items()
380
+ for layer_nr, layer in coil.layers.items() for block_order in layer
381
+ for ht_key, ht in self.md.geometries.coil.coils[coil_nr].poles[block_order.pole].layers[
382
+ layer_nr].windings[block_order.winding].blocks[block_order.block].half_turns.areas.items()], int(4e6))
383
+ gmsh.model.setPhysicalName(0, self.md.domains.physical_groups.half_turns_points, 'points')
384
+
385
+ # Create physical groups of insulations
386
+ if not geometry.dict().get('use_TSA', True):
387
+ for coil_nr, coil in self.md.geometries.insulation.coils.items():
388
+ for group_nr, group in coil.group.items():
389
+ # Areas
390
+ for area_name, area in group.ins.areas.items():
391
+ if area_name.isdigit():
392
+ pg.insulations.surfaces[area_name] = gmsh.model.addPhysicalGroup(2, [area.surface], pg_tag)
393
+ gmsh.model.setPhysicalName(2, pg.insulations.surfaces[area_name], 'insul_' + area_name)
394
+ gmsh.model.setColor([(2, area.surface)], self.colors['insul'][0], self.colors['insul'][1], self.colors['insul'][2])
395
+ pg_tag += 1
396
+
397
+ # Boundaries
398
+ area_name = list(group.ins.areas.keys())[0] # todo: test for Mono
399
+ pg.insulations.curves['ext' + area_name] = gmsh.model.addPhysicalGroup(
400
+ 1, [bnd[1] for bnd in gmsh.model.getBoundary([(2, list(group.ins.areas.values())[0].surface)], combined=False, oriented=False)[:len(group.ins.lines)]], pg_tag)
401
+ gmsh.model.setPhysicalName(1, pg.insulations.curves['ext' + area_name], 'insul_ext' + area_name)
402
+ pg_tag += 1
403
+ # todo: NOT COMPLETED: would work if the tags were updated in the Geometry script after saving and loading brep
404
+ # side_lines = {'i': [], 'o': [], 'l': [], 'h': []}
405
+ # for line_name, line in group.ins.lines.items():
406
+ # side_lines[line_name[-1] if line_name[-1].isalpha() else sorted(line_name)[-1]].append(line)
407
+ # for side, side_line in side_lines.items():
408
+ # pg.insulations.curves[str(group_nr) + side] = gmsh.model.addPhysicalGroup(1, side_line, pg_tag)
409
+ # gmsh.model.setPhysicalName(1, pg.insulations.curves[str(group_nr) + side], str(group_nr) + side)
410
+ # pg_tag += 1
411
+
412
+ # Create physical groups of wedges
413
+ for coil_nr, coil in self.md.geometries.wedges.coils.items():
414
+ for layer_nr, layer in coil.layers.items():
415
+ for wedge_nr, wedge in layer.wedges.items():
416
+ pg.wedges[wedge_nr] = dM.WedgeGroup()
417
+ wedge_pg = pg.wedges[wedge_nr]
418
+ wedge_pg.tag = gmsh.model.addPhysicalGroup(2, [wedge.areas[str(wedge_nr)].surface], pg_tag)
419
+ gmsh.model.setPhysicalName(2, wedge_pg.tag, 'w' + str(wedge_nr))
420
+ gmsh.model.setColor([(2, wedge.areas[str(wedge_nr)].surface)],
421
+ self.colors['wedges'][0], self.colors['wedges'][1], self.colors['wedges'][2])
422
+ pg_tag += 1
423
+ # Assign thin-shell group
424
+ prev_block_hts = pg.blocks[layer.block_prev[wedge_nr]].half_turns
425
+ if len(list(prev_block_hts.keys())) > 1:
426
+ wedge_pg.group = prev_block_hts[list(prev_block_hts.keys())[-2]].group
427
+ else:
428
+ prev_group = prev_block_hts[list(prev_block_hts.keys())[0]].group
429
+ wedge_pg.group = ('r1' if prev_group[1] == '2' else 'r2') + prev_group[prev_group.index('_'):]
430
+ if geometry.dict().get('use_TSA', False):
431
+ # Create 1D physical groups of thin shells
432
+ for line_key, line in wedge.lines.items():
433
+ wedge_pg.lines[line_key] = gmsh.model.addPhysicalGroup(1, [line], pg_tag)
434
+ gmsh.model.setPhysicalName(1, wedge_pg.lines[line_key], 'w' + str(wedge_nr) + line_key)
435
+ color = [0, 0, 0] if wedge_pg.group[:2] == 'r1' else [150, 150, 150]
436
+ gmsh.model.setColor([(1, line)], color[0], color[1], color[2])
437
+ pg_tag += 1
438
+
439
+ # Create physical groups of thin shells
440
+ if geometry.dict().get('use_TSA', False):
441
+ gts = self.md.geometries.thin_shells
442
+ # Create physical groups of block mid-layer lines
443
+ block_coil_flag = False
444
+ for ts_name, ts in gts.mid_layers_ht_to_ht.items():
445
+ blk_i, blk_o = ts_name[:ts_name.index('_')], ts_name[ts_name.index('_') + 1:]
446
+ for coil_nr, coil in self.md.geometries.coil.anticlockwise_order.coils.items():
447
+ if (any(int(blk_i) == blk_order.block for blk_order in self.md.geometries.coil.anticlockwise_order.coils[coil_nr].layers[1]) and
448
+ any(int(blk_o) == blk_order.block for blk_order in self.md.geometries.coil.anticlockwise_order.coils[coil_nr].layers[1])): # block-coil mid-pole case
449
+ block_coil_flag = True
450
+ for line_name, line in ts.mid_layers.lines.items():
451
+ line_pg = gmsh.model.addPhysicalGroup(1, [line], pg_tag)
452
+ gmsh.model.setPhysicalName(1, line_pg, line_name)
453
+ pg_tag += 1
454
+ ht1, ht2 = int(line_name[:line_name.index('_')]), int(line_name[line_name.index('_') + 1:])
455
+ if ht1 in ts.half_turn_lists[blk_i]:
456
+ if block_coil_flag:
457
+ pg.blocks[int(blk_i)].half_turns[ht1].mid_layer_lines.inner[line_name] = line_pg
458
+ else:
459
+ pg.blocks[int(blk_i)].half_turns[ht1].mid_layer_lines.outer[line_name] = line_pg
460
+ pg.blocks[int(blk_o)].half_turns[ht2].mid_layer_lines.inner[line_name] = line_pg
461
+ else:
462
+ pg.blocks[int(blk_o)].half_turns[ht1].mid_layer_lines.inner[line_name] = line_pg
463
+ if block_coil_flag:
464
+ pg.blocks[int(blk_i)].half_turns[ht2].mid_layer_lines.inner[line_name] = line_pg
465
+ else:
466
+ pg.blocks[int(blk_i)].half_turns[ht2].mid_layer_lines.outer[line_name] = line_pg
467
+
468
+ # Create physical groups of wedge-to-block mid-layer lines
469
+ for ts_name, ts in gts.mid_layers_wdg_to_ht.items():
470
+ wdg, blk = ts_name.split('_')
471
+ for line_name, line in ts.lines.items():
472
+ line_pg = gmsh.model.addPhysicalGroup(1, [line], pg_tag)
473
+ gmsh.model.setPhysicalName(1, line_pg, line_name)
474
+ pg_tag += 1
475
+ pg.wedges[int(wdg[1:])].mid_layer_lines.outer[line_name] = line_pg
476
+ ht = line_name[:line_name.index('_')] if line_name[line_name.index('_') + 1:] == wdg else line_name[line_name.index('_') + 1:]
477
+ pg.blocks[int(blk)].half_turns[int(ht)].mid_layer_lines.inner[line_name] = line_pg
478
+
479
+ # Create physical groups of block-to-wedge mid-layer lines
480
+ for ts_name, ts in gts.mid_layers_ht_to_wdg.items():
481
+ wdg, blk = ts_name.split('_')
482
+ for line_name, line in ts.lines.items():
483
+ line_pg = gmsh.model.addPhysicalGroup(1, [line], pg_tag)
484
+ gmsh.model.setPhysicalName(1, line_pg, line_name)
485
+ pg_tag += 1
486
+ pg.wedges[int(wdg[1:])].mid_layer_lines.inner[line_name] = line_pg
487
+ ht = line_name[:line_name.index('_')] if line_name[line_name.index('_') + 1:] == wdg else line_name[line_name.index('_') + 1:]
488
+ pg.blocks[int(blk)].half_turns[int(ht)].mid_layer_lines.outer[line_name] = line_pg
489
+
490
+ # Create physical groups of wedge-to-wedge mid-layer lines
491
+ for ts_name, ts in gts.mid_layers_wdg_to_wdg.items():
492
+ wdg_i, wdg_o = ts_name[1:ts_name.index('_')], ts_name[ts_name.index('_') + 2:]
493
+ line_pg = gmsh.model.addPhysicalGroup(1, [ts.lines[list(ts.lines.keys())[0]]], pg_tag)
494
+ gmsh.model.setPhysicalName(1, line_pg, ts_name)
495
+ pg_tag += 1
496
+ pg.wedges[int(wdg_i)].mid_layer_lines.outer[ts_name] = line_pg
497
+ pg.wedges[int(wdg_o)].mid_layer_lines.inner[ts_name] = line_pg
498
+
499
+ # Create non-mid-layer thin shells
500
+ for ts_group_name, ts_group in zip(['mid_pole', 'mid_winding', 'mid_turn', 'mid_turn', 'aux'],
501
+ [gts.mid_poles, gts.mid_windings, gts.mid_turn_blocks, gts.mid_wedge_turn, gts.mid_layers_aux]):
502
+ for ts_name, ts in ts_group.items():
503
+ if '_' in ts_name:
504
+ el_name1, el_name2 = ts_name.split('_')
505
+ el_name1, el_name2 = el_name1.strip('w'), el_name2.strip('w')
506
+ else: # mid_turn_blocks
507
+ el_name1, el_name2 = ts_name, ts_name
508
+ for line_name, line in ts.lines.items():
509
+ line_pg = gmsh.model.addPhysicalGroup(1, [line], pg_tag)
510
+ gmsh.model.setPhysicalName(1, line_pg, line_name)
511
+ pg_tag += 1
512
+ if ts_group_name == 'aux':
513
+ ht1 = int(list(ts.points.keys())[0][:-1])
514
+ else:
515
+ ht1, ht2 = line_name.split('_')
516
+ pg.blocks[int(el_name2)].half_turns[int(ht2)].__dict__[ts_group_name + '_lines'][line_name] = line_pg
517
+ if ts_name[0] == 'w':
518
+ pg.wedges[int(el_name1)].__dict__[ts_group_name + '_lines'][line_name] = line_pg
519
+ else:
520
+ pg.blocks[int(el_name1)].half_turns[int(ht1)].__dict__[ts_group_name + '_lines'][line_name] = line_pg
521
+
522
+ # Create physical groups of symmetric boundaries
523
+ if geometry.dict().get('symmetry', 'none') != 'none':
524
+ line_tags_normal_free, line_tags_tangent_free = [], []
525
+ if geometry.symmetry == 'xy':
526
+ if len(self.md.geometries.coil.coils[1].poles) == 2:
527
+ line_tags_normal_free = self.md.domains.groups_entities.symmetric_boundaries.y
528
+ line_tags_tangent_free = self.md.domains.groups_entities.symmetric_boundaries.x
529
+ elif len(self.md.geometries.coil.coils[1].poles) == 4: # todo: think about other multi-pole types
530
+ line_tags_tangent_free = self.md.domains.groups_entities.symmetric_boundaries.x +\
531
+ self.md.domains.groups_entities.symmetric_boundaries.y
532
+ elif geometry.symmetry == 'x':
533
+ if 'solenoid' in self.md.geometries.coil.coils[1].type:
534
+ line_tags_normal_free = self.md.domains.groups_entities.symmetric_boundaries.x
535
+ else:
536
+ line_tags_tangent_free = self.md.domains.groups_entities.symmetric_boundaries.x
537
+ elif geometry.symmetry == 'y':
538
+ if len(self.md.geometries.coil.coils[1].poles) == 2:
539
+ line_tags_normal_free = self.md.domains.groups_entities.symmetric_boundaries.y
540
+ elif len(self.md.geometries.coil.coils[1].poles) == 4:
541
+ line_tags_tangent_free = self.md.domains.groups_entities.symmetric_boundaries.y
542
+ if line_tags_normal_free:
543
+ pg.symmetric_boundaries.normal_free = gmsh.model.addPhysicalGroup(1, line_tags_normal_free, pg_tag)
544
+ gmsh.model.setPhysicalName(1, pg.symmetric_boundaries.normal_free, 'normal_free_bnd')
545
+ pg_tag += 1
546
+ if line_tags_tangent_free:
547
+ pg.symmetric_boundaries.tangential_free = gmsh.model.addPhysicalGroup(1, line_tags_tangent_free, pg_tag)
548
+ gmsh.model.setPhysicalName(1, pg.symmetric_boundaries.tangential_free, 'tangent_free_bnd')
549
+ pg_tag += 1
550
+
551
+ def rearrangeThinShellsData(self):
552
+ pg = self.md.domains.physical_groups
553
+ ins_th = self.md.geometries.thin_shells.ins_thickness
554
+ qh = self.data.quench_protection.quench_heaters
555
+
556
+ def _store_qh_data(position, thin_shell, ts_tag):
557
+ qh_ins = self.ins_type_qh[position][qh.ids[ht_index]]
558
+ if thin_shell not in qh_ins: qh_ins[thin_shell] = []
559
+ qh_ins[thin_shell].append(ts_tag)
560
+ if thin_shell not in self.qh_data[qh.ids[ht_index]]:
561
+ self.qh_data[qh.ids[ht_index]][thin_shell] = {'conductor': blk_pg.conductor, 'ht_side': qh_side}
562
+
563
+ def _store_ts_tags(pg_el, geom_ts_name='', geom_ts_name2=None, ts_grp='', lines='', lines_side=None):
564
+ geom_ts = self.md.geometries.thin_shells.dict()[geom_ts_name]
565
+ for ln_name, ln_tag in (pg_el.dict()[lines][lines_side] if lines_side else pg_el.dict()[lines]).items():
566
+ for ts_name in ts_groups[ts_grp]:
567
+ if ts_name in geom_ts:
568
+ if ln_name in (geom_ts[ts_name][geom_ts_name2]['lines'] if geom_ts_name2 else geom_ts[ts_name]['lines']):
569
+ if ts_name not in self.ins_type[ts_grp]: self.ins_type[ts_grp][ts_name] = []
570
+ self.ins_type[ts_grp][ts_name].append(ln_tag)
571
+ break
572
+
573
+ # Collect thin shell tags
574
+ # Half turns
575
+ for blk_pg_nr, blk_pg in pg.blocks.items():
576
+ ts_groups = {'mid_wedge_turn': [ts_name for ts_name in self.md.geometries.thin_shells.mid_wedge_turn if blk_pg_nr == int(ts_name.split('_')[1])],
577
+ 'aux': [ts_name for ts_name in ins_th.mid_layer if str(blk_pg_nr) in ts_name.split('_')],
578
+ 'mid_winding': [ts_name for ts_name in ins_th.mid_winding if blk_pg_nr == int(ts_name.split('_')[0])],
579
+ 'mid_pole': [ts_name for ts_name in ins_th.mid_pole if blk_pg_nr == int(ts_name.split('_')[0])],
580
+ 'mid_layer': [ts_name for ts_name in ins_th.mid_layer if ts_name[0] != 'w' and blk_pg_nr == int(ts_name.split('_')[0])
581
+ or ts_name[0] == 'w' and ts_name.split('_')[1][0] != 'w' and blk_pg_nr == int(ts_name.split('_')[1])],
582
+ 'mid_layer_qh_i': [ts_name for ts_name in ins_th.mid_layer if ts_name.split('_')[1][0] != 'w' and blk_pg_nr == int(ts_name.split('_')[1])]}
583
+ ht_list = list(blk_pg.half_turns.keys())
584
+ self.ins_type_cond[str(blk_pg_nr)] = {'inner': [], 'outer': [], 'higher': [], 'lower': [], 'mid_turn': []}
585
+ for ht_nr, ht in blk_pg.half_turns.items():
586
+ if ht_nr in qh.turns and qh.N_strips > 0: # check if a quench heater strip touches the current half-turn
587
+ ht_index = qh.turns.index(ht_nr)
588
+ qh_side = qh.turns_sides[ht_index]
589
+ if qh.ids[ht_index] not in self.qh_data: self.qh_data[qh.ids[ht_index]] = {}
590
+ else: qh_side = ''
591
+
592
+ # find conductor type of ht adjacent to wedge
593
+ if ht_list.index(ht_nr) == len(ht_list) - 1 and ht.mid_turn_lines:
594
+ for line_name, line_tag in ht.mid_turn_lines.items():
595
+ for ts_name in ts_groups['mid_wedge_turn']:
596
+ if line_name in self.md.geometries.thin_shells.mid_wedge_turn[ts_name].lines:
597
+ self.wedge_cond[int(ts_name[1:ts_name.index('_')])] = blk_pg.conductor
598
+ break
599
+
600
+ self.ins_type_cond[str(blk_pg_nr)]['mid_turn'].extend(list(ht.mid_turn_lines.values())) # mid-turn insulation
601
+
602
+ if ht.aux_lines: # outer mid-layer insulation
603
+ _store_ts_tags(ht, geom_ts_name='mid_layers_aux', ts_grp='aux', lines='aux_lines')
604
+
605
+ if ht.mid_layer_lines.inner and qh_side == 'i':
606
+ for line_name, line_tag in ht.mid_layer_lines.inner.items():
607
+ for ts_name in ts_groups['mid_layer_qh_i']:
608
+ if ts_name in self.md.geometries.thin_shells.mid_layers_ht_to_ht:
609
+ ts_lines = self.md.geometries.thin_shells.mid_layers_ht_to_ht[ts_name].mid_layers.lines
610
+ elif ts_name in self.md.geometries.thin_shells.mid_layers_wdg_to_ht:
611
+ ts_lines = self.md.geometries.thin_shells.mid_layers_wdg_to_ht[ts_name].lines
612
+ else: ts_lines = []
613
+ if line_name in ts_lines:
614
+ _store_qh_data('internal', ts_name, line_tag)
615
+ break
616
+ elif ht.mid_layer_lines.inner: # block-coil (!) inner mid-layer insulation
617
+ _store_ts_tags(ht, geom_ts_name='mid_layers_ht_to_ht', geom_ts_name2='mid_layers', ts_grp='mid_layer', lines='mid_layer_lines', lines_side='inner')
618
+ elif not ht.mid_layer_lines.inner and qh_side == 'i': # quench heater inner insulation
619
+ _store_qh_data('external', blk_pg_nr, ht.lines['i'])
620
+ elif not ht.mid_layer_lines.inner: # inner insulation
621
+ self.ins_type_cond[str(blk_pg_nr)]['inner'].append(ht.lines['i'])
622
+
623
+ if ht.mid_layer_lines.outer and qh_side == 'o': # mid-layer quench heater insulation
624
+ for line_name, line_tag in ht.mid_layer_lines.outer.items():
625
+ for ts_name in ts_groups['mid_layer']:
626
+ if ts_name in self.md.geometries.thin_shells.mid_layers_ht_to_ht:
627
+ ts_lines = self.md.geometries.thin_shells.mid_layers_ht_to_ht[ts_name].mid_layers.lines
628
+ elif ts_name in self.md.geometries.thin_shells.mid_layers_ht_to_wdg:
629
+ ts_lines = self.md.geometries.thin_shells.mid_layers_ht_to_wdg[ts_name].lines
630
+ else: ts_lines = []
631
+ if line_name in ts_lines:
632
+ _store_qh_data('internal', ts_name, line_tag)
633
+ break
634
+ elif ht.mid_layer_lines.outer: # mid-layer insulation
635
+ for line_name, line_tag in ht.mid_layer_lines.outer.items():
636
+ for ts_name in ts_groups['mid_layer']:
637
+ if ts_name in self.md.geometries.thin_shells.mid_layers_ht_to_ht:
638
+ ts_lines = self.md.geometries.thin_shells.mid_layers_ht_to_ht[ts_name].mid_layers.lines
639
+ elif ts_name in self.md.geometries.thin_shells.mid_layers_ht_to_wdg:
640
+ ts_lines = self.md.geometries.thin_shells.mid_layers_ht_to_wdg[ts_name].lines
641
+ else: ts_lines = []
642
+ if line_name in ts_lines:
643
+ if ts_name not in self.ins_type['mid_layer']: self.ins_type['mid_layer'][ts_name] = []
644
+ self.ins_type['mid_layer'][ts_name].append(line_tag)
645
+ break
646
+ elif not ht.mid_layer_lines.outer and qh_side == 'o': # quench heater outer insulation
647
+ _store_qh_data('external', blk_pg_nr, ht.lines['o'])
648
+ else: # outer insulation
649
+ self.ins_type_cond[str(blk_pg_nr)]['outer'].append(ht.lines['o'])
650
+
651
+ # mid-pole insulation
652
+ _store_ts_tags(ht, geom_ts_name='mid_poles', ts_grp='mid_pole', lines='mid_pole_lines')
653
+
654
+ # mid-winding insulation
655
+ _store_ts_tags(ht, geom_ts_name='mid_windings', ts_grp='mid_winding', lines='mid_winding_lines')
656
+
657
+ if ht_list.index(ht_nr) == 0 and len(ht.mid_turn_lines) + len(ht.mid_winding_lines) + len(ht.mid_pole_lines) == 1: # lower angle external side insulation
658
+ if qh_side == 'l': _store_qh_data('external', blk_pg_nr, ht.lines['l'])
659
+ else: self.ins_type_cond[str(blk_pg_nr)]['lower'].append(ht.lines['l'])
660
+ if ht_list.index(ht_nr) == len(ht_list) - 1 and len(ht.mid_turn_lines) + len(ht.mid_winding_lines) + len(ht.mid_pole_lines) == 1: # higher angle external side insulation
661
+ if qh_side == 'h': _store_qh_data('external', blk_pg_nr, ht.lines['h'])
662
+ else: self.ins_type_cond[str(blk_pg_nr)]['higher'].append(ht.lines['h'])
663
+
664
+ # Wedges
665
+ for wdg_pg_nr, wdg_pg in pg.wedges.items():
666
+ ts_groups = {'aux': [ts_name for ts_name in ins_th.mid_layer if 'w' + str(wdg_pg_nr) in ts_name.split('_')],
667
+ 'mid_layer': [ts_name for ts_name in ins_th.mid_layer if 'w' + str(wdg_pg_nr) == ts_name.split('_')[0]]}
668
+ self.ins_type_cond['w' + str(wdg_pg_nr)] = {'inner': [], 'outer': [], 'higher': [], 'lower': [], 'mid_turn': []}
669
+ if wdg_pg.aux_lines: _store_ts_tags(wdg_pg, geom_ts_name='mid_layers_aux', ts_grp='aux', lines='aux_lines')
670
+ if not wdg_pg.mid_layer_lines.inner: self.ins_type_cond['w' + str(wdg_pg_nr)]['inner'].append(wdg_pg.lines['i'])
671
+ if wdg_pg.mid_layer_lines.outer:
672
+ for line_name, line_tag in wdg_pg.mid_layer_lines.outer.items():
673
+ for ts_name in ts_groups['mid_layer']:
674
+ if ts_name in self.md.geometries.thin_shells.mid_layers_wdg_to_ht:
675
+ ts_lines = self.md.geometries.thin_shells.mid_layers_wdg_to_ht[ts_name].lines
676
+ elif ts_name in self.md.geometries.thin_shells.mid_layers_wdg_to_wdg:
677
+ ts_lines = self.md.geometries.thin_shells.mid_layers_wdg_to_wdg[ts_name].lines
678
+ else: ts_lines = []
679
+ if line_name in ts_lines:
680
+ if ts_name not in self.ins_type['mid_layer']: self.ins_type['mid_layer'][ts_name] = []
681
+ self.ins_type['mid_layer'][ts_name].append(line_tag)
682
+ break
683
+ else: self.ins_type_cond['w' + str(wdg_pg_nr)]['outer'].append(wdg_pg.lines['o'])
684
+
685
+ # Collect common thin shells for double qh mid-layers
686
+ for qh_nr, ts_groups in self.ins_type_qh['internal'].items():
687
+ for qh_nr2, ts_groups2 in self.ins_type_qh['internal'].items():
688
+ if qh_nr != qh_nr2:
689
+ common_ts_groups = list(set(ts_groups) & set(ts_groups2))
690
+ for ts in common_ts_groups:
691
+ tags, tags2 = ts_groups[ts], ts_groups2[ts]
692
+ common_tags = list(set(tags) & set(tags2))
693
+ for tag in common_tags:
694
+ tags.remove(tag), tags2.remove(tag)
695
+ if self.qh_data[qh_nr2][ts]['ht_side'] == 'i': qh_name = str(qh_nr) + '_' + str(qh_nr2)
696
+ else: qh_name = str(qh_nr2) + '_' + str(qh_nr)
697
+ if qh_name not in self.ins_type_qh['internal_double']: self.ins_type_qh['internal_double'][qh_name] = {}
698
+ qh_ins_id = self.ins_type_qh['internal_double'][qh_name]
699
+ if ts not in qh_ins_id: qh_ins_id[ts] = []
700
+ qh_ins_id[ts].append(tag)
209
701
 
210
- pg.curves['air_inf'] = gmsh.model.addPhysicalGroup(1, [self.md.geometries.air_inf.lines['outer']])
211
- gmsh.model.setPhysicalName(1, pg.curves['air_inf'], 'air_inf')
212
-
213
- def assignRegionsTags(self):
214
- self.rm.air_far_field.vol.radius_out = float(gmsh.model.getValue(0, gmsh.model.getAdjacencies(
215
- 1, self.md.geometries.air_inf.lines['outer'])[1][0], []).max())
216
- self.rm.air_far_field.vol.radius_in = float(gmsh.model.getValue(0, gmsh.model.getAdjacencies(
217
- 1, self.md.geometries.air_inf.lines['inner'])[1][0], []).max())
218
-
219
- self.rm.air.vol.name = "Air"
220
- self.rm.air.vol.number = self.md.domains.physical_groups.surfaces['air']
221
- self.rm.air_far_field.vol.names = ["AirInf"]
222
- self.rm.air_far_field.vol.numbers = [self.md.domains.physical_groups.surfaces['air_inf']]
223
- self.rm.powered['Multipole'] = Reg_Mod_FiQ.Powered()
224
- self.rm.powered['Multipole'].vol.names = []
225
- self.rm.powered['Multipole'].vol.numbers = []
226
- self.rm.iron.vol.names = []
227
- self.rm.iron.vol.numbers = []
228
- self.rm.induced['Multipole'] = Reg_Mod_FiQ.Induced()
229
- self.rm.induced['Multipole'].vol.names = []
230
- self.rm.induced['Multipole'].vol.numbers = []
231
- for group_name, surface in self.md.domains.physical_groups.surfaces.items():
232
- if group_name[:5] == 'block':
233
- self.rm.powered['Multipole'].vol.names.append(group_name)
234
- self.rm.powered['Multipole'].vol.numbers.append(surface)
235
- # self.fd.half_turns.cross_sections.append(self.conductor_areas[name])
236
- elif group_name[:2] == 'BH':
237
- self.rm.iron.vol.names.append(group_name)
238
- self.rm.iron.vol.numbers.append(surface)
239
- elif group_name[:5] == 'wedge':
240
- self.rm.induced['Multipole'].vol.names.append(group_name)
241
- self.rm.induced['Multipole'].vol.numbers.append(surface)
242
-
243
- self.rm.air_far_field.surf.name = "Surface_Inf"
244
- self.rm.air_far_field.surf.number = self.md.domains.physical_groups.curves['air_inf']
245
-
246
- def setMeshOptions(self):
702
+ def assignRegionsTags(self, geometry, mesh):
703
+ def _get_input_insulation_data(i_name, i_type=None):
704
+ ow_idx = next((index for index, couple in enumerate(self.data.magnet.solve.thermal.insulation_TSA.block_to_block.blocks_connection_overwrite)
705
+ if all(element in couple for element in i_name.split('_'))), None)
706
+ if i_type == 'mid_winding': mid_mat, mid_th = [self.data.magnet.solve.wedges.material], []
707
+ elif ow_idx is not None:
708
+ mid_mat = self.data.magnet.solve.thermal.insulation_TSA.block_to_block.materials_overwrite[ow_idx]
709
+ mid_th = self.data.magnet.solve.thermal.insulation_TSA.block_to_block.thicknesses_overwrite[ow_idx]
710
+ else: mid_mat, mid_th = [self.data.magnet.solve.thermal.insulation_TSA.block_to_block.material], []
711
+ return mid_mat, mid_th
712
+
713
+ def _compute_insulation_thicknesses(tot_th, known_ins_th):
714
+ if not mid_thicknesses:
715
+ mid_lyrs = [Func.sig_dig(tot_th - known_ins_th) / len(mid_materials)] * len(mid_materials)
716
+ elif None in mid_thicknesses:
717
+ input_ths = sum([th for th in mid_thicknesses if th is not None])
718
+ mid_lyrs = [th if th is not None else Func.sig_dig(tot_th - known_ins_th - input_ths) / mid_thicknesses.count(None) for th in mid_thicknesses]
719
+ else: mid_lyrs = mid_thicknesses
720
+ zeros = [nbr for nbr, th in enumerate(mid_lyrs) if th < 1e-8]
721
+ if tot_th - known_ins_th - sum(mid_lyrs) < -1e-8:
722
+ raise ValueError("Layer-to-layer insulation exceeds the space between blocks: check 'solve'->'insulation_TSA'->'block_to_block'->'thicknesses_overwrite'")
723
+ else: return mid_lyrs, zeros
724
+
725
+ pg = self.md.domains.physical_groups
726
+ qh = self.data.quench_protection.quench_heaters
727
+
728
+ # Air and air far field
729
+ if 'bore_field' in mesh.dict():
730
+ self.rm.air_far_field.vol.radius_out = float(abs(max(gmsh.model.getValue(0, gmsh.model.getAdjacencies(
731
+ 1, self.md.geometries.air_inf.lines['outer'])[1][0], []), key=abs)))
732
+ self.rm.air_far_field.vol.radius_in = float(abs(max(gmsh.model.getValue(0, gmsh.model.getAdjacencies(
733
+ 1, self.md.geometries.air_inf.lines['inner'])[1][0], []), key=abs)))
734
+ self.rm.air.vol.name = "Air"
735
+ self.rm.air.vol.number = pg.air
736
+ self.rm.air_far_field.vol.names = ["AirInf"]
737
+ self.rm.air_far_field.vol.numbers = [pg.air_inf]
738
+ self.rm.air_far_field.surf.name = "Surface_Inf"
739
+ self.rm.air_far_field.surf.number = pg.air_inf_bnd
740
+ if geometry.dict().get('symmetry', 'none') != 'none':
741
+ self.rm.boundaries.symmetry.normal_free.name = 'normal_free_bnd'
742
+ self.rm.boundaries.symmetry.normal_free.number = pg.symmetric_boundaries.normal_free
743
+ self.rm.boundaries.symmetry.tangential_free.name = 'tangent_free_bnd'
744
+ self.rm.boundaries.symmetry.tangential_free.number = pg.symmetric_boundaries.tangential_free
745
+
746
+ if 'use_TSA' in geometry.dict():
747
+ self.rm.projection_points.name = 'projection_points'
748
+ self.rm.projection_points.number = self.md.domains.physical_groups.half_turns_points
749
+
750
+ # Initialize lists
751
+ for group in ['r1_a1', 'r2_a1', 'r1_a2', 'r2_a2']:
752
+ self.rm.powered[group] = rM.Powered()
753
+ self.rm.powered[group].vol.names = []
754
+ self.rm.powered[group].vol.numbers = []
755
+ for cond_name in self.data.conductors.keys(): self.rm.powered[group].conductors[cond_name] = []
756
+ if geometry.with_wedges:
757
+ self.rm.induced[group] = rM.Induced()
758
+ self.rm.induced[group].vol.names = []
759
+ self.rm.induced[group].vol.numbers = []
760
+ if 'bore_field' in mesh.dict():
761
+ initial_current = self.data.power_supply.I_initial
762
+ self.rm.powered[group].vol.currents = []
763
+ if geometry.dict().get('use_TSA', False):
764
+ self.rm.powered[group].surf_in.names = []
765
+ self.rm.powered[group].surf_in.numbers = []
766
+ self.rm.powered[group].surf_out.names = []
767
+ self.rm.powered[group].surf_out.numbers = []
768
+ if geometry.with_wedges:
769
+ self.rm.induced[group].surf_in.names = []
770
+ self.rm.induced[group].surf_in.numbers = []
771
+ self.rm.induced[group].surf_out.names = []
772
+ self.rm.induced[group].surf_out.numbers = []
773
+ if geometry.with_iron_yoke:
774
+ self.rm.iron.vol.names = []
775
+ self.rm.iron.vol.numbers = []
776
+ if geometry.dict().get('use_TSA', False):
777
+ unique_thin_shells = []
778
+ self.rm.thin_shells.second_group_is_next['azimuthally'] = []
779
+ self.rm.thin_shells.second_group_is_next['radially'] = []
780
+ self.rm.thin_shells.normals_directed['azimuthally'] = []
781
+ self.rm.thin_shells.normals_directed['radially'] = []
782
+ else:
783
+ self.rm.insulator.vol.names = []
784
+ self.rm.insulator.vol.numbers = []
785
+ self.rm.insulator.surf.names = []
786
+ self.rm.insulator.surf.numbers = []
787
+
788
+ if geometry.dict().get('use_TSA', False):
789
+ # Categorize insulation types
790
+ min_h = mesh.insulation.global_size
791
+ # min_h = 1
792
+ # for conductor in self.data.conductors.keys():
793
+ # min_h = min([self.data.conductors[conductor].cable.th_insulation_along_height,
794
+ # self.data.conductors[conductor].cable.th_insulation_along_width, min_h])
795
+ min_h_QH = mesh.insulation.TSA.global_size_QH if mesh.insulation.TSA.global_size_QH else min_h
796
+
797
+ # Conductor insulation layers
798
+ for el, ins in self.ins_type_cond.items():
799
+ cond = self.data.conductors[self.wedge_cond[int(el[1:])]].cable if 'w' in el else self.data.conductors[pg.blocks[int(el)].conductor].cable
800
+ for ins_side, tags in ins.items():
801
+ if tags:
802
+ side_ins_type = [cond.material_insulation]
803
+ if ins_side in ['inner', 'outer']: side_ins = [cond.th_insulation_along_width]
804
+ elif ins_side in ['higher', 'lower']: side_ins = [cond.th_insulation_along_height]
805
+ else: # mid_turn
806
+ side_ins = [cond.th_insulation_along_height, cond.th_insulation_along_height]
807
+ side_ins_type.append(cond.material_insulation)
808
+ if ins_side[0] in 'iohl' and el + ins_side[0] in self.data.magnet.solve.thermal.insulation_TSA.exterior.blocks:
809
+ add_mat_idx = self.data.magnet.solve.thermal.insulation_TSA.exterior.blocks.index(el + ins_side[0])
810
+ side_ins.extend(self.data.magnet.solve.thermal.insulation_TSA.exterior.thicknesses_append[add_mat_idx])
811
+ side_ins_type.extend(self.data.magnet.solve.thermal.insulation_TSA.exterior.materials_append[add_mat_idx])
812
+ if ins_side[0] in 'il': side_ins.reverse(), side_ins_type.reverse()
813
+ self.rm.thin_shells.insulation_types.layers_number.append(0)
814
+ self.rm.thin_shells.insulation_types.thin_shells.append(list(set(tags)))
815
+ self.rm.thin_shells.insulation_types.thicknesses.append([])
816
+ self.rm.thin_shells.insulation_types.layers_material.append([])
817
+ for nr, ins_lyr in enumerate(side_ins):
818
+ tsa_layers = max(mesh.insulation.TSA.minimum_discretizations, round(ins_lyr / min_h))
819
+ self.rm.thin_shells.insulation_types.layers_number[-1] += tsa_layers
820
+ self.rm.thin_shells.insulation_types.thicknesses[-1].extend([ins_lyr / tsa_layers] * tsa_layers)
821
+ self.rm.thin_shells.insulation_types.layers_material[-1].extend([side_ins_type[nr]] * tsa_layers)
822
+
823
+ # Mid-pole, mid-winding, and mid-layer insulation layers
824
+ ins_th_dict = self.md.geometries.thin_shells.ins_thickness.dict()
825
+ for ins_type, ins in self.ins_type.items():
826
+ for ins_name, tags in ins.items():
827
+ # Get conductors insulation
828
+ if ins_name.count('w') == 2:
829
+ el1, el2 = int(ins_name[1:ins_name.index('_')]), int(ins_name[ins_name.index('_') + 2:])
830
+ cond1 = self.data.conductors[self.wedge_cond[el1]].cable
831
+ cond2 = self.data.conductors[self.wedge_cond[el2]].cable
832
+ elif ins_name.count('w') == 1:
833
+ el1, el2 = int(ins_name[1:ins_name.index('_')]), int(ins_name[ins_name.index('_') + 1:])
834
+ cond1 = self.data.conductors[self.wedge_cond[el1]].cable
835
+ cond2 = self.data.conductors[pg.blocks[el2].conductor].cable
836
+ if ins_name in self.md.geometries.thin_shells.mid_layers_ht_to_wdg: cond1, cond2 = cond2, cond1
837
+ else:
838
+ el1, el2 = int(ins_name[:ins_name.index('_')]), int(ins_name[ins_name.index('_') + 1:])
839
+ cond1 = self.data.conductors[pg.blocks[el1].conductor].cable
840
+ cond2 = self.data.conductors[pg.blocks[el2].conductor].cable
841
+ if ins_type in ['mid_layer', 'aux']:
842
+ cond_ins1, cond_ins2 = cond1.th_insulation_along_width, cond2.th_insulation_along_width
843
+ else:
844
+ cond_ins1, cond_ins2 = cond1.th_insulation_along_height, cond2.th_insulation_along_height
845
+ # Get insulation layer thickness
846
+ mid_materials, mid_thicknesses = _get_input_insulation_data(ins_name, i_type=ins_type)
847
+ ins_thickness = ins_th_dict['mid_layer'][ins_name] / 2 if ins_type == 'aux' else ins_th_dict[ins_type][ins_name]
848
+ mid_lyr_th, null_idx = _compute_insulation_thicknesses(ins_thickness, cond_ins1 + cond_ins2)
849
+ for idx in null_idx: mid_lyr_th.pop(idx), mid_materials.pop(idx)
850
+ side_ins = [cond_ins1] + mid_lyr_th + [cond_ins2]
851
+ # Get insulation materials
852
+ side_ins_type = [cond1.material_insulation] + mid_materials + [cond2.material_insulation]
853
+ # Initialize sublists
854
+ self.rm.thin_shells.insulation_types.layers_number.append(0)
855
+ self.rm.thin_shells.insulation_types.thin_shells.append(list(set(tags)))
856
+ self.rm.thin_shells.insulation_types.thicknesses.append([])
857
+ self.rm.thin_shells.insulation_types.layers_material.append([])
858
+ for nr, ins_lyr in enumerate(side_ins):
859
+ tsa_layers = max(mesh.insulation.TSA.minimum_discretizations, round(ins_lyr / min_h))
860
+ self.rm.thin_shells.insulation_types.layers_number[-1] += tsa_layers
861
+ self.rm.thin_shells.insulation_types.thicknesses[-1].extend([ins_lyr / tsa_layers] * tsa_layers)
862
+ self.rm.thin_shells.insulation_types.layers_material[-1].extend([side_ins_type[nr]] * tsa_layers)
863
+
864
+ # Quench heater insulation layers
865
+ for ins_type, ins in self.ins_type_qh.items():
866
+ for qh_nr, ts_groups in ins.items():
867
+ for ts_name, tags in ts_groups.items():
868
+ if ins_type != 'external': mid_materials, mid_thicknesses = _get_input_insulation_data(ts_name)
869
+ if tags:
870
+ if ins_type == 'external':
871
+ data = self.qh_data[qh_nr]
872
+ if str(ts_name) + data[ts_name]['ht_side'] in self.data.magnet.solve.thermal.insulation_TSA.exterior.blocks:
873
+ add_mat_idx = self.data.magnet.solve.thermal.insulation_TSA.exterior.blocks.index(str(ts_name) + data[ts_name]['ht_side'])
874
+ additional_ths = self.data.magnet.solve.thermal.insulation_TSA.exterior.thicknesses_append[add_mat_idx]
875
+ additional_mats = self.data.magnet.solve.thermal.insulation_TSA.exterior.materials_append[add_mat_idx]
876
+ else: additional_ths, additional_mats = [], []
877
+ cond = self.data.conductors[data[ts_name]['conductor']].cable
878
+ ht_ins_th = cond.th_insulation_along_width if data[ts_name]['ht_side'] in 'io' else cond.th_insulation_along_height
879
+ side_ins = [ht_ins_th] + [h_ins for h_ins in qh.h_ins[qh_nr - 1]] + [qh.h[qh_nr - 1]] + [h_ground_ins for h_ground_ins in qh.h_ground_ins[qh_nr - 1]] + additional_ths
880
+ side_ins_type = [cond.material_insulation] + [type_ins for type_ins in qh.type_ins[qh_nr - 1]] + ['SS'] +\
881
+ [type_ground_ins for type_ground_ins in qh.type_ground_ins[qh_nr - 1]] + additional_mats
882
+ if data[ts_name]['ht_side'] in 'il': side_ins.reverse(), side_ins_type.reverse()
883
+ qh_list = [qh_nr]
884
+ elif ins_type == 'internal':
885
+ data = self.qh_data[qh_nr]
886
+ cond = self.data.conductors[data[ts_name]['conductor']].cable
887
+ cond2 = self.data.conductors[self.wedge_cond[int(ts_name.split('_')[0][1:])] if 'w' in ts_name
888
+ else pg.blocks[int(ts_name.split('_')[1]) if data[ts_name]['ht_side'] == 'o' else int(ts_name.split('_')[0])].conductor].cable
889
+ side_ins_qh = [h_ins for h_ins in qh.h_ins[qh_nr - 1]] + [qh.h[qh_nr - 1]] + [h_ground_ins for h_ground_ins in qh.h_ground_ins[qh_nr - 1]]
890
+ mid_lyr_th, null_idx = _compute_insulation_thicknesses(
891
+ ins_th_dict['mid_layer'][ts_name], sum([cond.th_insulation_along_width, cond2.th_insulation_along_width] + side_ins_qh))
892
+ for idx in null_idx: mid_lyr_th.pop(idx), mid_materials.pop(idx)
893
+ side_ins = [cond.th_insulation_along_width] + side_ins_qh + mid_lyr_th + [cond2.th_insulation_along_width]
894
+ side_ins_type = [cond.material_insulation] + [type_ins for type_ins in qh.type_ins[qh_nr - 1]] + ['SS'] +\
895
+ [type_ground_ins for type_ground_ins in qh.type_ground_ins[qh_nr - 1]] + mid_materials + [cond2.material_insulation]
896
+ if data[ts_name]['ht_side'] == 'i': side_ins.reverse(), side_ins_type.reverse()
897
+ qh_list = [qh_nr]
898
+ elif ins_type == 'internal_double':
899
+ qh_nr1, qh_nr2 = int(qh_nr.split('_')[0]), int(qh_nr.split('_')[1])
900
+ data, data2 = self.qh_data[qh_nr1], self.qh_data[qh_nr2]
901
+ cond = self.data.conductors[data[ts_name]['conductor']].cable
902
+ cond2 = self.data.conductors[data2[ts_name]['conductor']].cable
903
+ side_ins_qh = [h_ins for h_ins in qh.h_ins[qh_nr1 - 1]] + [qh.h[qh_nr1 - 1]] + [h_ground_ins for h_ground_ins in qh.h_ground_ins[qh_nr1 - 1]]
904
+ side_ins_qh2 = [h_ground_ins for h_ground_ins in qh.h_ground_ins[qh_nr2 - 1][::-1]] + [qh.h[qh_nr2 - 1]] + [h_ins for h_ins in qh.h_ins[qh_nr2 - 1][::-1]]
905
+ mid_lyr_th, null_idx = _compute_insulation_thicknesses(
906
+ ins_th_dict['mid_layer'][ts_name], sum([cond.th_insulation_along_width, cond2.th_insulation_along_width] + side_ins_qh + side_ins_qh2))
907
+ for idx in null_idx: mid_lyr_th.pop(idx), mid_materials.pop(idx)
908
+ side_ins = [cond.th_insulation_along_width] + side_ins_qh + mid_lyr_th + side_ins_qh2 + [cond2.th_insulation_along_width]
909
+ side_ins_type = [cond.material_insulation] + [type_ins for type_ins in qh.type_ins[qh_nr1 - 1]] + ['SS'] +\
910
+ [type_ground_ins for type_ground_ins in qh.type_ground_ins[qh_nr1 - 1]] + mid_materials +\
911
+ [type_ground_ins for type_ground_ins in qh.type_ground_ins[qh_nr2 - 1][::-1]] + ['SS'] +\
912
+ [type_ins for type_ins in qh.type_ins[qh_nr2 - 1][::-1]] + [cond2.material_insulation]
913
+ qh_list = [qh_nr1, qh_nr2]
914
+ qh_labels = [1 if m == 'SS' else None for m in side_ins_type]
915
+ ss_indexes = [index for index, value in enumerate(qh_labels) if value == 1]
916
+ for nr, idx in enumerate(ss_indexes): qh_labels[idx] = qh_list[nr]
917
+ self.rm.thin_shells.quench_heaters.layers_number.append(0)
918
+ self.rm.thin_shells.quench_heaters.thin_shells.append(list(set(tags)))
919
+ self.rm.thin_shells.quench_heaters.thicknesses.append([])
920
+ self.rm.thin_shells.quench_heaters.layers_material.append([])
921
+ self.rm.thin_shells.quench_heaters.label.append([])
922
+ for nr, ins_lyr in enumerate(side_ins):
923
+ tsa_layers = max(mesh.insulation.TSA.minimum_discretizations_QH, round(ins_lyr / min_h_QH))
924
+ self.rm.thin_shells.quench_heaters.layers_number[-1] += tsa_layers
925
+ self.rm.thin_shells.quench_heaters.thicknesses[-1].extend([ins_lyr / tsa_layers] * tsa_layers)
926
+ self.rm.thin_shells.quench_heaters.layers_material[-1].extend([side_ins_type[nr]] * tsa_layers)
927
+ self.rm.thin_shells.quench_heaters.label[-1].extend([qh_labels[nr]] * tsa_layers)
928
+
929
+ # Powered
930
+ for blk_nr, blk in pg.blocks.items():
931
+ ht_list = list(blk.half_turns.keys())
932
+ for ht_nr, ht in blk.half_turns.items():
933
+ ht_name = f"ht{ht_nr}_{'EM' if 'bore_field' in mesh.dict() else 'TH'}"
934
+ self.rm.powered[ht.group].conductors[blk.conductor].append(ht_name)
935
+ self.rm.powered[ht.group].vol.names.append(ht_name)
936
+ self.rm.powered[ht.group].vol.numbers.append(ht.tag)
937
+ if 'bore_field' in mesh.dict():
938
+ self.rm.powered[ht.group].vol.currents.append(initial_current * (1 if blk.current_sign > 0 else -1))
939
+ if geometry.dict().get('use_TSA', False):
940
+ for line in ['l', 'i', 'o', 'h']:
941
+ # Bare edges
942
+ self.rm.powered[ht.group].surf_in.names.append(ht_name + line)
943
+ self.rm.powered[ht.group].surf_in.numbers.append(ht.lines[line])
944
+ if line in 'io': self.rm.thin_shells.normals_directed['radially'].append(ht.lines[line])
945
+ else: self.rm.thin_shells.normals_directed['azimuthally'].append(ht.lines[line])
946
+ # Auxiliary thin shells
947
+ for line_name, line_tag in ht.aux_lines.items():
948
+ self.rm.powered[ht.group].surf_in.names.append(line_name)
949
+ self.rm.powered[ht.group].surf_in.numbers.append(line_tag)
950
+ self.rm.thin_shells.normals_directed['radially'].append(line_tag)
951
+ # Thin shells
952
+ for line_name, line_tag in dict(ht.mid_layer_lines.inner, **ht.mid_layer_lines.outer).items():
953
+ self.rm.powered[ht.group].surf_out.names.append(line_name)
954
+ self.rm.powered[ht.group].surf_out.numbers.append(line_tag)
955
+ self.rm.thin_shells.normals_directed['radially'].append(line_tag)
956
+ for line_name, line_tag in dict(ht.mid_pole_lines, **ht.mid_winding_lines, **ht.mid_turn_lines).items():
957
+ self.rm.powered[ht.group].surf_out.names.append(line_name)
958
+ self.rm.powered[ht.group].surf_out.numbers.append(line_tag)
959
+ self.rm.thin_shells.normals_directed['azimuthally'].append(line_tag)
960
+
961
+ # Which thin shells or exterior conductor edges precede a second group (r2 or a2)
962
+ if ht.group[1] == '2':
963
+ # mid-turn thin shells precede r2
964
+ for line_name, line_tag in ht.mid_turn_lines.items():
965
+ if (ht_list.index(ht_nr) != 0 and int(line_name[line_name.index('_') + 1:]) == ht_nr) or\
966
+ (ht_list.index(ht_nr) == 0 and 'w' in line_name):
967
+ self.rm.thin_shells.second_group_is_next['azimuthally'].append(line_tag)
968
+ # mid-pole thin shells precede r2
969
+ if ht_list.index(ht_nr) == 0 and ht.mid_pole_lines:
970
+ self.rm.thin_shells.second_group_is_next['azimuthally'].append(list(ht.mid_pole_lines.values())[0])
971
+ # conductor edges precede r2
972
+ elif ht_list.index(ht_nr) == 0 and len(ht.mid_turn_lines) + len(ht.mid_winding_lines) + len(ht.mid_pole_lines) == 1:
973
+ self.rm.thin_shells.second_group_is_next['azimuthally'].append(ht.lines['l'])
974
+ # mid-winding thin shells precede r2
975
+ for line_name, line_tag in ht.mid_winding_lines.items():
976
+ if int(line_name[line_name.index('_') + 1:]) == ht_nr:
977
+ self.rm.thin_shells.second_group_is_next['azimuthally'].append(line_tag)
978
+ elif ht_list.index(ht_nr) == len(ht_list) - 1:
979
+ # mid-turn thin shells precede r2
980
+ for line_name, line_tag in ht.mid_turn_lines.items():
981
+ if 'w' in line_name:
982
+ self.rm.thin_shells.second_group_is_next['azimuthally'].append(line_tag)
983
+ # conductor edges precede r2
984
+ if len(ht.mid_turn_lines) + len(ht.mid_winding_lines) + len(ht.mid_pole_lines) == 1:
985
+ self.rm.thin_shells.second_group_is_next['azimuthally'].append(ht.lines['h'])
986
+ if ht.group[4] == '2':
987
+ # mid-layer thin shells precede a2
988
+ for line_name, line_tag in ht.mid_layer_lines.inner.items():
989
+ self.rm.thin_shells.second_group_is_next['radially'].append(line_tag)
990
+ elif not ht.mid_layer_lines.outer:
991
+ # conductor edges precede a2
992
+ self.rm.thin_shells.second_group_is_next['radially'].append(ht.lines['o'])
993
+
994
+ if geometry.dict().get('use_TSA', False):
995
+ for group in ['r1_a1', 'r2_a1', 'r1_a2', 'r2_a2']:
996
+ unique_thin_shells.extend(self.rm.powered[group].surf_out.numbers)
997
+
998
+ # Wedges
999
+ if geometry.with_wedges:
1000
+ for wdg_nr, wdg in pg.wedges.items():
1001
+ wdg_name = f"w{wdg_nr}_{'EM' if 'bore_field' in mesh.dict() else 'TH'}"
1002
+ self.rm.induced[wdg.group].vol.names.append(wdg_name)
1003
+ self.rm.induced[wdg.group].vol.numbers.append(wdg.tag)
1004
+ if geometry.dict().get('use_TSA', False):
1005
+ # Bare edges
1006
+ for line in ['l', 'i', 'o', 'h']:
1007
+ self.rm.induced[wdg.group].surf_in.names.append(wdg_name + line)
1008
+ self.rm.induced[wdg.group].surf_in.numbers.append(wdg.lines[line])
1009
+ if line in 'io':
1010
+ self.rm.thin_shells.normals_directed['radially'].append(wdg.lines[line])
1011
+ else:
1012
+ self.rm.thin_shells.normals_directed['azimuthally'].append(wdg.lines[line])
1013
+ # Auxiliary thin shells
1014
+ for line_name, line_tag in wdg.aux_lines.items():
1015
+ self.rm.induced[wdg.group].surf_in.names.append(line_name)
1016
+ self.rm.induced[wdg.group].surf_in.numbers.append(line_tag)
1017
+ self.rm.thin_shells.normals_directed['radially'].append(line_tag)
1018
+ # Thin shells
1019
+ for line_name, line_tag in dict(wdg.mid_layer_lines.inner, **wdg.mid_layer_lines.outer).items():
1020
+ self.rm.induced[wdg.group].surf_out.names.append(line_name)
1021
+ self.rm.induced[wdg.group].surf_out.numbers.append(line_tag)
1022
+ self.rm.thin_shells.normals_directed['radially'].append(line_tag)
1023
+ for line_name, line_tag in wdg.mid_turn_lines.items():
1024
+ self.rm.induced[wdg.group].surf_out.names.append(line_name)
1025
+ self.rm.induced[wdg.group].surf_out.numbers.append(line_tag)
1026
+ self.rm.thin_shells.normals_directed['azimuthally'].append(line_tag)
1027
+ # Which thin shells or exterior conductor edges precede a second group (r2 or a2)
1028
+ if wdg.group[4] == '2':
1029
+ for line_name, line_tag in wdg.mid_layer_lines.inner.items():
1030
+ if line_name.count('w') == 2:
1031
+ self.rm.thin_shells.second_group_is_next['radially'].append(line_tag)
1032
+ elif not wdg.mid_layer_lines.outer:
1033
+ self.rm.thin_shells.second_group_is_next['radially'].append(wdg.lines['o'])
1034
+ if geometry.dict().get('use_TSA', False):
1035
+ for group in ['r1_a1', 'r2_a1', 'r1_a2', 'r2_a2']:
1036
+ unique_thin_shells.extend(self.rm.induced[group].surf_out.numbers)
1037
+
1038
+ # Unique mid layers
1039
+ if geometry.dict().get('use_TSA', False):
1040
+ self.rm.thin_shells.mid_turns_layers_poles = list(set(unique_thin_shells))
1041
+
1042
+ # Insulation
1043
+ for group_name, surface in pg.insulations.surfaces.items():
1044
+ self.rm.insulator.vol.names.append('ins' + group_name)
1045
+ self.rm.insulator.vol.numbers.append(surface)
1046
+ if 'insulation' in mesh.dict() and 'TSA' in mesh.dict()["insulation"]:
1047
+ for group_name, curve in pg.insulations.curves.items():
1048
+ self.rm.insulator.surf.names.append('ins' + group_name)
1049
+ self.rm.insulator.surf.numbers.append(curve)
1050
+
1051
+ # Iron
1052
+ for group_name, surface in pg.iron.surfaces.items():
1053
+ self.rm.iron.vol.names.append(group_name)
1054
+ self.rm.iron.vol.numbers.append(surface)
1055
+
1056
+ # Boundary conditions
1057
+ if 'insulation' in mesh.dict() and 'TSA' in mesh.dict()["insulation"]:
1058
+ # Initialize lists
1059
+ for bc_data, bc_rm in zip(self.data.magnet.solve.thermal.overwrite_boundary_conditions, self.rm.boundaries.thermal): # b.c. type
1060
+ bc_rm[1].bc.names = []
1061
+ bc_rm[1].bc.numbers = []
1062
+ if bc_data[0] == 'cooling':
1063
+ bc_rm[1].bc.values = []
1064
+ for group in ['1_r1_a1', '2_r1_a1', '1_r2_a1', '2_r2_a1', '1_r1_a2', '2_r1_a2', '1_r2_a2', '2_r2_a2']:
1065
+ bc_rm[1].groups[group] = []
1066
+ else:
1067
+ bc_rm[1].bc.value = []
1068
+ for group in ['r1_a1', 'r2_a1', 'r1_a2', 'r2_a2']:
1069
+ bc_rm[1].groups[group] = []
1070
+
1071
+ # Apply general cooling and adiabatic
1072
+ if self.data.magnet.solve.thermal.He_cooling.enabled:
1073
+ cooling_side = {'i': any(coil_side in self.data.magnet.solve.thermal.He_cooling.sides for coil_side in ['inner', 'external']),
1074
+ 'o': any(coil_side in self.data.magnet.solve.thermal.He_cooling.sides for coil_side in ['outer', 'external']),
1075
+ 'hl': self.data.magnet.solve.thermal.He_cooling.sides == 'external'}
1076
+ else:
1077
+ cooling_side = {'i': False, 'o': False, 'hl': False}
1078
+
1079
+ def __assign_bnd_tag(el, name, side, bc_type, tag=None):
1080
+ line_tag = tag if tag else el.lines[side]
1081
+ bnd_list_names[bc_type].append(name + side)
1082
+ bnd_list_numbers[bc_type].append(line_tag)
1083
+ if side in 'io': new_group = el.group[:3] + 'a1' if el.group[4] == '2' else el.group[:3] + 'a2'
1084
+ else: new_group = 'r1' + el.group[2:] if el.group[1] == '2' else 'r2' + el.group[2:]
1085
+ bc_rm[bc_type].groups[new_group].append(line_tag)
1086
+ for group_name, group in self.rm.thin_shells.groups.items():
1087
+ if line_tag in group:
1088
+ bc_rm[bc_type].groups[el.group[0] + '_' + new_group].append(line_tag)
1089
+ break
1090
+
1091
+ bc_rm = {'Robin': self.rm.boundaries.thermal.cooling, 'Neumann': self.rm.boundaries.thermal.heat_flux}
1092
+ bnd_list_names = {'Robin': [], 'Neumann': []}
1093
+ bnd_list_numbers = {'Robin': [], 'Neumann': []}
1094
+ if geometry.dict().get('use_TSA', False):
1095
+ # Half turn boundaries
1096
+ for coil_nr, coil in self.md.geometries.coil.anticlockwise_order.coils.items():
1097
+ for lyr_nr, orders in coil.layers.items():
1098
+ for order in orders:
1099
+ ht_list = list(pg.blocks[order.block].half_turns.keys())
1100
+ for ht_nr, ht in pg.blocks[order.block].half_turns.items():
1101
+ if not ht.mid_layer_lines.inner:
1102
+ __assign_bnd_tag(ht, 'ht' + str(ht_nr), 'i', 'Robin' if cooling_side['i'] else 'Neumann')
1103
+ if not ht.mid_layer_lines.outer:
1104
+ __assign_bnd_tag(ht, 'ht' + str(ht_nr), 'o', 'Robin' if cooling_side['o'] else 'Neumann')
1105
+ if ht_list.index(ht_nr) == 0 and len(ht.mid_turn_lines) + len(ht.mid_winding_lines) + len(ht.mid_pole_lines) == 1:
1106
+ __assign_bnd_tag(ht, 'ht' + str(ht_nr), 'l', 'Robin' if cooling_side['hl'] else 'Neumann')
1107
+ if ht_list.index(ht_nr) == len(ht_list) - 1 and len(ht.mid_turn_lines) + len(ht.mid_winding_lines) + len(ht.mid_pole_lines) == 1:
1108
+ __assign_bnd_tag(ht, 'ht' + str(ht_nr), 'h', 'Robin' if cooling_side['hl'] else 'Neumann')
1109
+ if ht.aux_lines:
1110
+ __assign_bnd_tag(ht, 'ht' + str(ht_nr), 'o', 'Robin' if cooling_side['hl'] else 'Neumann', list(ht.aux_lines.values())[0])
1111
+ # Wedge boundaries
1112
+ for wdg_nr, wdg in pg.wedges.items():
1113
+ if not wdg.mid_layer_lines.inner:
1114
+ __assign_bnd_tag(wdg, 'wd' + str(wdg_nr), 'i', 'Robin' if cooling_side['i'] else 'Neumann')
1115
+ if not wdg.mid_layer_lines.outer:
1116
+ __assign_bnd_tag(wdg, 'wd' + str(wdg_nr), 'o', 'Robin' if cooling_side['o'] else 'Neumann')
1117
+ if wdg.aux_lines:
1118
+ __assign_bnd_tag(wdg, 'wd' + str(wdg_nr), 'o', 'Robin' if cooling_side['hl'] else 'Neumann', list(wdg.aux_lines.values())[0])
1119
+ else: # insulation case
1120
+ for curves_group, tag in pg.insulations.curves.items():
1121
+ if self.data.magnet.solve.thermal.He_cooling.enabled:
1122
+ if self.data.magnet.solve.thermal.He_cooling.sides == 'external': bc_type = 'Robin'
1123
+ else:
1124
+ raise ValueError(f"Cooling side '{self.data.magnet.solve.thermal.He_cooling.sides}' is not supported for meshed insulation models.")
1125
+ # bc_type = 'Robin' if (self.data.magnet.solve.thermal.He_cooling.sides == 'external' or
1126
+ # ('inner' in self.data.magnet.solve.thermal.He_cooling.sides and curves_group[-1] == 'i') or
1127
+ # ('outer' in self.data.magnet.solve.thermal.He_cooling.sides and curves_group[-1] == 'o')) else 'Neumann'
1128
+ else:
1129
+ bc_type = 'Neumann'
1130
+ bnd_list_names[bc_type].append('ins' + curves_group)
1131
+ bnd_list_numbers[bc_type].append(tag)
1132
+ if self.data.magnet.solve.thermal.He_cooling.enabled:
1133
+ bc_rm['Robin'].bc.names.append(bnd_list_names['Robin'])
1134
+ bc_rm['Robin'].bc.numbers.append(bnd_list_numbers['Robin'])
1135
+ bc_rm['Robin'].bc.values.append([self.data.magnet.solve.thermal.He_cooling.heat_transfer_coefficient,
1136
+ self.data.magnet.solve.thermal.init_temperature])
1137
+ if bnd_list_names['Neumann']:
1138
+ bc_rm['Neumann'].bc.names.append(bnd_list_names['Neumann'])
1139
+ bc_rm['Neumann'].bc.numbers.append(bnd_list_numbers['Neumann'])
1140
+ bc_rm['Neumann'].bc.value.append(0.)
1141
+
1142
+ # Apply specific boundary conditions
1143
+ for bc_data, bc_rm in zip(self.data.magnet.solve.thermal.overwrite_boundary_conditions, self.rm.boundaries.thermal): # b.c. type
1144
+ # bc_data is a tuple like: ('temperature', {'const_T1': boundaries, value)})
1145
+ # bc_rm is a tuple like: ('temperature', DirichletCondition(names, numbers, value))
1146
+
1147
+ for _, bc in bc_data[1].items(): # all boundary conditions of one b.c. type (e.g., Dirichlet with different temperatures)
1148
+ bnd_list_names = []
1149
+ bnd_list_numbers = []
1150
+ if geometry.dict().get('use_TSA', False):
1151
+ for bnd in bc.boundaries: # all boundaries of one boundary condition
1152
+ if bnd[0] == 'w':
1153
+ if not geometry.with_wedges:
1154
+ raise Exception('Wedge regions are disabled.')
1155
+ # Fetch the physical group of the wedge
1156
+ pg_el = pg.wedges[int(bnd[1:-1])]
1157
+ name = 'wd' + bnd[1:]
1158
+ else:
1159
+ # Fetch the physical group of the half turn
1160
+ ht_index = self.strands['ht'].index(int(bnd[:-1]))
1161
+ pg_el = pg.blocks[self.strands['block'][ht_index]].half_turns[int(bnd[:-1])]
1162
+ name = 'ht' + bnd
1163
+ line_pg_tag = pg_el.lines[bnd[-1]]
1164
+ bnd_list_names.append(name)
1165
+ bnd_list_numbers.append(line_pg_tag)
1166
+ # Find the half turn group this boundary is assigned to and take the complementary
1167
+ if bnd[-1] in 'io':
1168
+ new_group = pg_el.group[:3] + 'a1' if pg_el.group[4] == '2' else pg_el.group[:3] + 'a2'
1169
+ else: # ['l', 'h'] todo: if applied to an inner line (i.e., not domain boundaries), extra code needed because the line would belong to two groups
1170
+ new_group = 'r1' + pg_el.group[2:] if pg_el.group[1] == '2' else 'r2' + pg_el.group[2:]
1171
+ # Overwrite general cooling and adiabatic condition
1172
+ if self.data.magnet.solve.thermal.He_cooling.enabled:
1173
+ if name in self.rm.boundaries.thermal.cooling.bc.names[0]:
1174
+ bnd_idx = self.rm.boundaries.thermal.cooling.bc.names[0].index(name)
1175
+ self.rm.boundaries.thermal.cooling.bc.names[0].pop(bnd_idx)
1176
+ bnd_idx = self.rm.boundaries.thermal.cooling.bc.numbers[0].pop(bnd_idx)
1177
+ self.rm.boundaries.thermal.cooling.groups[new_group].pop(
1178
+ self.rm.boundaries.thermal.cooling.groups[new_group].index(bnd_idx))
1179
+ if self.data.magnet.solve.thermal.He_cooling.sides != 'external':
1180
+ if name in self.rm.boundaries.thermal.heat_flux.bc.names[0]:
1181
+ bnd_idx = self.rm.boundaries.thermal.heat_flux.bc.names[0].index(name)
1182
+ self.rm.boundaries.thermal.heat_flux.bc.names[0].pop(bnd_idx)
1183
+ bnd_idx = self.rm.boundaries.thermal.heat_flux.bc.numbers[0].pop(bnd_idx)
1184
+ self.rm.boundaries.thermal.heat_flux.groups[new_group].pop(
1185
+ self.rm.boundaries.thermal.heat_flux.groups[new_group].index(bnd_idx))
1186
+ # Assign the tag
1187
+ bc_rm[1].groups[new_group].append(line_pg_tag)
1188
+ # Extra grouping for Robin virtual shells
1189
+ if bc_data[0] == 'cooling':
1190
+ for group_name, group in self.rm.thin_shells.groups.items():
1191
+ if line_pg_tag in group:
1192
+ bc_rm[1].groups[group_name[0] + '_' + new_group].append(line_pg_tag)
1193
+ break
1194
+ else: # the b.c. are assigned to insulation boundaries instead
1195
+ pass # todo: not supported yet
1196
+ bc_rm[1].bc.names.append(bnd_list_names)
1197
+ bc_rm[1].bc.numbers.append(bnd_list_numbers)
1198
+ if bc_data[0] == 'cooling':
1199
+ bc_rm[1].bc.values.append([bc.heat_transfer_coefficient, self.data.magnet.solve.thermal.init_temperature])
1200
+ elif bc_data[0] == 'temperature':
1201
+ bc_rm[1].bc.value.append(bc.const_temperature)
1202
+ elif bc_data[0] == 'heat_flux':
1203
+ bc_rm[1].bc.value.append(bc.const_heat_flux)
1204
+
1205
+ def setMeshOptions(self, run_type):
247
1206
  """
248
1207
  Meshes the generated domain
249
1208
  """
1209
+ mesh = self.data.magnet.mesh
250
1210
  gmsh.option.setNumber("Mesh.MeshSizeExtendFromBoundary", 0)
251
1211
  gmsh.option.setNumber("Mesh.MeshSizeFromPoints", 0)
252
1212
  gmsh.option.setNumber("Mesh.MeshSizeFromCurvature", 0)
253
- if not self.data.magnet.mesh.default_mesh:
254
- # gmsh.option.setNumber("Mesh.AngleToleranceFacetOverlap", self.data.one_lab.mesh.AngleToleranceFacetOverlap)
255
- # gmsh.option.setNumber("Mesh.MeshSizeFromCurvature", self.data.one_lab.mesh.MeshSizeFromCurvature)
256
- # gmsh.option.setNumber("Mesh.MeshSizeMin", self.data.one_lab.mesh.MeshSizeMin)
257
- # gmsh.option.setNumber("Mesh.MeshSizeMax", self.data.one_lab.mesh.MeshSizeMax)
258
- gmsh.option.setNumber("Mesh.Algorithm", self.data.magnet.mesh.Algorithm)
259
- gmsh.option.setNumber("Mesh.Optimize", self.data.magnet.mesh.Optimize)
260
- gmsh.option.setNumber("Mesh.ElementOrder", self.data.magnet.mesh.ElementOrder)
261
- else:
262
- gmsh.option.setNumber("Mesh.Algorithm", 6)
263
- gmsh.option.setNumber("Mesh.Optimize", 1)
264
- gmsh.option.setNumber("Mesh.ElementOrder", 2)
1213
+ gmsh.option.setNumber("Mesh.Algorithm", 6)
1214
+ gmsh.option.setNumber("Mesh.Optimize", 1)
1215
+ gmsh.option.setNumber("Mesh.ElementOrder", 1)
265
1216
 
266
1217
  def generateMesh(self):
1218
+ gmsh.option.setNumber("General.Terminal", self.verbose)
267
1219
  self.mesh.generate(2)
268
- self.mesh.removeDuplicateNodes()
1220
+ # self.mesh.removeDuplicateNodes()
1221
+ # self.occ.synchronize()
1222
+ # self.gu.launch_interactive_GUI()
269
1223
 
270
1224
  def checkMeshQuality(self):
271
1225
  tags = self.mesh.getElements(2)[1][0]
@@ -283,3 +1237,111 @@ class Mesh:
283
1237
 
284
1238
  # gmsh.logger.getLastError()
285
1239
  # gmsh.logger.get()
1240
+
1241
+ def saveClosestNeighboursList(self):
1242
+
1243
+ def _closest_node_on_reference(origin_points, reference_points):
1244
+ """
1245
+ Compute list of lists with each list representing one original node (3 entries for 3 coordinates) and the closest
1246
+ reference mesh node (3 entries for 3 coordinates).
1247
+ :param origin_points: list of origin point as list of 3 floats per origin node
1248
+ :param reference_points: list of coordinates of reference mesh points as list of 3 floats per reference mesh node
1249
+ :return: returns list of lists with the closest reference mesh node per origin node
1250
+ """
1251
+ closest_node = []
1252
+ for x in range(0, len(origin_points), 3):
1253
+ origin_point = origin_points[x:x + 3]
1254
+ # Compute distance list between origin point and reference point list
1255
+ dist_lst = [Func.points_distance(origin_point, reference_points[y:y + 3]) for y in range(0, len(reference_points), 3)]
1256
+ min_idx = 3 * np.argmin(dist_lst)
1257
+ closest_node.append(origin_point + reference_points[min_idx:min_idx + 3])
1258
+ return closest_node
1259
+
1260
+ def _get_closest_nodes(side):
1261
+ origin_list = self.mesh.getNodesForPhysicalGroup(1, mid_layer)[1].tolist()
1262
+ reference_list = self.mesh.getNodesForPhysicalGroup(1, el.lines[side])[1].tolist()
1263
+ self.rc.neighbouring_nodes.groups[group].extend(
1264
+ [node for node_list in _closest_node_on_reference(origin_list, reference_list) for node in node_list])
1265
+
1266
+ logger.info(f"Info : {self.data.general.magnet_name} - F i n d i n g C l o s e s t N e i g h b o u r s . . .")
1267
+ logger.info(f"Info : Finding closest reference nodes ...")
1268
+
1269
+ self.rc.neighbouring_nodes.groups['1_1'] = []
1270
+ self.rc.neighbouring_nodes.groups['2_1'] = []
1271
+ self.rc.neighbouring_nodes.groups['1_2'] = []
1272
+ self.rc.neighbouring_nodes.groups['2_2'] = []
1273
+
1274
+ for blk_nr, blk in self.md.domains.physical_groups.blocks.items():
1275
+ for ht_nr, el in blk.half_turns.items():
1276
+ ht_list = list(blk.half_turns.keys())
1277
+ group = el.group[1] + '_' + el.group[-1]
1278
+ for line_name, mid_layer in el.mid_layer_lines.inner.items(): _get_closest_nodes('i')
1279
+ for line_name, mid_layer in el.mid_layer_lines.outer.items(): _get_closest_nodes('o')
1280
+ for line_name, mid_layer in el.aux_lines.items(): _get_closest_nodes('o')
1281
+ for line_name, mid_layer in el.mid_pole_lines.items(): _get_closest_nodes('l' if ht_list.index(ht_nr) == 0 else 'h')
1282
+ for line_name, mid_layer in el.mid_winding_lines.items(): _get_closest_nodes('l' if ht_list.index(ht_nr) == 0 else 'h')
1283
+ for line_name, mid_layer in el.mid_turn_lines.items():
1284
+ high = ht_list.index(ht_nr) == len(ht_list) - 1 if 'w' in line_name\
1285
+ else int(line_name[:line_name.index('_')]) == ht_nr
1286
+ _get_closest_nodes('h' if high else 'l')
1287
+ for wdg_nr, el in self.md.domains.physical_groups.wedges.items():
1288
+ group = el.group[1] + '_' + el.group[-1]
1289
+ for line_name, mid_layer in el.mid_layer_lines.inner.items(): _get_closest_nodes('i')
1290
+ for line_name, mid_layer in el.mid_layer_lines.outer.items(): _get_closest_nodes('o')
1291
+ for line_name, mid_layer in el.aux_lines.items(): _get_closest_nodes('o')
1292
+ for line_name, mid_layer in el.mid_turn_lines.items():
1293
+ _get_closest_nodes('l' if line_name == list(el.mid_turn_lines.keys())[0] else 'h')
1294
+
1295
+ logger.info(f"Info : {self.data.general.magnet_name} - E n d F i n d i n g C l o s e s t N e i g h b o u r s")
1296
+
1297
+ def selectMeshNodes(self, elements: str):
1298
+
1299
+ logger.info(f"Info : {self.data.general.magnet_name} - S e l e c t i n g M e s h N o d e s . . .")
1300
+ logger.info(f"Info : Selecting a mesh node per isothermal {elements[:-1]} ...")
1301
+
1302
+ if elements == 'conductors':
1303
+ bare_mesh = {'1_1': self.rm.powered['r1_a1'].vol.numbers, '2_1': self.rm.powered['r2_a1'].vol.numbers,
1304
+ '1_2': self.rm.powered['r1_a2'].vol.numbers, '2_2': self.rm.powered['r2_a2'].vol.numbers}
1305
+ groups = self.rc.isothermal_nodes.conductors
1306
+
1307
+ # dir + robin + mid_layers
1308
+ # potentially all thin shell lines if easier
1309
+ line_tags = self.rm.boundaries.thermal.temperature.groups['r1_a1'] + \
1310
+ self.rm.boundaries.thermal.temperature.groups['r1_a2'] + \
1311
+ self.rm.boundaries.thermal.temperature.groups['r2_a1'] + \
1312
+ self.rm.boundaries.thermal.temperature.groups['r2_a2'] + \
1313
+ self.rm.boundaries.thermal.cooling.groups['r1_a1'] + \
1314
+ self.rm.boundaries.thermal.cooling.groups['r1_a2'] + \
1315
+ self.rm.boundaries.thermal.cooling.groups['r2_a1'] + \
1316
+ self.rm.boundaries.thermal.cooling.groups['r2_a2'] + \
1317
+ self.rm.thin_shells.mid_turns_layers_poles
1318
+ for tag in line_tags:
1319
+ coords = list(self.mesh.getNodesForPhysicalGroup(dim=1, tag=tag)[1])[:3]
1320
+ self.rc.isothermal_nodes.thin_shells[tag] = [float(coords[0]), float(coords[1]), float(coords[2])]
1321
+
1322
+ elif elements == 'wedges':
1323
+ bare_mesh = {'1_1': self.rm.induced['r1_a1'].vol.numbers, '2_1': self.rm.induced['r2_a1'].vol.numbers,
1324
+ '1_2': self.rm.induced['r1_a2'].vol.numbers, '2_2': self.rm.induced['r2_a2'].vol.numbers}
1325
+ groups = self.rc.isothermal_nodes.wedges
1326
+ else:
1327
+ bare_mesh = {}
1328
+ groups = {}
1329
+
1330
+ for group, tags_list in bare_mesh.items():
1331
+ groups[group] = {}
1332
+ for tag in tags_list:
1333
+ coords = list(self.mesh.getNodesForPhysicalGroup(dim=2, tag=tag)[1])[:3]
1334
+ groups[group][tag] = [float(coords[0]), float(coords[1]), float(coords[2])]
1335
+
1336
+ for tag in self.rm.boundaries.thermal.cooling.groups['r' + group[0] + '_' + 'a' + group[-1]]:
1337
+ coords = list(self.mesh.getNodesForPhysicalGroup(dim=1, tag=tag)[1])[:3]
1338
+ groups[group][tag] = [float(coords[0]), float(coords[1]), float(coords[2])]
1339
+
1340
+ logger.info(f"Info : {self.data.general.magnet_name} - E n d S e l e c t i n g M e s h N o d e s")
1341
+ # import time
1342
+ # time.sleep(100)
1343
+ # TODO: add to pro file automatically
1344
+
1345
+ # self.occ.synchronize()
1346
+ # self.gu.launch_interactive_GUI()
1347
+