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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. fiqus/MainFiQuS.py +15 -5
  2. fiqus/data/DataConductor.py +301 -0
  3. fiqus/data/DataFiQuS.py +5 -2
  4. fiqus/data/DataFiQuSConductor.py +84 -0
  5. fiqus/data/DataFiQuSConductorAC_Strand.py +565 -0
  6. fiqus/data/DataFiQuSPancake3D.py +149 -39
  7. fiqus/data/RegionsModelFiQuS.py +4 -2
  8. fiqus/geom_generators/GeometryCCT.py +19 -17
  9. fiqus/geom_generators/GeometryConductorAC_Strand.py +1391 -0
  10. fiqus/getdp_runners/RunGetdpConductorAC_Strand.py +202 -0
  11. fiqus/getdp_runners/RunGetdpMultipole.py +4 -4
  12. fiqus/mains/MainConductorAC_Strand.py +133 -0
  13. fiqus/mesh_generators/MeshCCT.py +8 -8
  14. fiqus/mesh_generators/MeshConductorAC_Strand.py +657 -0
  15. fiqus/mesh_generators/MeshMultipole.py +11 -8
  16. fiqus/mesh_generators/MeshPancake3D.py +20 -18
  17. fiqus/plotters/PlotPythonConductorAC.py +840 -0
  18. fiqus/post_processors/PostProcessConductorAC.py +49 -0
  19. fiqus/pro_assemblers/ProAssembler.py +4 -3
  20. fiqus/pro_templates/combined/CCT_template.pro +25 -25
  21. fiqus/pro_templates/combined/ConductorAC_template.pro +1025 -0
  22. fiqus/pro_templates/combined/Multipole_template.pro +5 -5
  23. fiqus/pro_templates/combined/Pancake3D_template.pro +131 -46
  24. fiqus/pro_templates/combined/materials.pro +13 -9
  25. {fiqus-2024.5.2.dist-info → fiqus-2024.6.0.dist-info}/METADATA +2 -1
  26. {fiqus-2024.5.2.dist-info → fiqus-2024.6.0.dist-info}/RECORD +34 -22
  27. {fiqus-2024.5.2.dist-info → fiqus-2024.6.0.dist-info}/WHEEL +1 -1
  28. tests/test_geometry_generators.py +41 -0
  29. tests/test_mesh_generators.py +45 -0
  30. tests/test_solvers.py +52 -0
  31. tests/utils/fiqus_test_classes.py +42 -6
  32. tests/utils/generate_reference_files_ConductorAC.py +57 -0
  33. tests/utils/generate_reference_files_Pancake3D.py +92 -0
  34. {fiqus-2024.5.2.dist-info → fiqus-2024.6.0.dist-info}/top_level.txt +0 -0
@@ -49,6 +49,51 @@ class TestMeshGenerators(FiQuSMeshTests):
49
49
  )
50
50
  self.compare_json_or_yaml_files(regions_file, reference_regions_file)
51
51
 
52
+ def test_ConductorAC_Strand(self):
53
+ """
54
+ Checks if ConductorAC geometry generators work correctly by comparing the number
55
+ of entities in the generated geometry file to the reference file that was
56
+ checked manually.
57
+ """
58
+ model_names = [
59
+ "TEST_CAC_Strand_adaptiveMesh",
60
+ "TEST_CAC_Strand_hexFilaments",
61
+ "TEST_CAC_wireInChannel",
62
+ ]
63
+
64
+ for model_name in model_names:
65
+ with self.subTest(model_name=model_name):
66
+ data_model = self.get_data_model(model_name)
67
+
68
+ # data_model can be modified here if necessary
69
+ # Example:
70
+
71
+ # data_model.magnet.mesh.wi.axne = 30
72
+
73
+ self.generate_mesh(data_model, model_name)
74
+
75
+ # Compare the number of entities with the reference file:
76
+ mesh_file = self.get_path_to_generated_file(
77
+ data_model=data_model, file_name=model_name, file_extension="msh"
78
+ )
79
+ reference_file = self.get_path_to_reference_file(
80
+ data_model=data_model, file_name=model_name, file_extension="msh"
81
+ )
82
+ self.compare_mesh_qualities(mesh_file, reference_file)
83
+
84
+ # Compare the regions files:
85
+ regions_file = self.get_path_to_generated_file(
86
+ data_model=data_model,
87
+ file_name=model_name,
88
+ file_extension="regions",
89
+ )
90
+ reference_regions_file = self.get_path_to_reference_file(
91
+ data_model=data_model,
92
+ file_name=model_name,
93
+ file_extension="regions",
94
+ )
95
+ self.compare_json_or_yaml_files(regions_file, reference_regions_file)
96
+
52
97
 
