fiqus 2024.6.0__py3-none-any.whl → 2024.12.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. fiqus/MainFiQuS.py +290 -134
  2. fiqus/data/DataConductor.py +301 -301
  3. fiqus/data/DataFiQuS.py +128 -84
  4. fiqus/data/DataFiQuSCCT.py +150 -150
  5. fiqus/data/DataFiQuSConductor.py +84 -84
  6. fiqus/data/DataFiQuSConductorAC_Strand.py +565 -565
  7. fiqus/data/DataFiQuSMultipole.py +716 -42
  8. fiqus/data/DataFiQuSPancake3D.py +737 -278
  9. fiqus/data/DataMultipole.py +180 -15
  10. fiqus/data/DataRoxieParser.py +90 -51
  11. fiqus/data/DataSettings.py +121 -0
  12. fiqus/data/DataWindingsCCT.py +37 -37
  13. fiqus/data/RegionsModelFiQuS.py +18 -6
  14. fiqus/geom_generators/GeometryCCT.py +905 -905
  15. fiqus/geom_generators/GeometryConductorAC_Strand.py +1391 -1391
  16. fiqus/geom_generators/GeometryMultipole.py +1827 -227
  17. fiqus/geom_generators/GeometryPancake3D.py +316 -117
  18. fiqus/geom_generators/GeometryPancake3DUtils.py +549 -0
  19. fiqus/getdp_runners/RunGetdpCCT.py +4 -4
  20. fiqus/getdp_runners/RunGetdpConductorAC_Strand.py +201 -201
  21. fiqus/getdp_runners/RunGetdpMultipole.py +115 -42
  22. fiqus/getdp_runners/RunGetdpPancake3D.py +28 -6
  23. fiqus/mains/MainCCT.py +2 -2
  24. fiqus/mains/MainConductorAC_Strand.py +132 -132
  25. fiqus/mains/MainMultipole.py +113 -62
  26. fiqus/mains/MainPancake3D.py +63 -23
  27. fiqus/mesh_generators/MeshCCT.py +209 -209
  28. fiqus/mesh_generators/MeshConductorAC_Strand.py +656 -656
  29. fiqus/mesh_generators/MeshMultipole.py +1243 -181
  30. fiqus/mesh_generators/MeshPancake3D.py +275 -192
  31. fiqus/parsers/ParserCOND.py +825 -0
  32. fiqus/parsers/ParserDAT.py +16 -16
  33. fiqus/parsers/ParserGetDPOnSection.py +212 -212
  34. fiqus/parsers/ParserGetDPTimeTable.py +134 -134
  35. fiqus/parsers/ParserMSH.py +53 -53
  36. fiqus/parsers/ParserPOS.py +214 -214
  37. fiqus/parsers/ParserRES.py +142 -142
  38. fiqus/plotters/PlotPythonCCT.py +133 -133
  39. fiqus/plotters/PlotPythonConductorAC.py +855 -840
  40. fiqus/plotters/PlotPythonMultipole.py +18 -18
  41. fiqus/post_processors/PostProcessCCT.py +440 -440
  42. fiqus/post_processors/PostProcessConductorAC.py +49 -49
  43. fiqus/post_processors/PostProcessMultipole.py +353 -229
  44. fiqus/post_processors/PostProcessPancake3D.py +8 -13
  45. fiqus/pre_processors/PreProcessCCT.py +175 -175
  46. fiqus/pro_assemblers/ProAssembler.py +14 -6
  47. fiqus/pro_material_functions/ironBHcurves.pro +246 -246
  48. fiqus/pro_templates/combined/CCT_template.pro +274 -274
  49. fiqus/pro_templates/combined/ConductorAC_template.pro +1025 -1025
  50. fiqus/pro_templates/combined/Multipole_template.pro +1694 -126
  51. fiqus/pro_templates/combined/Pancake3D_template.pro +2294 -1103
  52. fiqus/pro_templates/combined/TSA_materials.pro +162 -0
  53. fiqus/pro_templates/combined/materials.pro +36 -18
  54. fiqus/utils/Utils.py +508 -110
  55. fiqus/utils/update_data_settings.py +33 -0
  56. fiqus-2024.12.0.dist-info/METADATA +130 -0
  57. fiqus-2024.12.0.dist-info/RECORD +84 -0
  58. {fiqus-2024.6.0.dist-info → fiqus-2024.12.0.dist-info}/WHEEL +1 -1
  59. tests/test_FiQuS.py +1 -1
  60. tests/test_geometry_generators.py +101 -2
  61. tests/test_mesh_generators.py +154 -1
  62. tests/test_solvers.py +115 -21
  63. tests/utils/fiqus_test_classes.py +85 -21
  64. tests/utils/generate_reference_files_ConductorAC.py +57 -57
  65. tests/utils/generate_reference_files_Pancake3D.py +4 -5
  66. tests/utils/helpers.py +97 -97
  67. fiqus-2024.6.0.dist-info/METADATA +0 -103
  68. fiqus-2024.6.0.dist-info/RECORD +0 -79
  69. {fiqus-2024.6.0.dist-info → fiqus-2024.12.0.dist-info}/top_level.txt +0 -0
