fiqus 2024.5.2__py3-none-any.whl → 2024.6.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.
- fiqus/MainFiQuS.py +15 -5
- fiqus/data/DataConductor.py +301 -0
- fiqus/data/DataFiQuS.py +5 -2
- fiqus/data/DataFiQuSConductor.py +84 -0
- fiqus/data/DataFiQuSConductorAC_Strand.py +565 -0
- fiqus/data/DataFiQuSPancake3D.py +149 -39
- fiqus/data/RegionsModelFiQuS.py +4 -2
- fiqus/geom_generators/GeometryCCT.py +19 -17
- fiqus/geom_generators/GeometryConductorAC_Strand.py +1391 -0
- fiqus/getdp_runners/RunGetdpConductorAC_Strand.py +202 -0
- fiqus/getdp_runners/RunGetdpMultipole.py +4 -4
- fiqus/mains/MainConductorAC_Strand.py +133 -0
- fiqus/mesh_generators/MeshCCT.py +8 -8
- fiqus/mesh_generators/MeshConductorAC_Strand.py +657 -0
- fiqus/mesh_generators/MeshMultipole.py +11 -8
- fiqus/mesh_generators/MeshPancake3D.py +20 -18
- fiqus/plotters/PlotPythonConductorAC.py +840 -0
- fiqus/post_processors/PostProcessConductorAC.py +49 -0
- fiqus/pro_assemblers/ProAssembler.py +4 -3
- fiqus/pro_templates/combined/CCT_template.pro +25 -25
- fiqus/pro_templates/combined/ConductorAC_template.pro +1025 -0
- fiqus/pro_templates/combined/Multipole_template.pro +5 -5
- fiqus/pro_templates/combined/Pancake3D_template.pro +131 -46
- fiqus/pro_templates/combined/materials.pro +13 -9
- {fiqus-2024.5.2.dist-info → fiqus-2024.6.0.dist-info}/METADATA +2 -1
- {fiqus-2024.5.2.dist-info → fiqus-2024.6.0.dist-info}/RECORD +34 -22
- {fiqus-2024.5.2.dist-info → fiqus-2024.6.0.dist-info}/WHEEL +1 -1
- tests/test_geometry_generators.py +41 -0
- tests/test_mesh_generators.py +45 -0
- tests/test_solvers.py +52 -0
- tests/utils/fiqus_test_classes.py +42 -6
- tests/utils/generate_reference_files_ConductorAC.py +57 -0
- tests/utils/generate_reference_files_Pancake3D.py +92 -0
- {fiqus-2024.5.2.dist-info → fiqus-2024.6.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pickle
|
|
3
|
+
|
|
4
|
+
import gmsh
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from fiqus.data import RegionsModelFiQuS
|
|
8
|
+
from fiqus.utils.Utils import GmshUtils, FilesAndFolders
|
|
9
|
+
from fiqus.data.RegionsModelFiQuS import RegionsModel
|
|
10
|
+
|
|
11
|
+
from fiqus.geom_generators.GeometryConductorAC_Strand import TwistedStrand
|
|
12
|
+
|
|
13
|
+
occ = gmsh.model.occ
|
|
14
|
+
|
|
15
|
+
class Mesh:
|
|
16
|
+
def __init__(self, fdm, verbose=True):
|
|
17
|
+
"""
|
|
18
|
+
A base-class used to manage the mesh for the CAC Strand and Rutherford models.
|
|
19
|
+
|
|
20
|
+
:ivar fdm: The fiqus data model for input parameters.
|
|
21
|
+
:vartype fdm: dict
|
|
22
|
+
:ivar cacdm: The magnet section from the fdm.
|
|
23
|
+
:vartype cacdm: object
|
|
24
|
+
:ivar mesh_folder: The path to the folder where the mesh files are stored.
|
|
25
|
+
:vartype mesh_folder: str
|
|
26
|
+
:ivar magnet_name: The name of the magnet model.
|
|
27
|
+
:vartype magnet_name: str
|
|
28
|
+
:ivar mesh_file: The path to the .msh file for the mesh.
|
|
29
|
+
:vartype mesh_file: str
|
|
30
|
+
:ivar regions_file: The path to the .regions file for the mesh.
|
|
31
|
+
:vartype regions_file: str
|
|
32
|
+
:ivar verbose: If True, the class will print additional information during execution.
|
|
33
|
+
:vartype verbose: bool
|
|
34
|
+
:ivar gu: An instance of the GmshUtils class for managing the gmsh utility.
|
|
35
|
+
:vartype gu: GmshUtils
|
|
36
|
+
"""
|
|
37
|
+
self.fdm = fdm
|
|
38
|
+
self.cacdm = fdm.magnet
|
|
39
|
+
self.mesh_folder = os.path.join(os.getcwd())
|
|
40
|
+
self.magnet_name = fdm.general.magnet_name
|
|
41
|
+
self.mesh_file = os.path.join(self.mesh_folder, f"{self.magnet_name}.msh")
|
|
42
|
+
self.regions_file = os.path.join(self.mesh_folder, f"{self.magnet_name}.regions")
|
|
43
|
+
self.verbose = verbose
|
|
44
|
+
self.gu = GmshUtils(self.mesh_folder, self.verbose)
|
|
45
|
+
self.gu.initialize()
|
|
46
|
+
|
|
47
|
+
gmsh.option.setNumber("General.Terminal", verbose)
|
|
48
|
+
|
|
49
|
+
def save_mesh(self, gui: bool = False):
|
|
50
|
+
"""
|
|
51
|
+
Saves the mesh to a .msh file. If gui is True, the mesh is also loaded in the gmsh GUI.
|
|
52
|
+
"""
|
|
53
|
+
gmsh.write(self.mesh_file)
|
|
54
|
+
if gui:
|
|
55
|
+
self.gu.launch_interactive_GUI()
|
|
56
|
+
else:
|
|
57
|
+
gmsh.clear()
|
|
58
|
+
gmsh.finalize()
|
|
59
|
+
|
|
60
|
+
def load_mesh(self, gui : bool = False):
|
|
61
|
+
"""
|
|
62
|
+
Loads a previously generated mesh.
|
|
63
|
+
"""
|
|
64
|
+
gmsh.clear()
|
|
65
|
+
gmsh.open(self.mesh_file)
|
|
66
|
+
|
|
67
|
+
if gui:
|
|
68
|
+
self.gu.launch_interactive_GUI()
|
|
69
|
+
|
|
70
|
+
class StrandMesh(Mesh):
|
|
71
|
+
"""
|
|
72
|
+
A subclass of Mesh that handles mesh generation for the twisted strand cross-section geometries.
|
|
73
|
+
|
|
74
|
+
:ivar geometry_class: An instance of the TwistedStrand class that stores all the information about the model structure. Initially set to None and should be loaded or assigned before mesh generation methods are called.
|
|
75
|
+
:vartype geometry_class: TwistedStrand
|
|
76
|
+
|
|
77
|
+
:param fdm: The fiqus data model, providing all the necessary data and parameters for mesh generation.
|
|
78
|
+
:type fdm: object
|
|
79
|
+
:param verbose: A boolean flag that indicates whether additional information should be printed to the console during operation. Defaults to True.
|
|
80
|
+
:type verbose: bool, optional
|
|
81
|
+
"""
|
|
82
|
+
def __init__(self, fdm, verbose=True):
|
|
83
|
+
super().__init__(fdm, verbose)
|
|
84
|
+
self.geometry_class : TwistedStrand = None # Class from geometry generation step, used to store all information about the model structure.
|
|
85
|
+
|
|
86
|
+
def filament_field(self):
|
|
87
|
+
"""
|
|
88
|
+
Generates the filament mesh size field.
|
|
89
|
+
|
|
90
|
+
The standard mesh size field is linearly interpolated between the mesh size at the filament boundary and the center.
|
|
91
|
+
Amplitude dependent meshing is applied if the field is expected to only penetrate a few elements in the filaments, adjusting the mesh size near the filament edges to capture the field penetration region accurately.
|
|
92
|
+
|
|
93
|
+
:return: The filament mesh size field tag.
|
|
94
|
+
:rtype: int
|
|
95
|
+
"""
|
|
96
|
+
# Define some constants
|
|
97
|
+
# Choose the largest distance from the center of mass of the filaments to the filament boundary as the filament radius.
|
|
98
|
+
# This approximation is done to account for the fact that the filaments may not be circular, but can have any shape.
|
|
99
|
+
filament_rad = 0
|
|
100
|
+
for layer in self.geometry_class.filaments:
|
|
101
|
+
for filament in layer:
|
|
102
|
+
center_of_mass = gmsh.model.occ.get_center_of_mass(2, filament.surface_tag)
|
|
103
|
+
for curve in filament.boundary_curves:
|
|
104
|
+
r = max([np.linalg.norm(np.array(center_of_mass) - curve.P1.pos), np.linalg.norm(np.array(center_of_mass) - curve.P2.pos)])
|
|
105
|
+
if r > filament_rad:
|
|
106
|
+
filament_rad = r
|
|
107
|
+
meshSize_at_filament_boundary = filament_rad * self.cacdm.mesh.filaments.boundary_mesh_size_ratio
|
|
108
|
+
meshSize_at_filament_center = filament_rad * self.cacdm.mesh.filaments.center_mesh_size_ratio
|
|
109
|
+
filament_edges = sum([filament.boundary_curves for layer in self.geometry_class.filaments for filament in layer], [])
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
amplitude_dependent_meshing, field_penetration_distance = self.evaluate_amplitude_dependent_meshing().values()
|
|
113
|
+
desired_elements_in_field_penetration_region = self.cacdm.mesh.filaments.desired_elements_in_field_penetration_region
|
|
114
|
+
|
|
115
|
+
frequency_dependent_meshing, skin_depth = self.evaluate_frequency_dependent_meshing().values()
|
|
116
|
+
if frequency_dependent_meshing:
|
|
117
|
+
filaments_in_skindepth = [filament for layer in self.geometry_class.filaments for filament in layer if np.linalg.norm(filament.center_point.pos) > self.geometry_class.matrix[-1].rad - 2*skin_depth]
|
|
118
|
+
else:
|
|
119
|
+
filaments_in_skindepth = [filament for layer in self.geometry_class.filaments for filament in layer]
|
|
120
|
+
|
|
121
|
+
if amplitude_dependent_meshing:
|
|
122
|
+
# The field only penetrates a few elements. We should thus decrease the mesh size close to the filament edges.
|
|
123
|
+
# min_mesh_size will be overwritten, while meshSize_at_filament_center will remain.
|
|
124
|
+
|
|
125
|
+
circular_filament_fields = []
|
|
126
|
+
nodesPerFilament = 2*np.pi*filament_rad / ((field_penetration_distance)/desired_elements_in_field_penetration_region) # We place nodes for meshing at the filament boundary for more efficient meshing (transfinite meshing)
|
|
127
|
+
|
|
128
|
+
for filament in filaments_in_skindepth:
|
|
129
|
+
circular_field = gmsh.model.mesh.field.add("Ball")
|
|
130
|
+
gmsh.model.mesh.field.setNumber(circular_field, "Radius", (filament_rad-field_penetration_distance)*0.85 )
|
|
131
|
+
gmsh.model.mesh.field.setNumber(circular_field, "XCenter", filament.center_point.pos[0])
|
|
132
|
+
gmsh.model.mesh.field.setNumber(circular_field, "YCenter", filament.center_point.pos[1])
|
|
133
|
+
gmsh.model.mesh.field.setNumber(circular_field, "Thickness", (filament_rad-field_penetration_distance)*0.15)
|
|
134
|
+
gmsh.model.mesh.field.setNumber(circular_field, "VIn", meshSize_at_filament_center)
|
|
135
|
+
gmsh.model.mesh.field.setNumber(circular_field, "VOut", (field_penetration_distance)/desired_elements_in_field_penetration_region )
|
|
136
|
+
circular_filament_fields.append(circular_field)
|
|
137
|
+
|
|
138
|
+
for circle_arc_tag in [CA.tag for CA in filament.boundary_curves]:
|
|
139
|
+
gmsh.model.mesh.setTransfiniteCurve(circle_arc_tag, int(nodesPerFilament/len(filament.boundary_curves)), "Progression", 1)
|
|
140
|
+
|
|
141
|
+
filament_fields = gmsh.model.mesh.field.add("Max")
|
|
142
|
+
gmsh.model.mesh.field.setNumbers(filament_fields, "FieldsList", circular_filament_fields)
|
|
143
|
+
|
|
144
|
+
self.cacdm.mesh.filaments.boundary_mesh_size_ratio = ((field_penetration_distance)/desired_elements_in_field_penetration_region)/filament_rad
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
else:
|
|
148
|
+
# Generalized to arbitrary filament shapes (not only circular)
|
|
149
|
+
f_dist_boundary = gmsh.model.mesh.field.add("Distance")
|
|
150
|
+
gmsh.model.mesh.field.setNumbers(f_dist_boundary, "CurvesList", [edge.tag for edge in filament_edges])
|
|
151
|
+
|
|
152
|
+
mass_centers = []
|
|
153
|
+
for layer in self.geometry_class.filaments:
|
|
154
|
+
for filament in layer:
|
|
155
|
+
x,y,z = gmsh.model.occ.get_center_of_mass(2, filament.surface_tag)
|
|
156
|
+
mass_center_tag = gmsh.model.occ.addPoint(x, y, z)
|
|
157
|
+
mass_centers.append(mass_center_tag)
|
|
158
|
+
gmsh.model.occ.synchronize()
|
|
159
|
+
|
|
160
|
+
f_dist_center = gmsh.model.mesh.field.add("Distance")
|
|
161
|
+
gmsh.model.mesh.field.setNumbers(f_dist_center, "PointsList", mass_centers)
|
|
162
|
+
|
|
163
|
+
# Linearly interpolate between the mesh size at the filament center and the mesh size at the filament boundary.
|
|
164
|
+
filament_fields = gmsh.model.mesh.field.add("MathEval")
|
|
165
|
+
gmsh.model.mesh.field.setString(
|
|
166
|
+
filament_fields,
|
|
167
|
+
"F",
|
|
168
|
+
f"( {meshSize_at_filament_boundary} * F{f_dist_center} + {meshSize_at_filament_center} * F{f_dist_boundary} ) / ( F{f_dist_center} + F{f_dist_boundary} )"
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# Restrict the filament fields to the filaments
|
|
173
|
+
filament_mesh_field = gmsh.model.mesh.field.add("Restrict")
|
|
174
|
+
gmsh.model.mesh.field.setNumber(filament_mesh_field, "InField", filament_fields)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
if frequency_dependent_meshing:
|
|
178
|
+
gmsh.model.mesh.field.setNumbers(filament_mesh_field, "SurfacesList", [filament.surface_tag for filament in filaments_in_skindepth])
|
|
179
|
+
else:
|
|
180
|
+
gmsh.model.mesh.field.setNumbers(filament_mesh_field, "SurfacesList", [filament.surface_tag for layer in self.geometry_class.filaments for filament in layer] + [hole.surface_tag for layer in self.geometry_class.filament_holes for hole in layer])
|
|
181
|
+
|
|
182
|
+
return filament_mesh_field
|
|
183
|
+
|
|
184
|
+
def matrix_threshold_to_filament_field(self, filaments):
|
|
185
|
+
"""
|
|
186
|
+
Adjusts the matrix mesh size based on the distance from the filaments to improve mesh quality.
|
|
187
|
+
|
|
188
|
+
This method creates a mesh size field that gradually refines the mesh in the matrix as it approaches the filaments.
|
|
189
|
+
Filaments typically have a finer mesh than the matrix, and a large gradient in mesh size at their boundary can degrade the mesh quality.
|
|
190
|
+
The method interpolates the mesh size from a minimum value at the filament boundary to a maximum value at a specified distance from the filaments, based on configuration parameters in the FDM.
|
|
191
|
+
|
|
192
|
+
:param filaments: A list of filament surface objects for which the boundary mesh size adjustments are to be made.
|
|
193
|
+
:type filaments: list[list[object]]
|
|
194
|
+
|
|
195
|
+
:return: The tag of the created mesh size field, which is restricted to the matrix domain.
|
|
196
|
+
:rtype: int
|
|
197
|
+
"""
|
|
198
|
+
matrix = self.geometry_class.matrix
|
|
199
|
+
matrix_mesh_options = self.cacdm.mesh.matrix
|
|
200
|
+
|
|
201
|
+
filament_edges = sum([[CA.tag for CA in filament.boundary_curves] for filament in filaments], []) # List of tags of all filament boundaries
|
|
202
|
+
# Distance to filament boundaries field:
|
|
203
|
+
distance_to_filament_field = gmsh.model.mesh.field.add("Distance")
|
|
204
|
+
gmsh.model.mesh.field.setNumbers(distance_to_filament_field, "CurvesList", filament_edges)
|
|
205
|
+
|
|
206
|
+
# Choose the largest distance from the center of mass of the filaments to the filament boundary as the filament radius.
|
|
207
|
+
# This approximation is done to account for the fact that the filaments may not be circular, but can have any shape.
|
|
208
|
+
filament_rad = 0
|
|
209
|
+
for layer in self.geometry_class.filaments:
|
|
210
|
+
for filament in layer:
|
|
211
|
+
center_of_mass = gmsh.model.occ.get_center_of_mass(2, filament.surface_tag)
|
|
212
|
+
for curve in filament.boundary_curves:
|
|
213
|
+
r = max([np.linalg.norm(np.array(center_of_mass) - curve.P1.pos), np.linalg.norm(np.array(center_of_mass) - curve.P2.pos)])
|
|
214
|
+
if r > filament_rad:
|
|
215
|
+
filament_rad = r
|
|
216
|
+
|
|
217
|
+
filament_boundary_meshSize = filament_rad * self.cacdm.mesh.filaments.boundary_mesh_size_ratio
|
|
218
|
+
|
|
219
|
+
# Linarily interpolate the mesh size from the filament boundaries to a specified distance from filaments.
|
|
220
|
+
threshold_to_filament_field = gmsh.model.mesh.field.add("Threshold")
|
|
221
|
+
gmsh.model.mesh.field.setNumber(threshold_to_filament_field, "InField", distance_to_filament_field)
|
|
222
|
+
gmsh.model.mesh.field.setNumber(threshold_to_filament_field, "SizeMin", filament_boundary_meshSize)
|
|
223
|
+
gmsh.model.mesh.field.setNumber(threshold_to_filament_field, "SizeMax", filament_rad*matrix_mesh_options.mesh_size_matrix_ratio_inner)
|
|
224
|
+
gmsh.model.mesh.field.setNumber(threshold_to_filament_field, "DistMin", 0)
|
|
225
|
+
gmsh.model.mesh.field.setNumber(threshold_to_filament_field, "DistMax", filament_rad*matrix_mesh_options.interpolation_distance_from_filaments_ratio)
|
|
226
|
+
gmsh.model.mesh.field.setNumber(threshold_to_filament_field, "StopAtDistMax", 1)
|
|
227
|
+
# gmsh.model.mesh.field.setNumber(threshold_to_filament_field, "Sigmoid", 0)
|
|
228
|
+
|
|
229
|
+
meshSize_by_filament_field = gmsh.model.mesh.field.add("Restrict") # Restrict the mesh to the matrix
|
|
230
|
+
gmsh.model.mesh.field.setNumber(meshSize_by_filament_field, "InField", threshold_to_filament_field)
|
|
231
|
+
gmsh.model.mesh.field.setNumbers(meshSize_by_filament_field, "SurfacesList", [partition.surface_tag for partition in matrix])
|
|
232
|
+
# matrix_mesh_size_fields.append(meshSize_by_filament_field)
|
|
233
|
+
return meshSize_by_filament_field
|
|
234
|
+
|
|
235
|
+
def evaluate_amplitude_dependent_meshing(self):
|
|
236
|
+
"""
|
|
237
|
+
Evaluates the necessity for amplitude-dependent meshing based on the applied magnetic field's amplitude.
|
|
238
|
+
|
|
239
|
+
This method assesses whether the amplitude of the applied magnetic field is low enough that it only partially penetrates the filaments.
|
|
240
|
+
In such cases, a finer mesh is required in the field penetration region to accurately capture field variations, while a coarser mesh can be used in the filament center where the field does not penetrate.
|
|
241
|
+
It calculates the field penetration distance and determines whether an adjustment in the mesh is necessary to ensure accurate simulation results.
|
|
242
|
+
|
|
243
|
+
:return: A dictionary containing:
|
|
244
|
+
- "amplitude_dependent_meshing" (bool): Indicates whether amplitude-dependent meshing is needed based on the magnetic field's penetration.
|
|
245
|
+
- "field_penetration_distance" (float): The estimated distance the magnetic field penetrates into the filaments, used to adjust the mesh size accordingly.
|
|
246
|
+
:rtype: Dict[str, Union[bool, float]]
|
|
247
|
+
"""
|
|
248
|
+
if self.cacdm.mesh.filaments.amplitude_dependent_scaling and self.cacdm.solve.source_parameters.sine.current_amplitude == 0 and not self.cacdm.geometry.io_settings.load.load_from_yaml:
|
|
249
|
+
filament_radius = self.geometry_class.filaments[0][0].rad
|
|
250
|
+
min_meshSize = filament_radius * self.cacdm.mesh.filaments.boundary_mesh_size_ratio
|
|
251
|
+
meshSize_at_filament_center = filament_radius * self.cacdm.mesh.filaments.center_mesh_size_ratio
|
|
252
|
+
|
|
253
|
+
applied_field_amplitude = self.cacdm.solve.source_parameters.sine.field_amplitude
|
|
254
|
+
applied_field_amplitude = max(applied_field_amplitude, 0.01) # Limit the amplitude to 0.01 T to avoid excessively fine mesh (Could be removed?).
|
|
255
|
+
jc = 3e9 #self.cacdm.solve.material_properties.superconducting.jc.constant
|
|
256
|
+
mu0 = np.pi*4e-7
|
|
257
|
+
|
|
258
|
+
field_penetration_distance = applied_field_amplitude/(mu0*jc) * self.cacdm.mesh.filaments.field_penetration_depth_scaling_factor # Estimate the field penetration distance
|
|
259
|
+
|
|
260
|
+
# The mesh size in the filaments can be written as mesh_size(x) = a*x+b, with x being the distance from the filament boundary and a,b defined as:
|
|
261
|
+
a = (meshSize_at_filament_center - min_meshSize) / filament_radius
|
|
262
|
+
b = min_meshSize
|
|
263
|
+
# The number of elements in the range x:[x0, x1] is approximated by the function:
|
|
264
|
+
number_of_elements_in_range = lambda a, b, x0, x1 : np.log(a*x1+b)/a - np.log(a*x0+b)/a if a != 0 else (x1-x0)/b
|
|
265
|
+
|
|
266
|
+
number_of_elements_penetrated_by_field = number_of_elements_in_range(a, b, 0, field_penetration_distance)
|
|
267
|
+
desired_elements_in_field_penetration_region = self.cacdm.mesh.filaments.desired_elements_in_field_penetration_region
|
|
268
|
+
|
|
269
|
+
if (number_of_elements_penetrated_by_field < desired_elements_in_field_penetration_region) and field_penetration_distance < filament_radius:
|
|
270
|
+
print(f"The magnetic field is expected to penetrate over approximately {round(number_of_elements_penetrated_by_field, 3)} elements in the filaments. To ensure that the field penetrates over at least {desired_elements_in_field_penetration_region} elements, the mesh in the filaments has been adjusted.")
|
|
271
|
+
return dict(amplitude_dependent_meshing = True, field_penetration_distance = field_penetration_distance)
|
|
272
|
+
|
|
273
|
+
return dict(amplitude_dependent_meshing = False, field_penetration_distance = 1e10)
|
|
274
|
+
|
|
275
|
+
def evaluate_frequency_dependent_meshing(self):
|
|
276
|
+
"""
|
|
277
|
+
Evaluates the need for frequency-dependent meshing based on the skin depth due to the applied magnetic field's frequency.
|
|
278
|
+
|
|
279
|
+
This method determines if the frequency of the applied magnetic field is high enough to confine the currents to a thin layer near the surface of the strand.
|
|
280
|
+
A fine mesh is required in this region to capture current variations accurately, while a coarser mesh can suffice in the strand center.
|
|
281
|
+
The method calculates the skin depth using the field frequency and the matrix resistivity, then approximates the number of elements within this depth.
|
|
282
|
+
If the calculated number is less than desired, it indicates that frequency-dependent meshing adjustments are needed.
|
|
283
|
+
|
|
284
|
+
:return: A dictionary containing:
|
|
285
|
+
- "frequency_dependent_meshing" (bool): Indicates whether frequency-dependent meshing adjustments are necessary.
|
|
286
|
+
- "skin_depth" (float): The calculated skin depth, which informs how the mesh should be adjusted to accurately model current variations.
|
|
287
|
+
:rtype: Dict[str, Union[bool, float]]
|
|
288
|
+
"""
|
|
289
|
+
if self.cacdm.mesh.matrix.rate_dependent_scaling_matrix and not self.cacdm.geometry.io_settings.load.load_from_yaml:
|
|
290
|
+
matrix_mesh_options = self.cacdm.mesh.matrix
|
|
291
|
+
|
|
292
|
+
filament_radius = self.geometry_class.filaments[0][0].rad
|
|
293
|
+
|
|
294
|
+
mesh_size_matrix_inner = filament_radius * matrix_mesh_options.mesh_size_matrix_ratio_inner
|
|
295
|
+
mesh_size_matrix_middle = filament_radius * matrix_mesh_options.mesh_size_matrix_ratio_middle
|
|
296
|
+
mesh_size_matrix_outer = filament_radius * matrix_mesh_options.mesh_size_matrix_ratio_outer
|
|
297
|
+
|
|
298
|
+
matrix = self.geometry_class.matrix
|
|
299
|
+
|
|
300
|
+
rho_matrix = 1.81e-10 #self.cacdm.solve.material_properties.matrix.resistivity.constant
|
|
301
|
+
mu0 = np.pi*4e-7
|
|
302
|
+
skin_depth = np.sqrt( rho_matrix / (np.pi*mu0 * self.cacdm.solve.source_parameters.sine.frequency ) ) * self.cacdm.mesh.matrix.skindepth_scaling_factor
|
|
303
|
+
# Function which approximates number of elements in a range given a linearly interpolated mesh size.
|
|
304
|
+
number_of_elements_in_range = lambda a, b, x0, x1 : np.log(a*x1+b)/a - np.log(a*x0+b)/a if a != 0 else (x1-x0)/b
|
|
305
|
+
|
|
306
|
+
# If the skindepth is smaller than the outer matrix, we only evaluate the outer matrix field to approximate number of elements in skindepth.
|
|
307
|
+
if skin_depth <= matrix[-1].rad -matrix[-2].rad:
|
|
308
|
+
a = (mesh_size_matrix_outer - mesh_size_matrix_middle) / (matrix[-1].rad - matrix[-2].rad)
|
|
309
|
+
b = mesh_size_matrix_middle
|
|
310
|
+
elements_in_skindepth = number_of_elements_in_range(a, b, matrix[-1].rad -matrix[-2].rad - skin_depth, matrix[-1].rad-matrix[-2].rad)
|
|
311
|
+
|
|
312
|
+
elif skin_depth > matrix[-1].rad -matrix[-2].rad and skin_depth < matrix[-1].rad:
|
|
313
|
+
# If the skindepth is larger than the outer matrix, we evaluate the inner and outer matrix field to approximate number of elements in skindepth.
|
|
314
|
+
a1 = (mesh_size_matrix_middle - mesh_size_matrix_inner) / (matrix[-2].rad)
|
|
315
|
+
b1 = mesh_size_matrix_middle
|
|
316
|
+
a2 = (mesh_size_matrix_outer - mesh_size_matrix_middle) / (matrix[-1].rad - matrix[-2].rad)
|
|
317
|
+
b2 = mesh_size_matrix_middle
|
|
318
|
+
elements_in_skindepth = number_of_elements_in_range(a1, b1, matrix[-1].rad-skin_depth, matrix[-2].rad) + number_of_elements_in_range(a2, b2, 0, matrix[-1].rad-matrix[-2].rad)
|
|
319
|
+
|
|
320
|
+
else: # If the skindepth is greater than the matrix radius we do not want to act, and set the number of elements in skindepth to a high number.
|
|
321
|
+
elements_in_skindepth = 1e10
|
|
322
|
+
|
|
323
|
+
desired_elements_in_skindepth = self.cacdm.mesh.matrix.desired_elements_in_skindepth
|
|
324
|
+
|
|
325
|
+
if elements_in_skindepth < desired_elements_in_skindepth:
|
|
326
|
+
print(f"The skindepth of the matrix is expected to contain only {round(elements_in_skindepth, 3)} elements. The mesh in the matrix has been adjusted to ensure at least {desired_elements_in_skindepth} elements in the skindepth.")
|
|
327
|
+
return dict(frequency_dependent_meshing = True, skin_depth = skin_depth)
|
|
328
|
+
|
|
329
|
+
return dict(frequency_dependent_meshing = False, skin_depth = 1e10)
|
|
330
|
+
|
|
331
|
+
def matrix_field(self):
|
|
332
|
+
"""
|
|
333
|
+
Creates a mesh size field for the matrix as a combination of three fields:
|
|
334
|
+
- A distance-based interpolation between the matrix center and partition boundaries. The mesh size is linearly interpolated from the center to the boundary of the second matrix partition, and then to the outer matrix boundary.
|
|
335
|
+
- Adjustments near the filaments to ensure a smooth transition from the filament mesh to the matrix mesh.
|
|
336
|
+
- Frequency-dependent adjustments to capture skin depth effects.
|
|
337
|
+
|
|
338
|
+
:return: The tag of the composite mesh size field created for the matrix.
|
|
339
|
+
:rtype: int
|
|
340
|
+
"""
|
|
341
|
+
matrix_mesh_options = self.cacdm.mesh.matrix
|
|
342
|
+
# Choose the largest distance from the center of mass of the filaments to the filament boundary as the filament radius.
|
|
343
|
+
# This approximation is done to account for the fact that the filaments may not be circular, but can have any shape.
|
|
344
|
+
filament_rad = 0
|
|
345
|
+
for layer in self.geometry_class.filaments:
|
|
346
|
+
for filament in layer:
|
|
347
|
+
center_of_mass = gmsh.model.occ.get_center_of_mass(2, filament.surface_tag)
|
|
348
|
+
for curve in filament.boundary_curves:
|
|
349
|
+
r = max([np.linalg.norm(np.array(center_of_mass) - curve.P1.pos), np.linalg.norm(np.array(center_of_mass) - curve.P2.pos)])
|
|
350
|
+
if r > filament_rad:
|
|
351
|
+
filament_rad = r
|
|
352
|
+
|
|
353
|
+
mesh_size_matrix_inner = filament_rad * matrix_mesh_options.mesh_size_matrix_ratio_inner
|
|
354
|
+
mesh_size_matrix_middle = filament_rad * matrix_mesh_options.mesh_size_matrix_ratio_middle
|
|
355
|
+
mesh_size_matrix_outer = filament_rad * matrix_mesh_options.mesh_size_matrix_ratio_outer
|
|
356
|
+
|
|
357
|
+
filament_edge_points = []
|
|
358
|
+
for layer in self.geometry_class.filaments:
|
|
359
|
+
for filament in layer:
|
|
360
|
+
for curve in filament.boundary_curves:
|
|
361
|
+
filament_edge_points.append(curve.P1)
|
|
362
|
+
filament_edge_points.append(curve.P2)
|
|
363
|
+
filament_outer_point = max(filament_edge_points, key=lambda p: np.linalg.norm(p.pos))
|
|
364
|
+
# Distance from the outermost filament edge to the center of the matrix
|
|
365
|
+
# This is done instead of referencing the middle matrix radius, to be able to mesh arbitrary geometries.
|
|
366
|
+
filament_outer_point_dist = np.linalg.norm(filament_outer_point.pos)
|
|
367
|
+
|
|
368
|
+
matrix = self.geometry_class.matrix
|
|
369
|
+
matrix_outer_point_dist = max([np.linalg.norm(p.pos) for curve in matrix[-1].boundary_curves for p in [curve.P1, curve.P2]]) # Distance from the outermost matrix edge to the center of the matrix
|
|
370
|
+
|
|
371
|
+
matrix_mesh_size_fields = []
|
|
372
|
+
|
|
373
|
+
# 'Distance from matrix center'- field:
|
|
374
|
+
dist_from_center_field = gmsh.model.mesh.field.add("MathEval")
|
|
375
|
+
gmsh.model.mesh.field.setString(dist_from_center_field, "F", "sqrt(x^2 + y^2)")
|
|
376
|
+
|
|
377
|
+
# Mesh size-field in the middle partition of the matrix. The mesh size is large at the center and decreases linearily towards the edge of the middle matrix.
|
|
378
|
+
interpField_innerToMiddle = gmsh.model.mesh.field.add("Threshold")
|
|
379
|
+
gmsh.model.mesh.field.setNumber(interpField_innerToMiddle, "InField", dist_from_center_field)
|
|
380
|
+
gmsh.model.mesh.field.setNumber(interpField_innerToMiddle, "SizeMin", mesh_size_matrix_inner)
|
|
381
|
+
gmsh.model.mesh.field.setNumber(interpField_innerToMiddle, "SizeMax", mesh_size_matrix_middle)
|
|
382
|
+
gmsh.model.mesh.field.setNumber(interpField_innerToMiddle, "DistMin", 0)
|
|
383
|
+
gmsh.model.mesh.field.setNumber(interpField_innerToMiddle, "DistMax", filament_outer_point_dist)
|
|
384
|
+
|
|
385
|
+
# Restrict the middle field to the inner and middle matrix (only middle matrix in the case where we have a center filament and thus no inner matrix).
|
|
386
|
+
middle_matrix_field = gmsh.model.mesh.field.add("Restrict")
|
|
387
|
+
gmsh.model.mesh.field.setNumber(middle_matrix_field, "InField", interpField_innerToMiddle)
|
|
388
|
+
gmsh.model.mesh.field.setNumbers(middle_matrix_field, "SurfacesList", [partition.surface_tag for partition in matrix[:-1]])
|
|
389
|
+
matrix_mesh_size_fields.append(middle_matrix_field)
|
|
390
|
+
|
|
391
|
+
# Mesh size-field in the outer partition of the matrix. The mesh size is small at the edge of the middle matrix and increases towards the outer edge.
|
|
392
|
+
interpField_middleToOuter = gmsh.model.mesh.field.add("Threshold")
|
|
393
|
+
gmsh.model.mesh.field.setNumber(interpField_middleToOuter, "InField", dist_from_center_field)
|
|
394
|
+
gmsh.model.mesh.field.setNumber(interpField_middleToOuter, "SizeMin", mesh_size_matrix_middle)
|
|
395
|
+
gmsh.model.mesh.field.setNumber(interpField_middleToOuter, "SizeMax", mesh_size_matrix_outer)
|
|
396
|
+
gmsh.model.mesh.field.setNumber(interpField_middleToOuter, "DistMin", filament_outer_point_dist)
|
|
397
|
+
gmsh.model.mesh.field.setNumber(interpField_middleToOuter, "DistMax", matrix_outer_point_dist)
|
|
398
|
+
|
|
399
|
+
# Restric the outer field to the outer matrix partition:
|
|
400
|
+
outer_matrix_field = gmsh.model.mesh.field.add("Restrict")
|
|
401
|
+
gmsh.model.mesh.field.setNumber(outer_matrix_field, "InField", interpField_middleToOuter)
|
|
402
|
+
gmsh.model.mesh.field.setNumbers(outer_matrix_field, "SurfacesList", [matrix[-1].surface_tag])
|
|
403
|
+
matrix_mesh_size_fields.append(outer_matrix_field)
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
# The last field adjusts the matrix mesh size with respect to the distance from the filaments. The reason is that the filaments typically has a finer mesh than the matrix and
|
|
407
|
+
# the large mesh size gradient at the boundary between the matrix and the filaments can lead to a bad quality mesh. We thus want to gradually refine the mesh in the matrix close to the filaments.
|
|
408
|
+
# This is done by interpolating the mesh size based on the distance to the filament boundaries.
|
|
409
|
+
meshSize_by_filament_field = self.matrix_threshold_to_filament_field([filament for layer in self.geometry_class.filaments for filament in layer])
|
|
410
|
+
matrix_mesh_size_fields.append(meshSize_by_filament_field)
|
|
411
|
+
|
|
412
|
+
frequency_dependent_meshing, skin_depth = self.evaluate_frequency_dependent_meshing().values()
|
|
413
|
+
|
|
414
|
+
if frequency_dependent_meshing:
|
|
415
|
+
# If the skindepth only consists of a few elements, we add an additional mesh size field in the matrix. This field is large outside of the skindepth, but
|
|
416
|
+
# in the skindepth it is defined as the constant field which gives the skindepth the 'desired number of elements'. In between the two regions we interpolate between the two values.
|
|
417
|
+
# print(f"The skindepth of the matrix is expected to contain only {round(elements_in_skindepth, 3)} elements. The mesh in the matrix has been adjusted to ensure at least {desired_elements_in_skindepth} elements in the skindepth.")
|
|
418
|
+
|
|
419
|
+
circular_field = gmsh.model.mesh.field.add("Ball")
|
|
420
|
+
gmsh.model.mesh.field.setNumber(circular_field, "Radius", (matrix[-1].rad-skin_depth)*0.85 )
|
|
421
|
+
# gmsh.model.mesh.field.setNumber(circular_field, "Thickness", (matrix[-1].rad-skin_depth)*0.15)
|
|
422
|
+
gmsh.model.mesh.field.setNumber(circular_field, "VIn", mesh_size_matrix_inner)
|
|
423
|
+
gmsh.model.mesh.field.setNumber(circular_field, "VOut", (skin_depth)/self.cacdm.mesh.matrix.desired_elements_in_skindepth )
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
# -- Restrict the mesh size field to the matrix and the filaments not in the skindepth region -- #
|
|
428
|
+
filaments_not_in_skindepth = [filament for layer in self.geometry_class.filaments for filament in layer if np.linalg.norm(filament.center_point.pos) < self.geometry_class.matrix[-1].rad - 2*skin_depth]
|
|
429
|
+
|
|
430
|
+
circular_field_restricted = gmsh.model.mesh.field.add("Restrict")
|
|
431
|
+
gmsh.model.mesh.field.setNumber(circular_field_restricted, "InField", circular_field)
|
|
432
|
+
gmsh.model.mesh.field.setNumbers(circular_field_restricted, "SurfacesList", [partition.surface_tag for partition in matrix] + [filament.surface_tag for filament in filaments_not_in_skindepth] )
|
|
433
|
+
# -- -- #
|
|
434
|
+
|
|
435
|
+
self.cacdm.mesh.matrix.mesh_size_matrix_ratio_outer = skin_depth/self.cacdm.mesh.matrix.desired_elements_in_skindepth * 1/filament_rad
|
|
436
|
+
|
|
437
|
+
filaments_in_skindepth = [filament for layer in self.geometry_class.filaments for filament in layer if np.linalg.norm(filament.center_point.pos) > self.geometry_class.matrix[-1].rad - 2*skin_depth]
|
|
438
|
+
meshSize_by_filament_field = self.matrix_threshold_to_filament_field(filaments_in_skindepth) # Redefine the interpolation field to apply only to the filaments in the skindepth region.
|
|
439
|
+
|
|
440
|
+
matrix_meshSize_field = gmsh.model.mesh.field.add("Min")
|
|
441
|
+
gmsh.model.mesh.field.setNumbers(matrix_meshSize_field, "FieldsList", [circular_field_restricted, meshSize_by_filament_field])
|
|
442
|
+
return matrix_meshSize_field
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
# Combine all the matrix mesh size fields by selecting the minimum value of all fields.
|
|
448
|
+
matrix_meshSize_field = gmsh.model.mesh.field.add("Min")
|
|
449
|
+
gmsh.model.mesh.field.setNumbers(matrix_meshSize_field, "FieldsList", matrix_mesh_size_fields)
|
|
450
|
+
|
|
451
|
+
return matrix_meshSize_field
|
|
452
|
+
|
|
453
|
+
def air_field(self):
|
|
454
|
+
"""
|
|
455
|
+
Generates a mesh size field for the air domain surrounding the model.
|
|
456
|
+
The field linearly interpolates between the mesh size at the outer boundary of the matrix and the air boundary.
|
|
457
|
+
|
|
458
|
+
:return: The tag of the created mesh size field for the air domain.
|
|
459
|
+
:rtype: int
|
|
460
|
+
"""
|
|
461
|
+
# Choose the largest distance from the center of mass of the filaments to the filament boundary as the filament radius.
|
|
462
|
+
# This approximation is done to account for the fact that the filaments may not be circular, but can have any shape.
|
|
463
|
+
filament_rad = 0
|
|
464
|
+
for layer in self.geometry_class.filaments:
|
|
465
|
+
for filament in layer:
|
|
466
|
+
center_of_mass = gmsh.model.occ.get_center_of_mass(2, filament.surface_tag)
|
|
467
|
+
for curve in filament.boundary_curves:
|
|
468
|
+
r = max([np.linalg.norm(np.array(center_of_mass) - curve.P1.pos), np.linalg.norm(np.array(center_of_mass) - curve.P2.pos)])
|
|
469
|
+
if r > filament_rad:
|
|
470
|
+
filament_rad = r
|
|
471
|
+
|
|
472
|
+
mesh_size_outer_matrix = filament_rad * self.cacdm.mesh.matrix.mesh_size_matrix_ratio_outer
|
|
473
|
+
mesh_size_air_boundary = filament_rad * self.cacdm.mesh.air.max_mesh_size_ratio
|
|
474
|
+
|
|
475
|
+
air_outer_boundary_curves = self.geometry_class.air.boundary_curves
|
|
476
|
+
air_inner_boundary_curves = sum(self.geometry_class.air.inner_boundary_curves, [])
|
|
477
|
+
|
|
478
|
+
dist_from_outer_air_boundary_field = gmsh.model.mesh.field.add("Distance")
|
|
479
|
+
gmsh.model.mesh.field.setNumbers(dist_from_outer_air_boundary_field, "CurvesList", [curve.tag for curve in air_outer_boundary_curves])
|
|
480
|
+
|
|
481
|
+
dist_from_matrix_boundary = gmsh.model.mesh.field.add("Distance")
|
|
482
|
+
gmsh.model.mesh.field.setNumbers(dist_from_matrix_boundary, "CurvesList", [curve.tag for curve in air_inner_boundary_curves])
|
|
483
|
+
|
|
484
|
+
air_field = gmsh.model.mesh.field.add("MathEval")
|
|
485
|
+
gmsh.model.mesh.field.setString(
|
|
486
|
+
air_field,
|
|
487
|
+
"F",
|
|
488
|
+
f"( {mesh_size_air_boundary} * F{dist_from_matrix_boundary} + {mesh_size_outer_matrix} * F{dist_from_outer_air_boundary_field} ) / ( F{dist_from_matrix_boundary} + F{dist_from_outer_air_boundary_field} )"
|
|
489
|
+
)
|
|
490
|
+
return air_field
|
|
491
|
+
|
|
492
|
+
def load_geometry_class(self, geom_folder):
|
|
493
|
+
"""
|
|
494
|
+
Loads the TwistedStrand geometry class from a .pkl file saved within the specified geometry folder.
|
|
495
|
+
The geometry class contains all the information about the strand geometry, facilitating mesh generation.
|
|
496
|
+
|
|
497
|
+
:param geom_folder: The path to the folder where the geometry class .pkl file is saved.
|
|
498
|
+
:type geom_folder: str
|
|
499
|
+
|
|
500
|
+
:return: An instance of the TwistedStrand geometry class.
|
|
501
|
+
:rtype: object
|
|
502
|
+
"""
|
|
503
|
+
geom_save_file = os.path.join(geom_folder, f'{self.magnet_name}.pkl')
|
|
504
|
+
|
|
505
|
+
with open(geom_save_file, "rb") as geom_save_file: # Unnecessary to return geom instead of setting self.geometry_class
|
|
506
|
+
geom = pickle.load(geom_save_file)
|
|
507
|
+
return geom
|
|
508
|
+
|
|
509
|
+
def generate_mesh(self, geom_folder):
|
|
510
|
+
"""
|
|
511
|
+
Generates the mesh for the entire model geometry based on combined mesh size fields.
|
|
512
|
+
|
|
513
|
+
The method generates mesh size fields for the filament, matrix, and air domains separately and combines these fields to define a unified mesh size field for the full geometry.
|
|
514
|
+
The combined field is applied as the background mesh size field.
|
|
515
|
+
|
|
516
|
+
:param geom_folder: The path to the folder containing the saved geometry class (.pkl file). This folder is used to load the geometry details required for mesh generation.
|
|
517
|
+
:type geom_folder: str
|
|
518
|
+
"""
|
|
519
|
+
|
|
520
|
+
# So far: Adds physical groups and generates an automatic mesh. To be further developed.
|
|
521
|
+
self.geometry_class : TwistedStrand = self.load_geometry_class(geom_folder)
|
|
522
|
+
self.geometry_class.update_tags()
|
|
523
|
+
self.geometry_class.add_physical_groups()
|
|
524
|
+
|
|
525
|
+
total_filament_boundary_distance_field = self.filament_field()
|
|
526
|
+
matrixField = self.matrix_field()
|
|
527
|
+
airField = self.air_field()
|
|
528
|
+
|
|
529
|
+
total_meshSize_field = gmsh.model.mesh.field.add("Min")
|
|
530
|
+
gmsh.model.mesh.field.setNumbers(total_meshSize_field, "FieldsList", [total_filament_boundary_distance_field, matrixField, airField])
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
gmsh.model.mesh.field.setAsBackgroundMesh(total_meshSize_field)
|
|
534
|
+
|
|
535
|
+
gmsh.option.setNumber("Mesh.MeshSizeFactor", self.cacdm.mesh.scaling_global)
|
|
536
|
+
gmsh.option.setNumber("Mesh.MeshSizeExtendFromBoundary", 0)
|
|
537
|
+
gmsh.option.setNumber("Mesh.MeshSizeFromPoints", 0)
|
|
538
|
+
gmsh.option.setNumber("Mesh.MeshSizeFromCurvature", 0)
|
|
539
|
+
|
|
540
|
+
gmsh.model.mesh.generate(2)
|
|
541
|
+
|
|
542
|
+
def generate_cuts(self):
|
|
543
|
+
"""
|
|
544
|
+
Computes the cuts for imposing global quantities.
|
|
545
|
+
"""
|
|
546
|
+
# Computes cuts, as needed for imposing global quantities
|
|
547
|
+
if self.verbose:
|
|
548
|
+
print('(Co)homology computation')
|
|
549
|
+
# a) Cut for the total current intensity, associated with the strand boundary
|
|
550
|
+
strand_bnd = self.geometry_class.air.physical_inner_boundary_tags[0]
|
|
551
|
+
gmsh.model.mesh.addHomologyRequest("Homology", domainTags=[strand_bnd],dims=[1])
|
|
552
|
+
air = self.geometry_class.air.physical_surface_tag
|
|
553
|
+
gmsh.model.mesh.addHomologyRequest("Cohomology", domainTags=[air],dims=[1])
|
|
554
|
+
cuts = gmsh.model.mesh.computeHomology()
|
|
555
|
+
gmsh.model.mesh.clearHomologyRequests()
|
|
556
|
+
gmsh.plugin.setString("HomologyPostProcessing", "PhysicalGroupsOfOperatedChains", str(cuts[0][1]))
|
|
557
|
+
gmsh.plugin.setString("HomologyPostProcessing", "PhysicalGroupsOfOperatedChains2", str(cuts[1][1]))
|
|
558
|
+
gmsh.plugin.run("HomologyPostProcessing")
|
|
559
|
+
|
|
560
|
+
self.geometry_class.air.strand_bnd_cut_tag = cuts[1][1]+1 # The cut tag for the strand boundary
|
|
561
|
+
|
|
562
|
+
# b) Cuts for the individual filaments
|
|
563
|
+
for layer in self.geometry_class.filaments:
|
|
564
|
+
for filament in layer:
|
|
565
|
+
filBnd = filament.physical_boundary_tag
|
|
566
|
+
gmsh.model.mesh.addHomologyRequest("Cohomology", domainTags=[filBnd],dims=[1])
|
|
567
|
+
cuts = gmsh.model.mesh.computeHomology()
|
|
568
|
+
|
|
569
|
+
filaments = sum(self.geometry_class.filaments, [])
|
|
570
|
+
for i in range(len(cuts)):
|
|
571
|
+
filaments[i].cut_tag = cuts[i][1]
|
|
572
|
+
|
|
573
|
+
def generate_regions_file(self):
|
|
574
|
+
"""
|
|
575
|
+
Generates a .regions file for the GetDP solver, containing all necessary information about the model.
|
|
576
|
+
The regions model contains data about physical surfaces, boundaries, points and cuts and is stored in the mesh folder.
|
|
577
|
+
"""
|
|
578
|
+
regions_model = RegionsModel()
|
|
579
|
+
|
|
580
|
+
""" -- Initialize data -- """
|
|
581
|
+
regions_model.powered["Filaments"] = RegionsModelFiQuS.Powered()
|
|
582
|
+
regions_model.powered["Filaments"].vol.numbers = [] # Filament physical surfaces tags
|
|
583
|
+
regions_model.powered["Filaments"].vol.names = [] # Filament physical surfaces names
|
|
584
|
+
regions_model.powered["Filaments"].surf.numbers = [] # Filament physical boundaries tags
|
|
585
|
+
regions_model.powered["Filaments"].surf.names = [] # Filament physical boundaries names
|
|
586
|
+
regions_model.powered["Filaments"].cochain.numbers = [] # Filament boundary cut tags
|
|
587
|
+
regions_model.powered["Filaments"].curve.names = [] # Stores physical points at filament boundary (to fix phi=0)
|
|
588
|
+
regions_model.powered["Filaments"].curve.numbers = [] # Stores physical points at filament boundary (to fix phi=0)
|
|
589
|
+
|
|
590
|
+
regions_model.powered["Filaments"].surf_in.numbers = [] # Filament holes physical tags
|
|
591
|
+
regions_model.powered["Filaments"].surf_in.names = [] # Filament holes physical names
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
regions_model.induced["Matrix"] = RegionsModelFiQuS.Induced()
|
|
595
|
+
regions_model.induced["Matrix"].vol.numbers = [] # Matrix partition physical surfaces tags
|
|
596
|
+
regions_model.induced["Matrix"].vol.names = [] # Matrix partition physical surfaces names
|
|
597
|
+
regions_model.induced["Matrix"].surf_in.numbers = [] # Matrix partition physical boundaries tags
|
|
598
|
+
regions_model.induced["Matrix"].surf_in.names = [] # Matrix partition physical boundaries names
|
|
599
|
+
regions_model.induced["Matrix"].surf_out.numbers = [] # Strand physical outer boundary tag
|
|
600
|
+
regions_model.induced["Matrix"].surf_out.names = [] # Strand physical outer boundary name
|
|
601
|
+
regions_model.induced["Matrix"].cochain.numbers = [] # Strand outer boundary cut tag
|
|
602
|
+
regions_model.induced["Matrix"].cochain.names = [] # Strand outer boundary cut name
|
|
603
|
+
""" -- -- """
|
|
604
|
+
|
|
605
|
+
for layer_i, layer in enumerate(self.geometry_class.filaments):
|
|
606
|
+
for filament_j, filament in enumerate(layer):
|
|
607
|
+
regions_model.powered["Filaments"].vol.numbers.append(filament.physical_surface_tag) # Surfaces in powered.vol
|
|
608
|
+
if self.cacdm.geometry.io_settings.load.load_from_yaml:
|
|
609
|
+
regions_model.powered["Filaments"].vol.names.append(filament.material)
|
|
610
|
+
else:
|
|
611
|
+
regions_model.powered["Filaments"].vol.names.append(filament.physical_surface_name)
|
|
612
|
+
|
|
613
|
+
regions_model.powered["Filaments"].surf.numbers.append(filament.physical_boundary_tag) # Boundaries in powered.surf
|
|
614
|
+
regions_model.powered["Filaments"].surf.names.append(filament.physical_boundary_name)
|
|
615
|
+
|
|
616
|
+
regions_model.powered["Filaments"].cochain.numbers.append(filament.cut_tag)
|
|
617
|
+
|
|
618
|
+
# Add physical point at filament boundary to fix phi=0
|
|
619
|
+
regions_model.powered["Filaments"].curve.names.append(f"EdgePoint: filament_{layer_i+1}_{filament_j+1}")
|
|
620
|
+
regions_model.powered["Filaments"].curve.numbers.append(filament.physicalEdgePointTag)
|
|
621
|
+
|
|
622
|
+
for layer in self.geometry_class.filament_holes:
|
|
623
|
+
for hole in layer:
|
|
624
|
+
regions_model.powered["Filaments"].surf_in.numbers.append(hole.physical_surface_tag)
|
|
625
|
+
if self.cacdm.geometry.io_settings.load.load_from_yaml:
|
|
626
|
+
regions_model.powered["Filaments"].surf_in.names.append(hole.material)
|
|
627
|
+
else:
|
|
628
|
+
regions_model.powered["Filaments"].surf_in.names.append(hole.physical_surface_name)
|
|
629
|
+
|
|
630
|
+
for matrixPartition in self.geometry_class.matrix:
|
|
631
|
+
regions_model.induced["Matrix"].vol.numbers.append(matrixPartition.physical_surface_tag)
|
|
632
|
+
if self.cacdm.geometry.io_settings.load.load_from_yaml:
|
|
633
|
+
regions_model.induced["Matrix"].vol.names.append(matrixPartition.material)
|
|
634
|
+
else:
|
|
635
|
+
regions_model.induced["Matrix"].vol.names.append(matrixPartition.physical_surface_name)
|
|
636
|
+
|
|
637
|
+
regions_model.induced["Matrix"].surf_in.numbers.append(matrixPartition.physical_boundary_tag)
|
|
638
|
+
regions_model.induced["Matrix"].surf_in.names.append(matrixPartition.physical_boundary_name)
|
|
639
|
+
|
|
640
|
+
regions_model.induced["Matrix"].surf_out.numbers.append(self.geometry_class.air.physical_inner_boundary_tags[0])
|
|
641
|
+
regions_model.induced["Matrix"].surf_out.names.append(self.geometry_class.air.physical_inner_boundary_names[0])
|
|
642
|
+
|
|
643
|
+
regions_model.induced["Matrix"].cochain.numbers.append(self.geometry_class.air.strand_bnd_cut_tag)
|
|
644
|
+
regions_model.induced["Matrix"].cochain.names.append("Strand boundary cut tag")
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
regions_model.air.vol.number = self.geometry_class.air.physical_surface_tag
|
|
648
|
+
regions_model.air.vol.name = self.geometry_class.air.physical_surface_name
|
|
649
|
+
|
|
650
|
+
regions_model.air.surf.number = self.geometry_class.air.physical_boundary_tag
|
|
651
|
+
regions_model.air.surf.name = self.geometry_class.air.physical_boundary_name
|
|
652
|
+
|
|
653
|
+
# Add physical point at matrix boundary to fix phi=0
|
|
654
|
+
regions_model.air.point.names = ["Point at strand boundary"]
|
|
655
|
+
regions_model.air.point.numbers = [self.geometry_class.air.strand_bnd_physicalEdgePointTag]
|
|
656
|
+
|
|
657
|
+
FilesAndFolders.write_data_to_yaml(self.regions_file, regions_model.model_dump())
|