53
98
  if __name__ == "__main__":
54
99
  unittest.main()
tests/test_solvers.py CHANGED
@@ -90,7 +90,59 @@ class TestSolvers(FiQuSSolverTests):
90
90
  )
91
91
  self.compare_pos_files(pos_file, reference_pos_file)
92
92
 
93
+ def test_ConductorAC_Strand(self):
94
+ """
95
+ Checks if ConductorAC_Strand solvers work correctly by comparing the results to the
96
+ reference results that were checked manually.
97
+ """
98
+ model_names = [
99
+ "TEST_CAC_Strand_hexFilaments",
100
+ "TEST_CAC_wireInChannel",
101
+ ]
102
+ for model_name in model_names:
103
+ with self.subTest(model_name=model_name):
104
+ data_model: FDM = self.get_data_model(model_name)
105
+
106
+ self.solve(data_model, model_name)
107
+
108
+ # Compare the pro files:
109
+ pro_file = self.get_path_to_generated_file(
110
+ data_model=data_model,
111
+ file_name=model_name,
112
+ file_extension="pro",
113
+ )
114
+ reference_pro_file = self.get_path_to_reference_file(
115
+ data_model=data_model,
116
+ file_name=model_name,
117
+ file_extension="pro",
118
+ )
119
+ self.compare_text_files(pro_file, reference_pro_file)
120
+
121
+ # Compare the magnetic flux density files:
122
+ pos_file = self.get_path_to_generated_file(
123
+ data_model=data_model,
124
+ file_name="b",
125
+ file_extension="pos",
126
+ )
127
+ reference_pos_file = self.get_path_to_reference_file(
128
+ data_model=data_model,
129
+ file_name="b",
130
+ file_extension="pos",
131
+ )
132
+ self.compare_pos_files(pos_file, reference_pos_file)
93
133
 
134
+ # Compare the current density files:
135
+ pos_file = self.get_path_to_generated_file(
136
+ data_model=data_model,
137
+ file_name="j",
138
+ file_extension="pos",
139
+ )
140
+ reference_pos_file = self.get_path_to_reference_file(
141
+ data_model=data_model,
142
+ file_name="j",
143
+ file_extension="pos",
144
+ )
145
+ self.compare_pos_files(pos_file, reference_pos_file)
94
146
 
95
147
  if __name__ == "__main__":
96
148
  unittest.main()
@@ -414,7 +414,7 @@ class BaseClassesForTests(unittest.TestCase):
414
414
 
415
415
  return reference_file
416
416
 