@@ -1,202 +1,202 @@
1
- import timeit
2
- import logging
3
- import os
4
- import subprocess
5
- import re
6
- import pandas as pd
7
- import pickle
8
- import numpy as np
9
-
10
- import gmsh
11
-
12
- from fiqus.utils.Utils import GmshUtils, FilesAndFolders
13
- from fiqus.data.RegionsModelFiQuS import RegionsModel
14
- import fiqus.data.DataFiQuSConductor as geom
15
- from fiqus.geom_generators.GeometryConductorAC_Strand import TwistedStrand
16
-
17
- from fiqus.pro_assemblers.ProAssembler import ASS_PRO
18
-
19
- logger = logging.getLogger(__name__)
20
-
21
- class Solve:
22
- def __init__(self, fdm, GetDP_path, geometry_folder, mesh_folder, verbose=True):
23
- self.fdm = fdm
24
- self.cacdm = fdm.magnet
25
- self.GetDP_path = GetDP_path
26
- self.solution_folder = os.path.join(os.getcwd())
27
- self.magnet_name = fdm.general.magnet_name
28
- self.geometry_folder = geometry_folder
29
- self.mesh_folder = mesh_folder
30
- self.mesh_file = os.path.join(self.mesh_folder, f"{self.magnet_name}.msh")
31
- self.pro_file = os.path.join(self.solution_folder, f"{self.magnet_name}.pro")
32
- self.regions_file = os.path.join(mesh_folder, f"{self.magnet_name}.regions")
33
-
34
- self.verbose = verbose
35
- self.gu = GmshUtils(self.solution_folder, self.verbose)
36
- self.gu.initialize()
37
-
38
- self.ass_pro = ASS_PRO(os.path.join(self.solution_folder, self.magnet_name))
39
- self.regions_model = FilesAndFolders.read_data_from_yaml(self.regions_file, RegionsModel)
40
- self.material_properties_model = None
41
-
42
- self.ed = {} # excitation dictionary
43
-
44
- gmsh.option.setNumber("General.Terminal", verbose)
45
-
46
- def read_excitation(self, inputs_folder_path):
47
- """
48
- Function for reading a CSV file for the 'from_file' excitation case.
49
-
50
- :param inputs_folder_path: The full path to the folder with input files.
51
- :type inputs_folder_path: str
52
- """
53
- if self.cacdm.solve.source_parameters.source_type == 'piecewise' and self.cacdm.solve.source_parameters.piecewise.source_csv_file:
54
- input_file = os.path.join(inputs_folder_path, self.cacdm.solve.source_parameters.piecewise.source_csv_file)
55
- print(f'Using excitation from file: {input_file}')
56
- df = pd.read_csv(input_file, delimiter=',', engine='python')
57
- excitation_time = df['time'].to_numpy(dtype='float').tolist()
58
- self.ed['time'] = excitation_time
59
- excitation_value = df['value'].to_numpy(dtype='float').tolist()
60
- self.ed['value'] = excitation_value
61
-
62
- def get_solution_parameters_from_yaml(self, inputs_folder_path):
63
- """
64
- Function for reading material properties from the geometry YAML file.
65
-
66
- This reads the 'solution' section of the YAML file and stores it in the solution folder.
67
- This could also be a place to change the material properties in the future.
68
-
69
- :param inputs_folder_path: The full path to the folder with input files.
70
- :type inputs_folder_path: str
71
- """
72
- if self.cacdm.geometry.io_settings.load.load_from_yaml:
73
- #load the geometry class from the pkl file
74
- geom_save_file = os.path.join(self.geometry_folder, f'{self.magnet_name}.pkl')
75
- with open(geom_save_file, "rb") as geom_save_file:
76
- geometry_class: TwistedStrand = pickle.load(geom_save_file)
77
-
78
- input_yaml_file = os.path.join(inputs_folder_path, self.cacdm.geometry.io_settings.load.filename)
79
- Conductor_dm = FilesAndFolders.read_data_from_yaml(input_yaml_file, geom.Conductor)
80
- solution_parameters = Conductor_dm.Solution
81
-
82
- # The geometry YAML file lists surfaces to exclude from the TI problem by their IDs. Here we convert these IDs to Gmsh physical surface tags.
83
- # This is done by comparing the outer boundary points of the surfaces to exclude with the outer boundary points of the matrix partitions.
84
- surfaces_excluded_from_TI_tags = []
85
-
86
- for surface_ID in solution_parameters.Surfaces_excluded_from_TI: # 1) Find the outer boundary points of the surfaces to exclude
87
- outer_boundary_points_a = []
88
- outer_boundary_curves_a = Conductor_dm.Geometry.Areas[surface_ID].Boundary
89
- for curve_ID in outer_boundary_curves_a:
90
- curve = Conductor_dm.Geometry.Curves[curve_ID]
91
- for point_ID in curve.Points:
92
- point = Conductor_dm.Geometry.Points[point_ID]
93
- outer_boundary_points_a.append(tuple(point.Coordinates))
94
-
95
- for matrix_partition in geometry_class.matrix: # 2) Find the outer boundary points of the matrix partitions
96
- outer_boundary_points_b = []
97
- outer_boundary_curves_b = matrix_partition.boundary_curves
98
- if len(outer_boundary_curves_b) == len(outer_boundary_curves_a): # If the number of boundary curves is different, the surfaces are not the same
99
- for curve in outer_boundary_curves_b:
100
- for point in curve.points:
101
- outer_boundary_points_b.append(tuple(point.pos))
102
-
103
- if np.allclose(sorted(outer_boundary_points_a), sorted(outer_boundary_points_b)): # If the outer boundary points are the same, the surfaces are the same
104
- surfaces_excluded_from_TI_tags.append(matrix_partition.physical_surface_tag) # 3) Add the physical surface tag to the list of surfaces to exclude
105
- break
106
-
107
- solution_parameters.Surfaces_excluded_from_TI = surfaces_excluded_from_TI_tags # Replace the surface IDs with the physical surface tags
108
-
109
- FilesAndFolders.write_data_to_yaml(os.path.join(self.solution_folder, "MaterialProperties.yaml"), solution_parameters.dict())
110
- self.material_properties_model = solution_parameters
111
-
112
-
113
-
114
-
115
- def assemble_pro(self):
116
- print("Assembling .pro file")
117
- self.ass_pro.assemble_combined_pro(template = self.cacdm.solve.pro_template, rm = self.regions_model, dm = self.fdm, ed=self.ed, mp=self.material_properties_model)
118
-
119
- def run_getdp(self, solve = True, postOperation = True, gui = False):
120
-
121
- command = ["-v2", "-verbose", "3"]
122
- if solve:
123
- command += ["-solve", "MagDyn"]
124
- if self.cacdm.solve.formulation_parameters.dynamic_correction:
125
- command += ["-pos", "MagDyn", "MagDyn_dynCorr"]
126
- else:
127
- command += ["-pos", "MagDyn"]
128
-
129
-
130
- startTime = timeit.default_timer()
131
- getdpProcess = subprocess.Popen([self.GetDP_path, self.pro_file, "-msh", self.mesh_file] + command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
132
-
133
- with getdpProcess.stdout:
134
- for line in iter(getdpProcess.stdout.readline, b""):
135
- line = line.decode("utf-8").rstrip()
136
- line = line.split("\r")[-1]
137
- if not "Test" in line:
138
- if line.startswith("Info"):
139
- parsedLine = re.sub(r"Info\s+:\s+", "", line)
140
- logger.info(parsedLine)
141
- elif line.startswith("Warning"):
142
- parsedLine = re.sub(r"Warning\s+:\s+", "", line)
143
- logger.warning(parsedLine)
144
- elif line.startswith("Error"):
145
- parsedLine = re.sub(r"Error\s+:\s+", "", line)
146
- logger.error(parsedLine)
147
- logger.error("Solving CAC failed.")
148
- # raise Exception(parsedLine)
149
- elif re.match("##", line):
150
- logger.critical(line)
151
- else:
152
- logger.info(line)
153
-
154
- simulation_time = timeit.default_timer()-startTime
155
- # Save simulation time:
156
- if solve:
157
- logger.info(f"Solving CAC_1 has finished in {round(simulation_time, 3)} seconds.")
158
- with open(os.path.join(self.solution_folder, 'test_temporary', 'simulation_time.txt'), 'w') as file:
159
- file.write(str(simulation_time))
160
-
161
-
162
- if gui and ((postOperation and not solve) or (solve and postOperation and self.cacdm.postproc.generate_pos_files)):
163
- # gmsh.option.setNumber("Geometry.Volumes", 1)
164
- # gmsh.option.setNumber("Geometry.Surfaces", 1)
165
- # gmsh.option.setNumber("Geometry.Curves", 1)
166
- # gmsh.option.setNumber("Geometry.Points", 0)
167
- posFiles = [
168
- fileName
169
- for fileName in os.listdir(self.solution_folder)
170
- if fileName.endswith(".pos")
171
- ]
172
- for posFile in posFiles:
173
- gmsh.open(os.path.join(self.solution_folder, posFile))
174
- self.gu.launch_interactive_GUI()
175
- else:
176
- gmsh.clear()
177
- gmsh.finalize()
178
-
179
- def cleanup(self):
180
- """
181
- This funtion is used to remove .msh, .pre and .res files from the solution folder, as they may be large and not needed.
182
- """
183
- magnet_name = self.fdm.general.magnet_name
184
- cleanup = self.cacdm.postproc.cleanup
185
-
186
- if cleanup.remove_res_file:
187
- res_file_path = os.path.join(self.solution_folder, f"{magnet_name}.res")
188
- if os.path.exists(res_file_path):
189
- os.remove(res_file_path)
190
- logger.info(f"Removed {magnet_name}.res")
191
-
192
- if cleanup.remove_pre_file:
193
- pre_file_path = os.path.join(self.solution_folder, f"{magnet_name}.pre")
194
- if os.path.exists(pre_file_path):
195
- os.remove(pre_file_path)
196
- logger.info(f"Removed {magnet_name}.pre")
197
-
198
- if cleanup.remove_msh_file:
199
- msh_file_path = os.path.join(self.mesh_folder, f"{magnet_name}.msh")
200
- if os.path.exists(msh_file_path):
201
- os.remove(msh_file_path)
1
+ import timeit
2
+ import logging
3
+ import os
4
+ import subprocess
5
+ import re
6
+ import pandas as pd
7
+ import pickle
8
+ import numpy as np
9
+
10
+ import gmsh
11
+
12
+ from fiqus.utils.Utils import GmshUtils, FilesAndFolders
13
+ from fiqus.data.RegionsModelFiQuS import RegionsModel
14
+ import fiqus.data.DataFiQuSConductor as geom
15
+ from fiqus.geom_generators.GeometryConductorAC_Strand import TwistedStrand
16
+
17
+ from fiqus.pro_assemblers.ProAssembler import ASS_PRO
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ class Solve:
22
+ def __init__(self, fdm, GetDP_path, geometry_folder, mesh_folder, verbose=True):
23
+ self.fdm = fdm
24
+ self.cacdm = fdm.magnet
25
+ self.GetDP_path = GetDP_path
26
+ self.solution_folder = os.path.join(os.getcwd())
27
+ self.magnet_name = fdm.general.magnet_name
28
+ self.geometry_folder = geometry_folder
29
+ self.mesh_folder = mesh_folder
30
+ self.mesh_file = os.path.join(self.mesh_folder, f"{self.magnet_name}.msh")
31
+ self.pro_file = os.path.join(self.solution_folder, f"{self.magnet_name}.pro")
32
+ self.regions_file = os.path.join(mesh_folder, f"{self.magnet_name}.regions")
33
+
34
+ self.verbose = verbose
35
+ self.gu = GmshUtils(self.solution_folder, self.verbose)
36
+ self.gu.initialize()
37
+
38
+ self.ass_pro = ASS_PRO(os.path.join(self.solution_folder, self.magnet_name))
39
+ self.regions_model = FilesAndFolders.read_data_from_yaml(self.regions_file, RegionsModel)
40
+ self.material_properties_model = None
41
+
42
+ self.ed = {} # excitation dictionary
43
+
44
+ gmsh.option.setNumber("General.Terminal", verbose)
45
+
46
+ def read_excitation(self, inputs_folder_path):
47
+ """
48
+ Function for reading a CSV file for the 'from_file' excitation case.
49
+
50
+ :param inputs_folder_path: The full path to the folder with input files.
51
+ :type inputs_folder_path: str
52
+ """
53
+ if self.cacdm.solve.source_parameters.source_type == 'piecewise' and self.cacdm.solve.source_parameters.piecewise.source_csv_file:
54
+ input_file = os.path.join(inputs_folder_path, self.cacdm.solve.source_parameters.piecewise.source_csv_file)
55
+ print(f'Using excitation from file: {input_file}')
56
+ df = pd.read_csv(input_file, delimiter=',', engine='python')
57
+ excitation_time = df['time'].to_numpy(dtype='float').tolist()
58
+ self.ed['time'] = excitation_time
59
+ excitation_value = df['value'].to_numpy(dtype='float').tolist()
60
+ self.ed['value'] = excitation_value
61
+
62
+ def get_solution_parameters_from_yaml(self, inputs_folder_path):
63
+ """
64
+ Function for reading material properties from the geometry YAML file.
65
+
66
+ This reads the 'solution' section of the YAML file and stores it in the solution folder.
67
+ This could also be a place to change the material properties in the future.
68
+
69
+ :param inputs_folder_path: The full path to the folder with input files.
70
+ :type inputs_folder_path: str
71
+ """
72
+ if self.cacdm.geometry.io_settings.load.load_from_yaml:
73
+ #load the geometry class from the pkl file
74
+ geom_save_file = os.path.join(self.geometry_folder, f'{self.magnet_name}.pkl')
75
+ with open(geom_save_file, "rb") as geom_save_file:
76
+ geometry_class: TwistedStrand = pickle.load(geom_save_file)
77
+
78
+ input_yaml_file = os.path.join(inputs_folder_path, self.cacdm.geometry.io_settings.load.filename)
79
+ Conductor_dm = FilesAndFolders.read_data_from_yaml(input_yaml_file, geom.Conductor)
80
+ solution_parameters = Conductor_dm.Solution
81
+
82
+ # The geometry YAML file lists surfaces to exclude from the TI problem by their IDs. Here we convert these IDs to Gmsh physical surface tags.
83
+ # This is done by comparing the outer boundary points of the surfaces to exclude with the outer boundary points of the matrix partitions.
84
+ surfaces_excluded_from_TI_tags = []
85
+
86
+ for surface_ID in solution_parameters.Surfaces_excluded_from_TI: # 1) Find the outer boundary points of the surfaces to exclude
87
+ outer_boundary_points_a = []
88
+ outer_boundary_curves_a = Conductor_dm.Geometry.Areas[surface_ID].Boundary
89
+ for curve_ID in outer_boundary_curves_a:
90
+ curve = Conductor_dm.Geometry.Curves[curve_ID]
91
+ for point_ID in curve.Points:
92
+ point = Conductor_dm.Geometry.Points[point_ID]
93
+ outer_boundary_points_a.append(tuple(point.Coordinates))
94
+
95
+ for matrix_partition in geometry_class.matrix: # 2) Find the outer boundary points of the matrix partitions
96
+ outer_boundary_points_b = []
97
+ outer_boundary_curves_b = matrix_partition.boundary_curves
98
+ if len(outer_boundary_curves_b) == len(outer_boundary_curves_a): # If the number of boundary curves is different, the surfaces are not the same
99
+ for curve in outer_boundary_curves_b:
100
+ for point in curve.points:
101
+ outer_boundary_points_b.append(tuple(point.pos))
102
+
103
+ if np.allclose(sorted(outer_boundary_points_a), sorted(outer_boundary_points_b)): # If the outer boundary points are the same, the surfaces are the same
104
+ surfaces_excluded_from_TI_tags.append(matrix_partition.physical_surface_tag) # 3) Add the physical surface tag to the list of surfaces to exclude
105
+ break
106
+
107
+ solution_parameters.Surfaces_excluded_from_TI = surfaces_excluded_from_TI_tags # Replace the surface IDs with the physical surface tags
108
+
109
+ FilesAndFolders.write_data_to_yaml(os.path.join(self.solution_folder, "MaterialProperties.yaml"), solution_parameters.dict())
110
+ self.material_properties_model = solution_parameters
111
+
112
+
113
+
114
+
115
+ def assemble_pro(self):
116
+ print("Assembling .pro file")
117
+ self.ass_pro.assemble_combined_pro(template = self.cacdm.solve.pro_template, rm = self.regions_model, dm = self.fdm, ed=self.ed, mp=self.material_properties_model)
118
+
119
+ def run_getdp(self, solve = True, postOperation = True, gui = False):
120
+
121
+ command = ["-v2", "-verbose", "3"]
122
+ if solve:
123
+ command += ["-solve", "MagDyn"]
124
+ if self.cacdm.solve.formulation_parameters.dynamic_correction:
125
+ command += ["-pos", "MagDyn", "MagDyn_dynCorr"]
126
+ else:
127
+ command += ["-pos", "MagDyn"]
128
+
129
+
130
+ startTime = timeit.default_timer()
131
+ getdpProcess = subprocess.Popen([self.GetDP_path, self.pro_file, "-msh", self.mesh_file] + command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
132
+
133
+ with getdpProcess.stdout:
134
+ for line in iter(getdpProcess.stdout.readline, b""):
135
+ line = line.decode("utf-8").rstrip()
136
+ line = line.split("\r")[-1]
137
+ if not "Test" in line:
138
+ if line.startswith("Info"):
139
+ parsedLine = re.sub(r"Info\s+:\s+", "", line)
140
+ logger.info(parsedLine)
141
+ elif line.startswith("Warning"):
142
+ parsedLine = re.sub(r"Warning\s+:\s+", "", line)
143
+ logger.warning(parsedLine)
144
+ elif line.startswith("Error"):
145
+ parsedLine = re.sub(r"Error\s+:\s+", "", line)
146
+ logger.error(parsedLine)
147
+ logger.error("Solving CAC failed.")
148
+ # raise Exception(parsedLine)
149
+ elif re.match("##", line):
150
+ logger.critical(line)
151
+ else:
152
+ logger.info(line)
153
+
154
+ simulation_time = timeit.default_timer()-startTime
155
+ # Save simulation time:
156
+ if solve:
157
+ logger.info(f"Solving CAC_1 has finished in {round(simulation_time, 3)} seconds.")
158
+ with open(os.path.join(self.solution_folder, 'test_temporary', 'simulation_time.txt'), 'w') as file:
159
+ file.write(str(simulation_time))
160
+
161
+
162
+ if gui and ((postOperation and not solve) or (solve and postOperation and self.cacdm.postproc.generate_pos_files)):
163
+ # gmsh.option.setNumber("Geometry.Volumes", 1)
164
+ # gmsh.option.setNumber("Geometry.Surfaces", 1)
165
+ # gmsh.option.setNumber("Geometry.Curves", 1)
166
+ # gmsh.option.setNumber("Geometry.Points", 0)
167
+ posFiles = [
168
+ fileName
169
+ for fileName in os.listdir(self.solution_folder)
170
+ if fileName.endswith(".pos")
171
+ ]
172
+ for posFile in posFiles:
173
+ gmsh.open(os.path.join(self.solution_folder, posFile))
174
+ self.gu.launch_interactive_GUI()
175
+ else:
176
+ gmsh.clear()
177
+ gmsh.finalize()
178
+
179
+ def cleanup(self):
180
+ """
181
+ This funtion is used to remove .msh, .pre and .res files from the solution folder, as they may be large and not needed.
182
+ """
183
+ magnet_name = self.fdm.general.magnet_name
184
+ cleanup = self.cacdm.postproc.cleanup
185
+
186
+ if cleanup.remove_res_file:
187
+ res_file_path = os.path.join(self.solution_folder, f"{magnet_name}.res")
188
+ if os.path.exists(res_file_path):
189
+ os.remove(res_file_path)
190
+ logger.info(f"Removed {magnet_name}.res")
191
+
192
+ if cleanup.remove_pre_file:
193
+ pre_file_path = os.path.join(self.solution_folder, f"{magnet_name}.pre")
194
+ if os.path.exists(pre_file_path):
195
+ os.remove(pre_file_path)
196
+ logger.info(f"Removed {magnet_name}.pre")
197
+
198
+ if cleanup.remove_msh_file:
199
+ msh_file_path = os.path.join(self.mesh_folder, f"{magnet_name}.msh")
200
+ if os.path.exists(msh_file_path):
201
+ os.remove(msh_file_path)
202
202
  logger.info(f"Removed {magnet_name}.msh")
@@ -1,13 +1,19 @@
1
1
  import os
2
2
  import pathlib
3
+ import subprocess
4
+ import logging
5
+ import timeit
6
+ import re
3
7
 
4
8
  import gmsh
5
-
6
9
  from fiqus.pro_assemblers.ProAssembler import ASS_PRO as aP
7
10
  from fiqus.utils.Utils import GmshUtils
8
11
  from fiqus.utils.Utils import FilesAndFolders as Util
9
12
  from fiqus.data import DataFiQuS as dF
10
- from fiqus.data.RegionsModelFiQuS import RegionsModel as rM
13
+ from fiqus.data import RegionsModelFiQuS as rM
14
+ from fiqus.data import DataMultipole as dM
15
+
16
+ logger = logging.getLogger(__name__)
11
17
 
12
18
 
13
19
  class AssignNaming:
@@ -19,75 +25,142 @@ class AssignNaming:
19
25
  self.data: dF.FDM() = data
20
26
 
21
27
  self.naming_conv = {'omega': 'Omega', 'boundary': 'Bd_', 'powered': '_p', 'induced': '_i', 'air': '_a',
22
- 'air_far_field': '_aff', 'iron': '_bh', 'conducting': '_c', 'terms': 'Terms'}
23
- self.data.magnet.postproc.volumes = \
24
- [self.naming_conv['omega'] + self.naming_conv[var] if not var == 'omega'
25
- else self.naming_conv['omega'] for var in self.data.magnet.postproc.volumes]
28
+ 'air_far_field': '_aff', 'iron': '_bh', 'conducting': '_c', 'insulator': '_ins', 'terms': 'Terms'}
29
+ self.data.magnet.postproc.electromagnetics.volumes = \
30
+ [self.naming_conv['omega'] + (self.naming_conv[var] if var != 'omega' else '') for var in self.data.magnet.postproc.electromagnetics.volumes]
31
+ self.data.magnet.postproc.thermal.volumes = \
32
+ [self.naming_conv['omega'] + (self.naming_conv[var] if var != 'omega' else '') for var in self.data.magnet.postproc.thermal.volumes]
26
33
 
27
34
 
28
35
  class RunGetdpMultipole:
29
- def __init__(self, data: AssignNaming = None, sett: dF.FiQuSSettings() = None, solution_folder: str = None,
30
- settings: dict = None, verbose: bool = False):
36
+ def __init__(self, data: AssignNaming = None, solution_folder: str = None, GetDP_path: str = None, verbose: bool = False):
31
37
  """
32
38
  Class to solve pro file
33
39
  :param data: FiQuS data model
34
- :param sett: settings data model
40
+ :param GetDP_path: settings data model
35
41
  :param verbose: If True more information is printed in python console.
36
42
  """
43
+ logger.info(
44
+ f"Initializing Multipole runner for {os.path.basename(solution_folder)}."
45
+ )
37
46
  self.data: dF.FDM() = data.data
38
47
  self.naming_conv: dict = data.naming_conv
39
- self.set: dF.FiQuSSettings() = sett
40
48
  self.solution_folder = solution_folder
41
- self.settings = settings
49
+ self.GetDP_path = GetDP_path
42
50
  self.verbose: bool = verbose
51
+ self.call_method = 'subprocess' # or onelab
43
52
 
44
- self.rm = rM()
53
+ self.rm_EM = rM.RegionsModel()
54
+ self.rm_TH = rM.RegionsModel()
55
+ self.rc = dM.MultipoleRegionCoordinate()\
56
+ if self.data.magnet.mesh.thermal.isothermal_conductors and self.data.magnet.solve.thermal.solve_type else None
45
57
 
46
58
  self.gu = GmshUtils(self.solution_folder, self.verbose)
47
- self.gu.initialize()
59
+ self.gu.initialize(verbosity_Gmsh=self.data.run.verbosity_Gmsh)
48
60
  self.occ = gmsh.model.occ
49
61
  self.mesh = gmsh.model.mesh
50
62
 
51
63
  self.brep_iron_curves = {1: set(), 2: set(), 3: set(), 4: set()}
52
- self.mesh_folder = os.path.dirname(self.solution_folder)
64
+ self.mesh_files = os.path.join(os.path.dirname(self.solution_folder), self.data.general.magnet_name)
53
65
  self.model_file = os.path.join(self.solution_folder, 'Center_line.csv')
54
66
 
55
- self.II = (self.set.Model_Data_GS.general_parameters.I_ref[0] if self.data.magnet.postproc.compare_to_ROXIE
56
- else self.data.magnet.solve.I_initial[0])
67
+ self.mf = {'EM': f"{self.mesh_files}_EM.msh", 'TH': f"{self.mesh_files}_TH.msh"}
57
68
 
58
- def loadRegionFile(self):
59
- self.rm = Util.read_data_from_yaml(f"{os.path.join(self.mesh_folder, self.data.general.magnet_name)}.reg", rM)
69
+ def loadRegionFiles(self):
70
+ if self.data.magnet.solve.electromagnetics.solve_type:
71
+ self.rm_EM = Util.read_data_from_yaml(f"{self.mesh_files}_EM.reg", rM.RegionsModel)
72
+ if self.data.magnet.solve.thermal.solve_type:
73
+ self.rm_TH = Util.read_data_from_yaml(f"{self.mesh_files}_TH.reg", rM.RegionsModel)
60
74
 
61
- def assemblePro(self):
62
- self.rm.powered['Multipole'].vol.currents = []
63
- for name in self.rm.powered['Multipole'].vol.names:
64
- if name[-3:] == 'pos':
65
- self.rm.powered['Multipole'].vol.currents.append(self.II)
66
- else:
67
- self.rm.powered['Multipole'].vol.currents.append(-self.II)
75
+ def loadRegionCoordinateFile(self):
76
+ self.rc = Util.read_data_from_yaml(f"{self.mesh_files}_TH.reco", dM.MultipoleRegionCoordinate)
68
77
 
69
- ap = aP(file_base_path=os.path.join(self.solution_folder, self.data.general.magnet_name),
70
- naming_conv=self.naming_conv)
78
+ def assemblePro(self):
79
+ logger.info(f"Assembling pro file...")
80
+ start_time = timeit.default_timer()
81
+ ap = aP(file_base_path=os.path.join(self.solution_folder, self.data.general.magnet_name), naming_conv=self.naming_conv)
71
82
  BH_curves_path = os.path.join(pathlib.Path(os.path.dirname(__file__)).parent, 'pro_material_functions', 'ironBHcurves.pro')
72
- ap.assemble_combined_pro(template=self.data.magnet.solve.pro_template, rm=self.rm, dm=self.data.magnet, BH_curves_path=BH_curves_path)
83
+ ap.assemble_combined_pro(template='Multipole_template.pro', rm_EM=self.rm_EM, rm_TH=self.rm_TH, rc=self.rc, dm=self.data, mf=self.mf, BH_curves_path=BH_curves_path)
84
+ logger.info(
85
+ f"Assembling pro file took"
86
+ f" {timeit.default_timer() - start_time:.2f} s."
87
+ )
73
88
 
74
89
  def solve_and_postprocess(self):
75
- command = "-solve -v2 -pos"
76
- self._run(command=command)
90
+ commands = None
91
+ if self.call_method == 'onelab':
92
+ commands = f"-solve -v2 -pos -verbose {self.data.run.verbosity_GetDP}"
93
+ elif self.call_method == 'subprocess':
94
+ commands = []
95
+ commands.append(["-solve", 'resolution', "-v2", "-pos", "Dummy", "-verbose", str(self.data.run.verbosity_GetDP)])
96
+
97
+ self._run(commands=commands)
77
98
 
78
99
  def postprocess(self):
79
- command = "-v2 -pos"
80
- self._run(command=command)
81
-
82
- def _run(self, command):
83
- gmsh.onelab.run(f"{self.data.general.magnet_name}",
84
- f"{self.settings['GetDP_path']} "
85
- f"{os.path.join(self.solution_folder, self.data.general.magnet_name)}.pro "
86
- f"{command} -msh {os.path.join(self.mesh_folder, self.data.general.magnet_name)}.msh")
87
- gmsh.onelab.setChanged("GetDP", 0)
88
- # view_tag = gmsh.view.getTags() # this should be b
89
- # # # v = "View[" + str(gmsh.view.getIndex('b')) + "]"
90
- # gmsh.view.write(view_tag, f"{os.path.join(self.solution_folder, self.data.general.magnet_name)}-view.msh")
100
+ if self.call_method == 'onelab':
101
+ command = "-v2 -pos -verbose {self.data.run.verbosity_GetDP}"
102
+ elif self.call_method == 'subprocess':
103
+ command = [["-v2", "-pos", "-verbose", str(self.data.run.verbosity_GetDP)]]
104
+ self._run(commands=command)
105
+
106
+ def _run(self, commands):
107
+ logger.info("Solving...")
108
+ start_time = timeit.default_timer()
109
+ if self.call_method == 'onelab':
110
+ for command in commands:
111
+ gmsh.onelab.run(f"{self.data.general.magnet_name}",
112
+ f"{self.GetDP_path} {os.path.join(self.solution_folder, self.data.general.magnet_name)}.pro {command}")
113
+ gmsh.onelab.setChanged("GetDP", 0)
114
+ elif self.call_method == 'subprocess':
115
+ # subprocess.call([f"{self.GetDP_path}", f"{os.path.join(self.solution_folder, self.data.general.magnet_name)}.pro"] + command + ["-msh", f"{self.mesh_files}.msh"])
116
+
117
+ # view_tag = gmsh.view.getTags() # this should be b
118
+ # # # v = "View[" + str(gmsh.view.getIndex('b')) + "]"
119
+ # gmsh.view.write(view_tag, f"{os.path.join(self.solution_folder, self.data.general.magnet_name)}-view.msh")
120
+
121
+ if self.data.magnet.solve.noOfMPITasks:
122
+ mpi_prefix = ["mpiexec", "-np", str(self.data.magnet.solve.noOfMPITasks)]
123
+ else:
124
+ mpi_prefix = []
125
+
126
+ for command in commands:
127
+ getdpProcess = subprocess.Popen(
128
+ mpi_prefix + [f"{self.GetDP_path}", f"{os.path.join(self.solution_folder, self.data.general.magnet_name)}.pro"] +
129
+ command,
130
+ stdout=subprocess.PIPE,
131
+ stderr=subprocess.STDOUT,
132
+ )
133
+ with getdpProcess.stdout:
134
+ for line in iter(getdpProcess.stdout.readline, b""):
135
+ line = line.decode("utf-8").rstrip()
136
+ line = line.split("\r")[-1]
137
+ if "Info" in line:
138
+ parsedLine = re.sub(r"Info\s*:\s*", "", line)
139
+ logger.info(parsedLine)
140
+ elif "Warning" in line:
141
+ parsedLine = re.sub(r"Warning\s*:\s*", "", line)
142
+ if "Unknown" not in parsedLine:
143
+ logger.warning(parsedLine)
144
+ elif "Error" in line:
145
+ parsedLine = re.sub(r"Error\s*:\s*", "", line)
146
+ logger.error(parsedLine)
147
+ raise Exception(parsedLine)
148
+ elif "Critical" in line:
149
+ parsedLine = re.sub(r"Critical\s*:\s*", "", line)
150
+ logger.critical(parsedLine)
151
+ # catch the maximum temperature line
152
+ elif "Maximum temperature" in line:
153
+ parsedLine = re.sub(r"Print\s*:\s*", "", line)
154
+ logger.info(parsedLine)
155
+ # this activates the debugging message mode
156
+ elif self.data.run.verbosity_GetDP > 99:
157
+ logger.info(line)
158
+
159
+ getdpProcess.wait()
160
+
161
+ logger.info(
162
+ f"Solving took {timeit.default_timer() - start_time:.2f} s."
163
+ )
91
164
 
92
165
  def ending_step(self, gui: bool = False):
93
166
  if gui: