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.
- fiqus/MainFiQuS.py +290 -134
- fiqus/data/DataConductor.py +301 -301
- fiqus/data/DataFiQuS.py +128 -84
- fiqus/data/DataFiQuSCCT.py +150 -150
- fiqus/data/DataFiQuSConductor.py +84 -84
- fiqus/data/DataFiQuSConductorAC_Strand.py +565 -565
- fiqus/data/DataFiQuSMultipole.py +716 -42
- fiqus/data/DataFiQuSPancake3D.py +737 -278
- fiqus/data/DataMultipole.py +180 -15
- fiqus/data/DataRoxieParser.py +90 -51
- fiqus/data/DataSettings.py +121 -0
- fiqus/data/DataWindingsCCT.py +37 -37
- fiqus/data/RegionsModelFiQuS.py +18 -6
- fiqus/geom_generators/GeometryCCT.py +905 -905
- fiqus/geom_generators/GeometryConductorAC_Strand.py +1391 -1391
- fiqus/geom_generators/GeometryMultipole.py +1827 -227
- fiqus/geom_generators/GeometryPancake3D.py +316 -117
- fiqus/geom_generators/GeometryPancake3DUtils.py +549 -0
- fiqus/getdp_runners/RunGetdpCCT.py +4 -4
- fiqus/getdp_runners/RunGetdpConductorAC_Strand.py +201 -201
- fiqus/getdp_runners/RunGetdpMultipole.py +115 -42
- fiqus/getdp_runners/RunGetdpPancake3D.py +28 -6
- fiqus/mains/MainCCT.py +2 -2
- fiqus/mains/MainConductorAC_Strand.py +132 -132
- fiqus/mains/MainMultipole.py +113 -62
- fiqus/mains/MainPancake3D.py +63 -23
- fiqus/mesh_generators/MeshCCT.py +209 -209
- fiqus/mesh_generators/MeshConductorAC_Strand.py +656 -656
- fiqus/mesh_generators/MeshMultipole.py +1243 -181
- fiqus/mesh_generators/MeshPancake3D.py +275 -192
- fiqus/parsers/ParserCOND.py +825 -0
- fiqus/parsers/ParserDAT.py +16 -16
- fiqus/parsers/ParserGetDPOnSection.py +212 -212
- fiqus/parsers/ParserGetDPTimeTable.py +134 -134
- fiqus/parsers/ParserMSH.py +53 -53
- fiqus/parsers/ParserPOS.py +214 -214
- fiqus/parsers/ParserRES.py +142 -142
- fiqus/plotters/PlotPythonCCT.py +133 -133
- fiqus/plotters/PlotPythonConductorAC.py +855 -840
- fiqus/plotters/PlotPythonMultipole.py +18 -18
- fiqus/post_processors/PostProcessCCT.py +440 -440
- fiqus/post_processors/PostProcessConductorAC.py +49 -49
- fiqus/post_processors/PostProcessMultipole.py +353 -229
- fiqus/post_processors/PostProcessPancake3D.py +8 -13
- fiqus/pre_processors/PreProcessCCT.py +175 -175
- fiqus/pro_assemblers/ProAssembler.py +14 -6
- fiqus/pro_material_functions/ironBHcurves.pro +246 -246
- fiqus/pro_templates/combined/CCT_template.pro +274 -274
- fiqus/pro_templates/combined/ConductorAC_template.pro +1025 -1025
- fiqus/pro_templates/combined/Multipole_template.pro +1694 -126
- fiqus/pro_templates/combined/Pancake3D_template.pro +2294 -1103
- fiqus/pro_templates/combined/TSA_materials.pro +162 -0
- fiqus/pro_templates/combined/materials.pro +36 -18
- fiqus/utils/Utils.py +508 -110
- fiqus/utils/update_data_settings.py +33 -0
- fiqus-2024.12.0.dist-info/METADATA +130 -0
- fiqus-2024.12.0.dist-info/RECORD +84 -0
- {fiqus-2024.6.0.dist-info → fiqus-2024.12.0.dist-info}/WHEEL +1 -1
- tests/test_FiQuS.py +1 -1
- tests/test_geometry_generators.py +101 -2
- tests/test_mesh_generators.py +154 -1
- tests/test_solvers.py +115 -21
- tests/utils/fiqus_test_classes.py +85 -21
- tests/utils/generate_reference_files_ConductorAC.py +57 -57
- tests/utils/generate_reference_files_Pancake3D.py +4 -5
- tests/utils/helpers.py +97 -97
- fiqus-2024.6.0.dist-info/METADATA +0 -103
- fiqus-2024.6.0.dist-info/RECORD +0 -79
- {fiqus-2024.6.0.dist-info → fiqus-2024.12.0.dist-info}/top_level.txt +0 -0
fiqus/mains/MainPancake3D.py
CHANGED
|
@@ -4,6 +4,7 @@ import math
|
|
|
4
4
|
import time as Time
|
|
5
5
|
import shutil
|
|
6
6
|
from copy import deepcopy
|
|
7
|
+
from enum import Enum
|
|
7
8
|
|
|
8
9
|
from fiqus.data.DataFiQuSPancake3D import (
|
|
9
10
|
Pancake3DGeometry,
|
|
@@ -13,10 +14,18 @@ from fiqus.data.DataFiQuSPancake3D import (
|
|
|
13
14
|
Pancake3DSolveSaveQuantity,
|
|
14
15
|
)
|
|
15
16
|
|
|
17
|
+
from fiqus.geom_generators.GeometryPancake3DUtils import spiralCurve
|
|
18
|
+
|
|
16
19
|
if len(sys.argv) == 3:
|
|
17
20
|
sys.path.insert(0, os.path.join(os.getcwd(), "steam-fiqus-dev"))
|
|
18
21
|
|
|
22
|
+
class direction(Enum):
|
|
23
|
+
"""
|
|
24
|
+
A class to specify direction easily.
|
|
25
|
+
"""
|
|
19
26
|
|
|
27
|
+
ccw = 0
|
|
28
|
+
cw = 1
|
|
20
29
|
class Base:
|
|
21
30
|
"""
|
|
22
31
|
Base class for geometry, mesh, and solution classes. It is created to avoid code
|
|
@@ -146,11 +155,32 @@ class MainPancake3D:
|
|
|
146
155
|
self.mesh_folder,
|
|
147
156
|
self.solution_folder,
|
|
148
157
|
)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
158
|
+
if self.fdm.magnet.geometry.conductorWrite:
|
|
159
|
+
self.fdm.magnet.geometry.gapBetweenPancakes = 0.00024
|
|
160
|
+
innerRadius = self.fdm.magnet.geometry.winding.innerRadius
|
|
161
|
+
gap = self.fdm.magnet.geometry.winding.thickness
|
|
162
|
+
# gap = 0.01
|
|
163
|
+
turns = self.fdm.magnet.geometry.numberOfPancakes
|
|
164
|
+
# turns = 4
|
|
165
|
+
z = - self.fdm.magnet.geometry.winding.height/2
|
|
166
|
+
initialTheta = 0.0
|
|
167
|
+
# transitionNotchAngle = self.fdm.magnet.geometry.wi.t ##TODO pull from internal recalculated
|
|
168
|
+
# transitionNotchAngle = 0.7853981633974483/2
|
|
169
|
+
d = direction(0)
|
|
170
|
+
sc = spiralCurve(innerRadius,
|
|
171
|
+
gap,
|
|
172
|
+
turns,
|
|
173
|
+
z,
|
|
174
|
+
initialTheta,
|
|
175
|
+
transitionNotchAngle,
|
|
176
|
+
self.fdm.magnet.geometry,
|
|
177
|
+
direction=d, # TODO code this to understand cw direction
|
|
178
|
+
cutPlaneNormal=None)
|
|
179
|
+
else:
|
|
180
|
+
geometry.generate_geometry()
|
|
181
|
+
geometry.generate_vi_file()
|
|
182
|
+
|
|
183
|
+
self.model_file = geometry.brep_file
|
|
154
184
|
|
|
155
185
|
def load_geometry(self, gui=False):
|
|
156
186
|
"""
|
|
@@ -210,11 +240,11 @@ class MainPancake3D:
|
|
|
210
240
|
processes the results.
|
|
211
241
|
"""
|
|
212
242
|
# calculate inductance and time constant:
|
|
213
|
-
if self.fdm.magnet.solve.
|
|
243
|
+
if self.fdm.magnet.solve.quantitiesToBeSaved is not None:
|
|
214
244
|
save_list = list(
|
|
215
245
|
map(
|
|
216
246
|
lambda quantity_object: quantity_object.quantity,
|
|
217
|
-
self.fdm.magnet.solve.
|
|
247
|
+
self.fdm.magnet.solve.quantitiesToBeSaved,
|
|
218
248
|
)
|
|
219
249
|
)
|
|
220
250
|
new_solve_object_for_inductance_and_time_constant = {
|
|
@@ -242,7 +272,7 @@ class MainPancake3D:
|
|
|
242
272
|
],
|
|
243
273
|
},
|
|
244
274
|
},
|
|
245
|
-
"nonlinearSolver": self.fdm.magnet.solve.
|
|
275
|
+
"nonlinearSolver": self.fdm.magnet.solve.nonlinearSolver,
|
|
246
276
|
"air": {
|
|
247
277
|
"permeability": 1.2566e-06,
|
|
248
278
|
},
|
|
@@ -295,7 +325,7 @@ class MainPancake3D:
|
|
|
295
325
|
solve_object = Pancake3DSolve(
|
|
296
326
|
**new_solve_object_for_inductance_and_time_constant
|
|
297
327
|
)
|
|
298
|
-
solve_object.
|
|
328
|
+
solve_object.quantitiesToBeSaved = [Pancake3DSolveSaveQuantity(quantity="inductance")]
|
|
299
329
|
copy_fdm.magnet.solve = solve_object
|
|
300
330
|
|
|
301
331
|
new_power_supply_object_for_inductance = {
|
|
@@ -348,17 +378,17 @@ class MainPancake3D:
|
|
|
348
378
|
# equivalent radial resistance of the winding:
|
|
349
379
|
# https://doi.org/10.1109/TASC.2021.3063653
|
|
350
380
|
R_eq = 0
|
|
351
|
-
for i in range(1, int(copy_fdm.magnet.geometry.
|
|
381
|
+
for i in range(1, int(copy_fdm.magnet.geometry.winding.numberOfTurns)):
|
|
352
382
|
r_i = (
|
|
353
|
-
copy_fdm.magnet.geometry.
|
|
354
|
-
+ i * copy_fdm.magnet.geometry.
|
|
355
|
-
+ (i - 1) * copy_fdm.magnet.geometry.
|
|
356
|
-
+ copy_fdm.magnet.geometry.
|
|
383
|
+
copy_fdm.magnet.geometry.winding.innerRadius
|
|
384
|
+
+ i * copy_fdm.magnet.geometry.winding.thickness
|
|
385
|
+
+ (i - 1) * copy_fdm.magnet.geometry.contactLayer.thickness
|
|
386
|
+
+ copy_fdm.magnet.geometry.contactLayer.thickness / 2
|
|
357
387
|
)
|
|
358
|
-
w_d = copy_fdm.magnet.geometry.
|
|
388
|
+
w_d = copy_fdm.magnet.geometry.winding.height
|
|
359
389
|
R_ct = (
|
|
360
|
-
copy_fdm.magnet.solve.
|
|
361
|
-
* copy_fdm.magnet.geometry.
|
|
390
|
+
copy_fdm.magnet.solve.contactLayer.resistivity
|
|
391
|
+
* copy_fdm.magnet.geometry.contactLayer.thickness
|
|
362
392
|
)
|
|
363
393
|
R_eq = R_eq + R_ct / (2 * math.pi * r_i * w_d)
|
|
364
394
|
|
|
@@ -379,22 +409,22 @@ class MainPancake3D:
|
|
|
379
409
|
0.1 + estimated_time_constant * 8 + 0.001
|
|
380
410
|
)
|
|
381
411
|
new_solve_object_for_inductance_and_time_constant["winding"] = (
|
|
382
|
-
copy_fdm.magnet.solve.
|
|
412
|
+
copy_fdm.magnet.solve.winding.dict()
|
|
383
413
|
)
|
|
384
414
|
new_solve_object_for_inductance_and_time_constant["contactLayer"] = (
|
|
385
|
-
copy_fdm.magnet.solve.
|
|
415
|
+
copy_fdm.magnet.solve.contactLayer.dict()
|
|
386
416
|
)
|
|
387
417
|
new_solve_object_for_inductance_and_time_constant["terminals"] = (
|
|
388
|
-
copy_fdm.magnet.solve.
|
|
418
|
+
copy_fdm.magnet.solve.terminals.dict()
|
|
389
419
|
)
|
|
390
420
|
solve_object = Pancake3DSolve(
|
|
391
421
|
**new_solve_object_for_inductance_and_time_constant
|
|
392
422
|
)
|
|
393
|
-
solve_object.
|
|
423
|
+
solve_object.quantitiesToBeSaved = [
|
|
394
424
|
Pancake3DSolveSaveQuantity(quantity="timeConstant")
|
|
395
425
|
]
|
|
396
426
|
|
|
397
|
-
solve_object.
|
|
427
|
+
solve_object.contactLayer.resistivity = copy_fdm.magnet.solve.contactLayer.resistivity
|
|
398
428
|
copy_fdm.magnet.solve = solve_object
|
|
399
429
|
|
|
400
430
|
new_power_supply_object_for_time_constant = {
|
|
@@ -431,7 +461,7 @@ class MainPancake3D:
|
|
|
431
461
|
solve.run_getdp(solve=True, postOperation=True)
|
|
432
462
|
|
|
433
463
|
# then compute the time constant and write it to a file:
|
|
434
|
-
# read axialComponentOfTheMagneticFieldForTimeConstant-TimeTableFormat.
|
|
464
|
+
# read axialComponentOfTheMagneticFieldForTimeConstant-TimeTableFormat.txt
|
|
435
465
|
|
|
436
466
|
time_constant_file = os.path.join(
|
|
437
467
|
time_constant_solution_folder,
|
|
@@ -488,6 +518,16 @@ class MainPancake3D:
|
|
|
488
518
|
f.write(str(time_constant))
|
|
489
519
|
|
|
490
520
|
# main solver:
|
|
521
|
+
if "inductance" in save_list:
|
|
522
|
+
for save_object in self.fdm.magnet.solve.quantitiesToBeSaved:
|
|
523
|
+
if save_object.quantity == "inductance":
|
|
524
|
+
self.fdm.magnet.solve.quantitiesToBeSaved.remove(save_object)
|
|
525
|
+
|
|
526
|
+
if "timeConstant" in save_list:
|
|
527
|
+
for save_object in self.fdm.magnet.solve.quantitiesToBeSaved:
|
|
528
|
+
if save_object.quantity == "timeConstant":
|
|
529
|
+
self.fdm.magnet.solve.quantitiesToBeSaved.remove(save_object)
|
|
530
|
+
|
|
491
531
|
solve = Solve(
|
|
492
532
|
self.fdm,
|
|
493
533
|
self.GetDP_path,
|
fiqus/mesh_generators/MeshCCT.py
CHANGED
|
@@ -1,209 +1,209 @@
|
|
|
1
|
-
import math
|
|
2
|
-
import os
|
|
3
|
-
import timeit
|
|
4
|
-
import json
|
|
5
|
-
from typing import List, Any
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
|
|
8
|
-
import gmsh
|
|
9
|
-
import numpy as np
|
|
10
|
-
from fiqus.utils.Utils import FilesAndFolders as uff
|
|
11
|
-
from fiqus.utils.Utils import GmshUtils
|
|
12
|
-
from fiqus.data.DataWindingsCCT import WindingsInformation # for volume information
|
|
13
|
-
from fiqus.data.RegionsModelFiQuS import RegionsModel
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class Mesh:
|
|
17
|
-
def __init__(self, fdm, verbose=True):
|
|
18
|
-
"""
|
|
19
|
-
Class to preparing brep files by adding terminals.
|
|
20
|
-
:param fdm: FiQuS data model
|
|
21
|
-
:param verbose: If True more information is printed in python console.
|
|
22
|
-
"""
|
|
23
|
-
self.cctdm = fdm.magnet
|
|
24
|
-
self.model_folder = os.path.join(os.getcwd())
|
|
25
|
-
self.magnet_name = fdm.general.magnet_name
|
|
26
|
-
|
|
27
|
-
self.geom_folder = Path(self.model_folder).parent
|
|
28
|
-
|
|
29
|
-
self.verbose = verbose
|
|
30
|
-
regions_file = os.path.join(self.geom_folder, f'{self.magnet_name}.regions')
|
|
31
|
-
self.cctrm = uff.read_data_from_yaml(regions_file, RegionsModel)
|
|
32
|
-
winding_info_file = os.path.join(self.geom_folder, f'{self.magnet_name}.wi')
|
|
33
|
-
self.cctwi = uff.read_data_from_yaml(winding_info_file, WindingsInformation)
|
|
34
|
-
self.gu = GmshUtils(self.model_folder, self.verbose)
|
|
35
|
-
self.gu.initialize()
|
|
36
|
-
self.model_file = f"{os.path.join(self.model_folder, self.magnet_name)}.msh"
|
|
37
|
-
self.formers = []
|
|
38
|
-
self.powered_vols = []
|
|
39
|
-
self.air_boundary_tags = []
|
|
40
|
-
|
|
41
|
-
def _find_surf(self, volume_tag):
|
|
42
|
-
for surf in gmsh.model.getBoundary([(3, volume_tag)], oriented=False):
|
|
43
|
-
_, _, zmin, _, _, zmax = gmsh.model.occ.getBoundingBox(*surf)
|
|
44
|
-
z = (zmin + zmax) / 2
|
|
45
|
-
if math.isclose(z, self.cctdm.geometry.air.z_min, rel_tol=1e-5) or math.isclose(z, self.cctdm.geometry.air.z_max, rel_tol=1e-5):
|
|
46
|
-
return surf[1]
|
|
47
|
-
|
|
48
|
-
def generate_physical_groups(self, gui=False):
|
|
49
|
-
if self.verbose:
|
|
50
|
-
print('Generating Physical Groups Started')
|
|
51
|
-
start_time = timeit.default_timer()
|
|
52
|
-
r_types = ['w' for _ in self.cctwi.w_names] + ['f' for _ in self.cctwi.f_names] # needed for picking different pow_surf later on
|
|
53
|
-
vol_max_loop = 0
|
|
54
|
-
|
|
55
|
-
for i, (f_name, r_type, r_name, r_tag) in enumerate(zip(self.cctwi.w_names + self.cctwi.f_names, r_types, self.cctrm.powered['cct'].vol.names, self.cctrm.powered['cct'].vol.numbers)):
|
|
56
|
-
vol_dict = json.load(open(f"{os.path.join(self.geom_folder, f_name)}.vi"))
|
|
57
|
-
volumes_file = np.array(vol_dict['all'])
|
|
58
|
-
vol_max_file = np.max(volumes_file)
|
|
59
|
-
vols_to_use = volumes_file + vol_max_loop
|
|
60
|
-
v_tags = (list(map(int, vols_to_use)))
|
|
61
|
-
vol_max_loop = vol_max_file + vol_max_loop
|
|
62
|
-
self.powered_vols.extend(v_tags) # used later for meshing
|
|
63
|
-
gmsh.model.addPhysicalGroup(dim=3, tags=v_tags, tag=r_tag)
|
|
64
|
-
gmsh.model.setPhysicalName(dim=3, tag=r_tag, name=r_name)
|
|
65
|
-
powered_in_surf = self._find_surf(v_tags[0])
|
|
66
|
-
gmsh.model.addPhysicalGroup(dim=2, tags=[powered_in_surf], tag=self.cctrm.powered['cct'].surf_in.numbers[i])
|
|
67
|
-
gmsh.model.setPhysicalName(dim=2, tag=self.cctrm.powered['cct'].surf_in.numbers[i], name=self.cctrm.powered['cct'].surf_in.names[i])
|
|
68
|
-
powered_out_surf = self._find_surf(v_tags[-1])
|
|
69
|
-
gmsh.model.addPhysicalGroup(dim=2, tags=[powered_out_surf], tag=self.cctrm.powered['cct'].surf_out.numbers[i])
|
|
70
|
-
gmsh.model.setPhysicalName(dim=2, tag=self.cctrm.powered['cct'].surf_out.numbers[i], name=self.cctrm.powered['cct'].surf_out.names[i])
|
|
71
|
-
|
|
72
|
-
vol_max_loop = v_tags[-1]
|
|
73
|
-
|
|
74
|
-
for r_name, r_tag in zip(self.cctrm.induced['cct'].vol.names, self.cctrm.induced['cct'].vol.numbers):
|
|
75
|
-
vol_max_loop += 1
|
|
76
|
-
self.formers.append(vol_max_loop)
|
|
77
|
-
gmsh.model.addPhysicalGroup(dim=3, tags=[vol_max_loop], tag=r_tag)
|
|
78
|
-
gmsh.model.setPhysicalName(dim=3, tag=r_tag, name=r_name)
|
|
79
|
-
|
|
80
|
-
vol_max_loop += 1
|
|
81
|
-
gmsh.model.addPhysicalGroup(dim=3, tags=[vol_max_loop], tag=self.cctrm.air.vol.number)
|
|
82
|
-
gmsh.model.setPhysicalName(dim=3, tag=self.cctrm.air.vol.number, name=self.cctrm.air.vol.name)
|
|
83
|
-
abt = gmsh.model.getEntities(2)[-3:]
|
|
84
|
-
self.air_boundary_tags = [surf[1] for surf in abt]
|
|
85
|
-
gmsh.model.addPhysicalGroup(dim=2, tags=self.air_boundary_tags, tag=self.cctrm.air.surf.number)
|
|
86
|
-
gmsh.model.setPhysicalName(dim=2, tag=self.cctrm.air.surf.number, name=self.cctrm.air.surf.name)
|
|
87
|
-
|
|
88
|
-
# air_line_tags = []
|
|
89
|
-
# for air_boundary_tag in self.air_boundary_tags:
|
|
90
|
-
# air_line_tags.extend(gmsh.model.getBoundary([(2, air_boundary_tag)], oriented=False)[1])
|
|
91
|
-
# self.air_center_line_tags = [int(np.max(air_line_tags) + 1)] # this assumes that the above found the lines of air boundary but not the one in the middle that is just with the subsequent tag
|
|
92
|
-
# gmsh.model.addPhysicalGroup(dim=1, tags=self.air_center_line_tags, tag=self.cctrm.air.line.number)
|
|
93
|
-
# gmsh.model.setPhysicalName(dim=1, tag=self.cctrm.air.line.number, name=self.cctrm.air.line.name)
|
|
94
|
-
|
|
95
|
-
gmsh.model.occ.synchronize()
|
|
96
|
-
if gui:
|
|
97
|
-
self.gu.launch_interactive_GUI()
|
|
98
|
-
if self.verbose:
|
|
99
|
-
print(f'Generating Physical Groups Took {timeit.default_timer() - start_time:.2f} s')
|
|
100
|
-
|
|
101
|
-
def generate_mesh(self, gui=False):
|
|
102
|
-
if self.verbose:
|
|
103
|
-
print('Generating Mesh Started')
|
|
104
|
-
start_time = timeit.default_timer()
|
|
105
|
-
# gmsh.option.setNumber("Mesh.AngleToleranceFacetOverlap", 0.01)
|
|
106
|
-
gmsh.option.setNumber("Mesh.Algorithm", 5)
|
|
107
|
-
gmsh.option.setNumber("Mesh.Algorithm3D", 10)
|
|
108
|
-
gmsh.option.setNumber("Mesh.AllowSwapAngle", 20)
|
|
109
|
-
line_tags_transfinite = []
|
|
110
|
-
num_div_transfinite = []
|
|
111
|
-
line_tags_mesh: List[Any] = []
|
|
112
|
-
point_tags_mesh = []
|
|
113
|
-
for vol_tag in self.powered_vols:
|
|
114
|
-
vol_surf_tags = gmsh.model.getAdjacencies(3, vol_tag)[1]
|
|
115
|
-
for surf_tag in vol_surf_tags:
|
|
116
|
-
line_tags = gmsh.model.getAdjacencies(2, surf_tag)[1]
|
|
117
|
-
# line_tags_mesh.extend(line_tags)
|
|
118
|
-
line_lengths = []
|
|
119
|
-
for line_tag in line_tags:
|
|
120
|
-
point_tags = gmsh.model.getAdjacencies(1, line_tag)[1]
|
|
121
|
-
if line_tag not in line_tags_mesh:
|
|
122
|
-
line_tags_mesh.append(line_tag)
|
|
123
|
-
x = []
|
|
124
|
-
y = []
|
|
125
|
-
z = []
|
|
126
|
-
for p, point_tag in enumerate(point_tags):
|
|
127
|
-
xmin, ymin, zmin, xmax, ymax, zmax = gmsh.model.occ.getBoundingBox(0, point_tag)
|
|
128
|
-
x.append((xmin + xmax) / 2)
|
|
129
|
-
y.append((ymin + ymax) / 2)
|
|
130
|
-
z.append((zmin + zmax) / 2)
|
|
131
|
-
if point_tag not in point_tags_mesh:
|
|
132
|
-
point_tags_mesh.append(point_tag)
|
|
133
|
-
line_lengths.append(math.sqrt((x[0] - x[1]) ** 2 + (y[0] - y[1]) ** 2 + (z[0] - z[1]) ** 2))
|
|
134
|
-
# num_div = math.ceil(dist / self.cctdm.mesh_generators.MeshSizeWindings) + 1
|
|
135
|
-
l_length_min = np.min(line_lengths)
|
|
136
|
-
for line_tag, l_length in zip(line_tags, line_lengths):
|
|
137
|
-
aspect = l_length / l_length_min
|
|
138
|
-
if aspect > self.cctdm.mesh.MaxAspectWindings:
|
|
139
|
-
num_div = math.ceil(l_length / (l_length_min * self.cctdm.mesh.MaxAspectWindings)) + 1
|
|
140
|
-
if line_tag in line_tags_transfinite:
|
|
141
|
-
idx = line_tags_transfinite.index(line_tag)
|
|
142
|
-
num_div_set = num_div_transfinite[idx]
|
|
143
|
-
if num_div_set < num_div:
|
|
144
|
-
num_div_transfinite[idx] = num_div
|
|
145
|
-
else:
|
|
146
|
-
line_tags_transfinite.append(line_tag)
|
|
147
|
-
num_div_transfinite.append(num_div)
|
|
148
|
-
for line_tag, num_div in zip(line_tags_transfinite, num_div_transfinite):
|
|
149
|
-
gmsh.model.mesh.setTransfiniteCurve(line_tag, num_div)
|
|
150
|
-
|
|
151
|
-
gmsh.model.setColor([(1, i) for i in line_tags_mesh], 255, 0, 0) # , recursive=True) # Red
|
|
152
|
-
|
|
153
|
-
# gmsh.model.mesh.setTransfiniteCurve(self.air_center_line_tags[0], 15)
|
|
154
|
-
# gmsh.model.setColor([(1, i) for i in self.air_center_line_tags], 0, 0, 0)
|
|
155
|
-
|
|
156
|
-
sld = gmsh.model.mesh.field.add("Distance") # straight line distance
|
|
157
|
-
gmsh.model.mesh.field.setNumbers(sld, "CurvesList", line_tags_mesh)
|
|
158
|
-
# gmsh.model.mesh_generators.field.setNumbers(1, "PointsList", point_tags_mesh)
|
|
159
|
-
gmsh.model.mesh.field.setNumber(sld, "Sampling", 100)
|
|
160
|
-
slt = gmsh.model.mesh.field.add("Threshold") # straight line threshold
|
|
161
|
-
gmsh.model.mesh.field.setNumber(slt, "InField", sld)
|
|
162
|
-
gmsh.model.mesh.field.setNumber(slt, "SizeMin", self.cctdm.mesh.ThresholdSizeMin)
|
|
163
|
-
gmsh.model.mesh.field.setNumber(slt, "SizeMax", self.cctdm.mesh.ThresholdSizeMax)
|
|
164
|
-
gmsh.model.mesh.field.setNumber(slt, "DistMin", self.cctdm.mesh.ThresholdDistMin)
|
|
165
|
-
gmsh.model.mesh.field.setNumber(slt, "DistMax", self.cctdm.mesh.ThresholdDistMax)
|
|
166
|
-
# gmsh.model.mesh_generators.field.add("Min", 7)
|
|
167
|
-
# gmsh.model.mesh_generators.field.setNumbers(7, "FieldsList", [slt])
|
|
168
|
-
gmsh.model.mesh.field.setAsBackgroundMesh(slt)
|
|
169
|
-
gmsh.option.setNumber("Mesh.MeshSizeFromCurvature", 0)
|
|
170
|
-
gmsh.option.setNumber("Mesh.MeshSizeFromPoints", 0)
|
|
171
|
-
gmsh.option.setNumber("Mesh.MeshSizeExtendFromBoundary", 0)
|
|
172
|
-
gmsh.option.setNumber("Mesh.OptimizeNetgen", 0)
|
|
173
|
-
gmsh.model.mesh.generate(3)
|
|
174
|
-
if self.verbose:
|
|
175
|
-
print(f'Generating Mesh Took {timeit.default_timer() - start_time:.2f} s')
|
|
176
|
-
if gui:
|
|
177
|
-
self.gu.launch_interactive_GUI()
|
|
178
|
-
|
|
179
|
-
def generate_cuts(self, gui=False):
|
|
180
|
-
if self.verbose:
|
|
181
|
-
print('Generating Cuts Started')
|
|
182
|
-
start_time = timeit.default_timer()
|
|
183
|
-
for vol, surf_in, surf_out in zip(self.cctrm.powered['cct'].vol.numbers, self.cctrm.powered['cct'].surf_in.numbers, self.cctrm.powered['cct'].surf_out.numbers):
|
|
184
|
-
gmsh.model.mesh.addHomologyRequest("Cohomology", domainTags=[vol], subdomainTags=[surf_in, surf_out], dims=[1, 2, 3])
|
|
185
|
-
for vol in self.cctrm.induced['cct'].vol.numbers:
|
|
186
|
-
gmsh.model.mesh.addHomologyRequest("Cohomology", domainTags=[vol], dims=[1, 2, 3])
|
|
187
|
-
gmsh.model.mesh.computeHomology()
|
|
188
|
-
if self.verbose:
|
|
189
|
-
print(f'Generating Cuts Took {timeit.default_timer() - start_time:.2f} s')
|
|
190
|
-
if gui:
|
|
191
|
-
self.gu.launch_interactive_GUI()
|
|
192
|
-
|
|
193
|
-
def save_mesh(self, gui=False):
|
|
194
|
-
if self.verbose:
|
|
195
|
-
print('Saving Mesh Started')
|
|
196
|
-
start_time = timeit.default_timer()
|
|
197
|
-
gmsh.write(self.model_file)
|
|
198
|
-
if self.verbose:
|
|
199
|
-
print(f'Saving Mesh Took {timeit.default_timer() - start_time:.2f} s')
|
|
200
|
-
if gui:
|
|
201
|
-
self.gu.launch_interactive_GUI()
|
|
202
|
-
else:
|
|
203
|
-
gmsh.clear()
|
|
204
|
-
gmsh.finalize()
|
|
205
|
-
|
|
206
|
-
def load_mesh(self, gui=False):
|
|
207
|
-
gmsh.open(self.model_file)
|
|
208
|
-
if gui:
|
|
209
|
-
self.gu.launch_interactive_GUI()
|
|
1
|
+
import math
|
|
2
|
+
import os
|
|
3
|
+
import timeit
|
|
4
|
+
import json
|
|
5
|
+
from typing import List, Any
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import gmsh
|
|
9
|
+
import numpy as np
|
|
10
|
+
from fiqus.utils.Utils import FilesAndFolders as uff
|
|
11
|
+
from fiqus.utils.Utils import GmshUtils
|
|
12
|
+
from fiqus.data.DataWindingsCCT import WindingsInformation # for volume information
|
|
13
|
+
from fiqus.data.RegionsModelFiQuS import RegionsModel
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Mesh:
|
|
17
|
+
def __init__(self, fdm, verbose=True):
|
|
18
|
+
"""
|
|
19
|
+
Class to preparing brep files by adding terminals.
|
|
20
|
+
:param fdm: FiQuS data model
|
|
21
|
+
:param verbose: If True more information is printed in python console.
|
|
22
|
+
"""
|
|
23
|
+
self.cctdm = fdm.magnet
|
|
24
|
+
self.model_folder = os.path.join(os.getcwd())
|
|
25
|
+
self.magnet_name = fdm.general.magnet_name
|
|
26
|
+
|
|
27
|
+
self.geom_folder = Path(self.model_folder).parent
|
|
28
|
+
|
|
29
|
+
self.verbose = verbose
|
|
30
|
+
regions_file = os.path.join(self.geom_folder, f'{self.magnet_name}.regions')
|
|
31
|
+
self.cctrm = uff.read_data_from_yaml(regions_file, RegionsModel)
|
|
32
|
+
winding_info_file = os.path.join(self.geom_folder, f'{self.magnet_name}.wi')
|
|
33
|
+
self.cctwi = uff.read_data_from_yaml(winding_info_file, WindingsInformation)
|
|
34
|
+
self.gu = GmshUtils(self.model_folder, self.verbose)
|
|
35
|
+
self.gu.initialize()
|
|
36
|
+
self.model_file = f"{os.path.join(self.model_folder, self.magnet_name)}.msh"
|
|
37
|
+
self.formers = []
|
|
38
|
+
self.powered_vols = []
|
|
39
|
+
self.air_boundary_tags = []
|
|
40
|
+
|
|
41
|
+
def _find_surf(self, volume_tag):
|
|
42
|
+
for surf in gmsh.model.getBoundary([(3, volume_tag)], oriented=False):
|
|
43
|
+
_, _, zmin, _, _, zmax = gmsh.model.occ.getBoundingBox(*surf)
|
|
44
|
+
z = (zmin + zmax) / 2
|
|
45
|
+
if math.isclose(z, self.cctdm.geometry.air.z_min, rel_tol=1e-5) or math.isclose(z, self.cctdm.geometry.air.z_max, rel_tol=1e-5):
|
|
46
|
+
return surf[1]
|
|
47
|
+
|
|
48
|
+
def generate_physical_groups(self, gui=False):
|
|
49
|
+
if self.verbose:
|
|
50
|
+
print('Generating Physical Groups Started')
|
|
51
|
+
start_time = timeit.default_timer()
|
|
52
|
+
r_types = ['w' for _ in self.cctwi.w_names] + ['f' for _ in self.cctwi.f_names] # needed for picking different pow_surf later on
|
|
53
|
+
vol_max_loop = 0
|
|
54
|
+
|
|
55
|
+
for i, (f_name, r_type, r_name, r_tag) in enumerate(zip(self.cctwi.w_names + self.cctwi.f_names, r_types, self.cctrm.powered['cct'].vol.names, self.cctrm.powered['cct'].vol.numbers)):
|
|
56
|
+
vol_dict = json.load(open(f"{os.path.join(self.geom_folder, f_name)}.vi"))
|
|
57
|
+
volumes_file = np.array(vol_dict['all'])
|
|
58
|
+
vol_max_file = np.max(volumes_file)
|
|
59
|
+
vols_to_use = volumes_file + vol_max_loop
|
|
60
|
+
v_tags = (list(map(int, vols_to_use)))
|
|
61
|
+
vol_max_loop = vol_max_file + vol_max_loop
|
|
62
|
+
self.powered_vols.extend(v_tags) # used later for meshing
|
|
63
|
+
gmsh.model.addPhysicalGroup(dim=3, tags=v_tags, tag=r_tag)
|
|
64
|
+
gmsh.model.setPhysicalName(dim=3, tag=r_tag, name=r_name)
|
|
65
|
+
powered_in_surf = self._find_surf(v_tags[0])
|
|
66
|
+
gmsh.model.addPhysicalGroup(dim=2, tags=[powered_in_surf], tag=self.cctrm.powered['cct'].surf_in.numbers[i])
|
|
67
|
+
gmsh.model.setPhysicalName(dim=2, tag=self.cctrm.powered['cct'].surf_in.numbers[i], name=self.cctrm.powered['cct'].surf_in.names[i])
|
|
68
|
+
powered_out_surf = self._find_surf(v_tags[-1])
|
|
69
|
+
gmsh.model.addPhysicalGroup(dim=2, tags=[powered_out_surf], tag=self.cctrm.powered['cct'].surf_out.numbers[i])
|
|
70
|
+
gmsh.model.setPhysicalName(dim=2, tag=self.cctrm.powered['cct'].surf_out.numbers[i], name=self.cctrm.powered['cct'].surf_out.names[i])
|
|
71
|
+
|
|
72
|
+
vol_max_loop = v_tags[-1]
|
|
73
|
+
|
|
74
|
+
for r_name, r_tag in zip(self.cctrm.induced['cct'].vol.names, self.cctrm.induced['cct'].vol.numbers):
|
|
75
|
+
vol_max_loop += 1
|
|
76
|
+
self.formers.append(vol_max_loop)
|
|
77
|
+
gmsh.model.addPhysicalGroup(dim=3, tags=[vol_max_loop], tag=r_tag)
|
|
78
|
+
gmsh.model.setPhysicalName(dim=3, tag=r_tag, name=r_name)
|
|
79
|
+
|
|
80
|
+
vol_max_loop += 1
|
|
81
|
+
gmsh.model.addPhysicalGroup(dim=3, tags=[vol_max_loop], tag=self.cctrm.air.vol.number)
|
|
82
|
+
gmsh.model.setPhysicalName(dim=3, tag=self.cctrm.air.vol.number, name=self.cctrm.air.vol.name)
|
|
83
|
+
abt = gmsh.model.getEntities(2)[-3:]
|
|
84
|
+
self.air_boundary_tags = [surf[1] for surf in abt]
|
|
85
|
+
gmsh.model.addPhysicalGroup(dim=2, tags=self.air_boundary_tags, tag=self.cctrm.air.surf.number)
|
|
86
|
+
gmsh.model.setPhysicalName(dim=2, tag=self.cctrm.air.surf.number, name=self.cctrm.air.surf.name)
|
|
87
|
+
|
|
88
|
+
# air_line_tags = []
|
|
89
|
+
# for air_boundary_tag in self.air_boundary_tags:
|
|
90
|
+
# air_line_tags.extend(gmsh.model.getBoundary([(2, air_boundary_tag)], oriented=False)[1])
|
|
91
|
+
# self.air_center_line_tags = [int(np.max(air_line_tags) + 1)] # this assumes that the above found the lines of air boundary but not the one in the middle that is just with the subsequent tag
|
|
92
|
+
# gmsh.model.addPhysicalGroup(dim=1, tags=self.air_center_line_tags, tag=self.cctrm.air.line.number)
|
|
93
|
+
# gmsh.model.setPhysicalName(dim=1, tag=self.cctrm.air.line.number, name=self.cctrm.air.line.name)
|
|
94
|
+
|
|
95
|
+
gmsh.model.occ.synchronize()
|
|
96
|
+
if gui:
|
|
97
|
+
self.gu.launch_interactive_GUI()
|
|
98
|
+
if self.verbose:
|
|
99
|
+
print(f'Generating Physical Groups Took {timeit.default_timer() - start_time:.2f} s')
|
|
100
|
+
|
|
101
|
+
def generate_mesh(self, gui=False):
|
|
102
|
+
if self.verbose:
|
|
103
|
+
print('Generating Mesh Started')
|
|
104
|
+
start_time = timeit.default_timer()
|
|
105
|
+
# gmsh.option.setNumber("Mesh.AngleToleranceFacetOverlap", 0.01)
|
|
106
|
+
gmsh.option.setNumber("Mesh.Algorithm", 5)
|
|
107
|
+
gmsh.option.setNumber("Mesh.Algorithm3D", 10)
|
|
108
|
+
gmsh.option.setNumber("Mesh.AllowSwapAngle", 20)
|
|
109
|
+
line_tags_transfinite = []
|
|
110
|
+
num_div_transfinite = []
|
|
111
|
+
line_tags_mesh: List[Any] = []
|
|
112
|
+
point_tags_mesh = []
|
|
113
|
+
for vol_tag in self.powered_vols:
|
|
114
|
+
vol_surf_tags = gmsh.model.getAdjacencies(3, vol_tag)[1]
|
|
115
|
+
for surf_tag in vol_surf_tags:
|
|
116
|
+
line_tags = gmsh.model.getAdjacencies(2, surf_tag)[1]
|
|
117
|
+
# line_tags_mesh.extend(line_tags)
|
|
118
|
+
line_lengths = []
|
|
119
|
+
for line_tag in line_tags:
|
|
120
|
+
point_tags = gmsh.model.getAdjacencies(1, line_tag)[1]
|
|
121
|
+
if line_tag not in line_tags_mesh:
|
|
122
|
+
line_tags_mesh.append(line_tag)
|
|
123
|
+
x = []
|
|
124
|
+
y = []
|
|
125
|
+
z = []
|
|
126
|
+
for p, point_tag in enumerate(point_tags):
|
|
127
|
+
xmin, ymin, zmin, xmax, ymax, zmax = gmsh.model.occ.getBoundingBox(0, point_tag)
|
|
128
|
+
x.append((xmin + xmax) / 2)
|
|
129
|
+
y.append((ymin + ymax) / 2)
|
|
130
|
+
z.append((zmin + zmax) / 2)
|
|
131
|
+
if point_tag not in point_tags_mesh:
|
|
132
|
+
point_tags_mesh.append(point_tag)
|
|
133
|
+
line_lengths.append(math.sqrt((x[0] - x[1]) ** 2 + (y[0] - y[1]) ** 2 + (z[0] - z[1]) ** 2))
|
|
134
|
+
# num_div = math.ceil(dist / self.cctdm.mesh_generators.MeshSizeWindings) + 1
|
|
135
|
+
l_length_min = np.min(line_lengths)
|
|
136
|
+
for line_tag, l_length in zip(line_tags, line_lengths):
|
|
137
|
+
aspect = l_length / l_length_min
|
|
138
|
+
if aspect > self.cctdm.mesh.MaxAspectWindings:
|
|
139
|
+
num_div = math.ceil(l_length / (l_length_min * self.cctdm.mesh.MaxAspectWindings)) + 1
|
|
140
|
+
if line_tag in line_tags_transfinite:
|
|
141
|
+
idx = line_tags_transfinite.index(line_tag)
|
|
142
|
+
num_div_set = num_div_transfinite[idx]
|
|
143
|
+
if num_div_set < num_div:
|
|
144
|
+
num_div_transfinite[idx] = num_div
|
|
145
|
+
else:
|
|
146
|
+
line_tags_transfinite.append(line_tag)
|
|
147
|
+
num_div_transfinite.append(num_div)
|
|
148
|
+
for line_tag, num_div in zip(line_tags_transfinite, num_div_transfinite):
|
|
149
|
+
gmsh.model.mesh.setTransfiniteCurve(line_tag, num_div)
|
|
150
|
+
|
|
151
|
+
gmsh.model.setColor([(1, i) for i in line_tags_mesh], 255, 0, 0) # , recursive=True) # Red
|
|
152
|
+
|
|
153
|
+
# gmsh.model.mesh.setTransfiniteCurve(self.air_center_line_tags[0], 15)
|
|
154
|
+
# gmsh.model.setColor([(1, i) for i in self.air_center_line_tags], 0, 0, 0)
|
|
155
|
+
|
|
156
|
+
sld = gmsh.model.mesh.field.add("Distance") # straight line distance
|
|
157
|
+
gmsh.model.mesh.field.setNumbers(sld, "CurvesList", line_tags_mesh)
|
|
158
|
+
# gmsh.model.mesh_generators.field.setNumbers(1, "PointsList", point_tags_mesh)
|
|
159
|
+
gmsh.model.mesh.field.setNumber(sld, "Sampling", 100)
|
|
160
|
+
slt = gmsh.model.mesh.field.add("Threshold") # straight line threshold
|
|
161
|
+
gmsh.model.mesh.field.setNumber(slt, "InField", sld)
|
|
162
|
+
gmsh.model.mesh.field.setNumber(slt, "SizeMin", self.cctdm.mesh.ThresholdSizeMin)
|
|
163
|
+
gmsh.model.mesh.field.setNumber(slt, "SizeMax", self.cctdm.mesh.ThresholdSizeMax)
|
|
164
|
+
gmsh.model.mesh.field.setNumber(slt, "DistMin", self.cctdm.mesh.ThresholdDistMin)
|
|
165
|
+
gmsh.model.mesh.field.setNumber(slt, "DistMax", self.cctdm.mesh.ThresholdDistMax)
|
|
166
|
+
# gmsh.model.mesh_generators.field.add("Min", 7)
|
|
167
|
+
# gmsh.model.mesh_generators.field.setNumbers(7, "FieldsList", [slt])
|
|
168
|
+
gmsh.model.mesh.field.setAsBackgroundMesh(slt)
|
|
169
|
+
gmsh.option.setNumber("Mesh.MeshSizeFromCurvature", 0)
|
|
170
|
+
gmsh.option.setNumber("Mesh.MeshSizeFromPoints", 0)
|
|
171
|
+
gmsh.option.setNumber("Mesh.MeshSizeExtendFromBoundary", 0)
|
|
172
|
+
gmsh.option.setNumber("Mesh.OptimizeNetgen", 0)
|
|
173
|
+
gmsh.model.mesh.generate(3)
|
|
174
|
+
if self.verbose:
|
|
175
|
+
print(f'Generating Mesh Took {timeit.default_timer() - start_time:.2f} s')
|
|
176
|
+
if gui:
|
|
177
|
+
self.gu.launch_interactive_GUI()
|
|
178
|
+
|
|
179
|
+
def generate_cuts(self, gui=False):
|
|
180
|
+
if self.verbose:
|
|
181
|
+
print('Generating Cuts Started')
|
|
182
|
+
start_time = timeit.default_timer()
|
|
183
|
+
for vol, surf_in, surf_out in zip(self.cctrm.powered['cct'].vol.numbers, self.cctrm.powered['cct'].surf_in.numbers, self.cctrm.powered['cct'].surf_out.numbers):
|
|
184
|
+
gmsh.model.mesh.addHomologyRequest("Cohomology", domainTags=[vol], subdomainTags=[surf_in, surf_out], dims=[1, 2, 3])
|
|
185
|
+
for vol in self.cctrm.induced['cct'].vol.numbers:
|
|
186
|
+
gmsh.model.mesh.addHomologyRequest("Cohomology", domainTags=[vol], dims=[1, 2, 3])
|
|
187
|
+
gmsh.model.mesh.computeHomology()
|
|
188
|
+
if self.verbose:
|
|
189
|
+
print(f'Generating Cuts Took {timeit.default_timer() - start_time:.2f} s')
|
|
190
|
+
if gui:
|
|
191
|
+
self.gu.launch_interactive_GUI()
|
|
192
|
+
|
|
193
|
+
def save_mesh(self, gui=False):
|
|
194
|
+
if self.verbose:
|
|
195
|
+
print('Saving Mesh Started')
|
|
196
|
+
start_time = timeit.default_timer()
|
|
197
|
+
gmsh.write(self.model_file)
|
|
198
|
+
if self.verbose:
|
|
199
|
+
print(f'Saving Mesh Took {timeit.default_timer() - start_time:.2f} s')
|
|
200
|
+
if gui:
|
|
201
|
+
self.gu.launch_interactive_GUI()
|
|
202
|
+
else:
|
|
203
|
+
gmsh.clear()
|
|
204
|
+
gmsh.finalize()
|
|
205
|
+
|
|
206
|
+
def load_mesh(self, gui=False):
|
|
207
|
+
gmsh.open(self.model_file)
|
|
208
|
+
if gui:
|
|
209
|
+
self.gu.launch_interactive_GUI()
|