417
- def compare_json_or_yaml_files(self, file_1, file_2):
417
+ def compare_json_or_yaml_files(self, file_1, file_2, tolerance=0):
418
418
  """
419
419
  This method compares the contents of two JSON or YAML files. It is used to
420
420
  check that the generated files are the same as the reference.
@@ -436,11 +436,47 @@ class BaseClassesForTests(unittest.TestCase):
436
436
  raise ValueError("The files must be JSON or YAML files!")
437
437
 
438
438
  # Compare the dictionaries:
439
- self.assertDictEqual(
440
- file_1_dictionary,
441
- file_2_dictionary,
442
- msg=f"{file_1} did not match {file_2}!",
443
- )
439
+ if tolerance == 0:
440
+ self.assertDictEqual(
441
+ file_1_dictionary,
442
+ file_2_dictionary,
443
+ msg=f"{file_1} did not match {file_2}!",
444
+ )
445
+ else:
446
+ self.compare_dicts(file_1_dictionary, file_2_dictionary, tolerance)
447
+
448
+ def compare_dicts(self, dict1, dict2, tolerance):
449
+ """
450
+ This method compares the contents of two dictionaries, taking into account
451
+ floating point precision issues.
452
+
453
+ :param dict1: first dictionary to compare
454
+ :type dict1: dict
455
+ :param dict2: second dictionary to compare
456
+ :type dict2: dict
457
+ :param tolerance: tolerance for comparing floating point numbers
458
+ :type tolerance: float
459
+ """
460
+ for key in dict1.keys():
461
+ if key not in dict2:
462
+ self.fail(f'Key "{key}" not in both {dict1} and {dict2}')
463
+ if isinstance(dict1[key], dict):
464
+ self.compare_dicts(dict1[key], dict2[key], tolerance)
465
+ elif isinstance(dict1[key], float): # To handle precision errors in floats
466
+ if not np.isclose(dict1[key], dict2[key], atol=tolerance):
467
+ self.fail(f'Values for key {key} are not close: {dict1[key]} vs {dict2[key]}')
468
+ elif isinstance(dict1[key], list): # To handle precision errors in lists of floats
469
+ if len(dict1[key]) != len(dict2[key]):
470
+ self.fail(f'Lists for key {key} are not the same length')
471
+ for i in range(len(dict1[key])):
472
+ if isinstance(dict1[key][i], float):
473
+ if not np.isclose(dict1[key][i], dict2[key][i], atol=tolerance):
474
+ self.fail(f'Values at index {i} for key {key} are not close: {dict1[key][i]} vs {dict2[key][i]}')
475
+ elif dict1[key][i] != dict2[key][i]:
476
+ self.fail(f'Values at index {i} for key {key} are not equal: {dict1[key][i]} vs {dict2[key][i]}')
477
+ else:
478
+ if dict1[key] != dict2[key]:
479
+ self.fail(f'Values for key {key} are not equal: {dict1[key]} vs {dict2[key]}')
444
480
 
445
481
  def compare_pkl_files(self, file_1, file_2):
446
482
  """
