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
@@ -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
- geometry.generate_geometry()
151
- geometry.generate_vi_file()
152
-
153
- self.model_file = geometry.brep_file
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.save is not None:
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.save,
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.nls,
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.save = [Pancake3DSolveSaveQuantity(quantity="inductance")]
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.wi.N)):
381
+ for i in range(1, int(copy_fdm.magnet.geometry.winding.numberOfTurns)):
352
382
  r_i = (
353
- copy_fdm.magnet.geometry.wi.r_i
354
- + i * copy_fdm.magnet.geometry.wi.t
355
- + (i - 1) * copy_fdm.magnet.geometry.ii.t
356
- + copy_fdm.magnet.geometry.ii.t / 2
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.wi.h
388
+ w_d = copy_fdm.magnet.geometry.winding.height
359
389
  R_ct = (
360
- copy_fdm.magnet.solve.ii.resistivity
361
- * copy_fdm.magnet.geometry.ii.t
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.wi.dict()
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.ii.dict()
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.ti.dict()
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.save = [
423
+ solve_object.quantitiesToBeSaved = [
394
424
  Pancake3DSolveSaveQuantity(quantity="timeConstant")
395
425
  ]
396
426
 
397
- solve_object.ii.resistivity = copy_fdm.magnet.solve.ii.resistivity
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.csv
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,
@@ -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()