@@ -0,0 +1,57 @@
1
+ import os
2
+ import shutil
3
+ import sys
4
+ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../'))) # Add the path to the fiqus package to the system path
5
+ from fiqus.data.DataFiQuS import FDM
6
+ from fiqus.utils.Utils import FilesAndFolders as Util
7
+ from fiqus import MainFiQuS as mf
8
+
9
+
10
+ # Generate reference files for the models below:
11
+ model_names = [
12
+ "TEST_CAC_Strand_adaptiveMesh",
13
+ "TEST_CAC_Strand_hexFilaments",
14
+ "TEST_CAC_wireInChannel",
15
+ ]
16
+ # The run types for the models above:
17
+ run_types = [
18
+ 'geometry_and_mesh',
19
+ 'start_from_yaml',
20
+ 'start_from_yaml',
21
+ ]
22
+
23
+ for model_name, run_type in zip(model_names, run_types):
24
+ # get path to the input file:
25
+ input_file = os.path.join(
26
+ os.path.dirname(os.path.dirname(__file__)),
27
+ "_inputs",
28
+ model_name,
29
+ f"{model_name}.yaml",
30
+ )
31
+
32
+ # select _references folder as the output folder:
33
+ output_folder = os.path.join(
34
+ os.path.dirname(os.path.dirname(__file__)), "_references", model_name
35
+ )
36
+
37
+ # if the output folder exists, remove it:
38
+ if os.path.exists(output_folder):
39
+ shutil.rmtree(output_folder)
40
+
41
+ # Create the output folder:
42
+ os.makedirs(output_folder)
43
+
44
+ # Cast input yaml file to FDM
45
+ data_model: FDM = Util.read_data_from_yaml(input_file, FDM)
46
+
47
+ data_model.run.overwrite = True
48
+
49
+ # Make the run type start_from_yaml:
50
+ data_model.run.type = run_type
51
+
52
+ fiqus_instance = mf.MainFiQuS(
53
+ fdm=data_model, model_folder=output_folder, input_file_path=input_file
54
+ )
55
+
56
+ # remove fiqus_instance to avoid memory issues:
57
+ del fiqus_instance
@@ -0,0 +1,92 @@
1
+ import os
2
+ import shutil
3
+ import sys
4
+ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../'))) # Add the path to the fiqus package to the system path
5
+ import fiqus.data.DataFiQuSPancake3D as Pancake3D
6
+ from fiqus.data.DataFiQuS import FDM
7
+ from fiqus.utils.Utils import FilesAndFolders as Util
8
+ from fiqus import MainFiQuS as mf
9
+
10
+
11
+ # Generate reference files for the models below:
12
+ model_names = [
13
+ "TEST_Pancake3D_REF",
14
+ "TEST_Pancake3D_REFStructured",
15
+ "TEST_Pancake3D_TSA",
16
+ "TEST_Pancake3D_TSAStructured",
17
+ "TEST_Pancake3D_TSAInsulating"
18
+ ]
19
+
20
+ for model_name in model_names:
21
+ input_file = os.path.join(
22
+ os.path.dirname(os.path.dirname(__file__)),
23
+ "_inputs",
24
+ model_name,
25
+ f"{model_name}.yaml",
26
+ )
27
+ # select _references folder as the output folder:
28
+ output_folder = os.path.join(
29
+ os.path.dirname(os.path.dirname(__file__)), "_references", model_name
30
+ )
31
+
32
+ # Cast input yaml file to FDM
33
+ data_model: FDM = Util.read_data_from_yaml(input_file, FDM)
34
+ data_model.magnet.postproc = Pancake3D.Pancake3DPostprocess()
35
+ data_model.run.overwrite = True
36
+ data_model.run.launch_gui = False
37
+
38
+ # if the output folder exists, remove it and create a new one:
39
+ if os.path.exists(output_folder):
40
+ shutil.rmtree(output_folder)
41
+ # Create the output folder:
42
+ os.makedirs(output_folder)
43
+
44
+ # Solve the same model three times with different solve types:
45
+ solve_types = [
46
+ "weaklyCoupled",
47
+ "stronglyCoupled",
48
+ "electromagnetic",
49
+ ]
50
+ for i, solve_type in enumerate(solve_types):
51
+ if i == 0:
52
+ # Make the run type start_from_yaml:
53
+ data_model.run.type = "start_from_yaml"
54
+ else:
55
+ # Make the run type solve_only:
56
+ data_model.run.type = "solve_only"
57
+
58
+ # data_model.run.type = "post_process"
59
+ # data_model.run.type = "solve_only"
60
+
61
+ data_model.magnet.solve.type = solve_type
62
+ data_model.run.solution = solve_type
63
+ if solve_type in ["weaklyCoupled", "stronglyCoupled"]:
64
+ data_model.magnet.solve.save = [
65
+ Pancake3D.Pancake3DSolveSaveQuantity(
66
+ quantity="magneticField",
67
+ ),
68
+ Pancake3D.Pancake3DSolveSaveQuantity(
69
+ quantity="currentDensity",
70
+ ),
71
+ Pancake3D.Pancake3DSolveSaveQuantity(
72
+ quantity="temperature",
73
+ ),
74
+ ]
75
+ elif solve_type == "electromagnetic":
76
+ data_model.magnet.solve.save = [
77
+ Pancake3D.Pancake3DSolveSaveQuantity(
78
+ quantity="magneticField",
79
+ ),
80
+ Pancake3D.Pancake3DSolveSaveQuantity(
81
+ quantity="currentDensity",
82
+ ),
83
+ ]
84
+
85
+ fiqus_instance = mf.MainFiQuS(fdm=data_model, model_folder=output_folder)
86
+
87
+ # remove fiqus_instance to avoid memory issues:
88
+ del fiqus_instance
89
+
90
+ # # remove logs directory and run_log.csv inside the output folder:
91
+ # shutil.rmtree(os.path.join(output_folder, "logs"))
92
+ # os.remove(os.path.join(output_folder, "run_log.csv"))