fiqus 2026.1.0__py3-none-any.whl → 2026.1.2__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 +1 -8
- fiqus/data/DataConductor.py +4 -8
- fiqus/data/DataFiQuSMultipole.py +358 -167
- fiqus/data/DataModelCommon.py +30 -15
- fiqus/data/DataMultipole.py +33 -10
- fiqus/data/DataWindingsCCT.py +37 -37
- fiqus/data/RegionsModelFiQuS.py +1 -1
- fiqus/geom_generators/GeometryMultipole.py +751 -54
- fiqus/getdp_runners/RunGetdpMultipole.py +181 -31
- fiqus/mains/MainMultipole.py +109 -17
- fiqus/mesh_generators/MeshCCT.py +209 -209
- fiqus/mesh_generators/MeshMultipole.py +938 -263
- fiqus/parsers/ParserCOND.py +2 -1
- 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/ParserRES.py +142 -142
- fiqus/plotters/PlotPythonCCT.py +133 -133
- fiqus/plotters/PlotPythonMultipole.py +18 -18
- fiqus/post_processors/PostProcessMultipole.py +16 -6
- fiqus/pre_processors/PreProcessCCT.py +175 -175
- fiqus/pro_assemblers/ProAssembler.py +3 -3
- fiqus/pro_material_functions/ironBHcurves.pro +246 -246
- fiqus/pro_templates/combined/CC_Module.pro +1213 -0
- fiqus/pro_templates/combined/ConductorAC_template.pro +1025 -0
- fiqus/pro_templates/combined/Multipole_template.pro +2738 -1338
- fiqus/pro_templates/combined/TSA_materials.pro +102 -2
- fiqus/pro_templates/combined/materials.pro +54 -3
- fiqus/utils/Utils.py +18 -25
- fiqus/utils/update_data_settings.py +1 -1
- {fiqus-2026.1.0.dist-info → fiqus-2026.1.2.dist-info}/METADATA +64 -68
- {fiqus-2026.1.0.dist-info → fiqus-2026.1.2.dist-info}/RECORD +42 -40
- {fiqus-2026.1.0.dist-info → fiqus-2026.1.2.dist-info}/WHEEL +1 -1
- tests/test_geometry_generators.py +29 -32
- tests/test_mesh_generators.py +35 -34
- tests/test_solvers.py +32 -31
- tests/utils/fiqus_test_classes.py +396 -147
- tests/utils/generate_reference_files_ConductorAC.py +57 -57
- tests/utils/helpers.py +76 -1
- {fiqus-2026.1.0.dist-info → fiqus-2026.1.2.dist-info}/LICENSE.txt +0 -0
- {fiqus-2026.1.0.dist-info → fiqus-2026.1.2.dist-info}/top_level.txt +0 -0
fiqus/parsers/ParserCOND.py
CHANGED
|
@@ -639,7 +639,8 @@ class ParserCOND:
|
|
|
639
639
|
:rtype: dict
|
|
640
640
|
"""
|
|
641
641
|
return {int(k): v for k, v in x.items()} # change dict keys from strings to integers
|
|
642
|
-
|
|
642
|
+
with open(json_file_path) as f:
|
|
643
|
+
return jsonKeys2int(json.load(f))
|
|
643
644
|
|
|
644
645
|
@staticmethod
|
|
645
646
|
def merge_conductor_dicts(cond_dict_list):
|
fiqus/parsers/ParserDAT.py
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import pandas as pd
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class ParserDAT:
|
|
5
|
-
|
|
6
|
-
def __init__(self, dat_file_path):
|
|
7
|
-
"""
|
|
8
|
-
Read dat file and returns its content as object attribute .pqv (postprocessed quantity value) that is a float
|
|
9
|
-
:param dat_file_path: Full path to .pos file, including file name and extension.
|
|
10
|
-
:return: nothing, keeps attribute pqv (postprocessed quantity value)
|
|
11
|
-
"""
|
|
12
|
-
pqn = 'pqn' # postprocessed quantity name
|
|
13
|
-
delimiter = ' '
|
|
14
|
-
columns = ['NaN', pqn]
|
|
15
|
-
df = pd.read_csv(dat_file_path, delimiter=delimiter, header=None, engine='python', names=columns, skipinitialspace=True)
|
|
16
|
-
self.pqv = float(df[pqn][0])
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ParserDAT:
|
|
5
|
+
|
|
6
|
+
def __init__(self, dat_file_path):
|
|
7
|
+
"""
|
|
8
|
+
Read dat file and returns its content as object attribute .pqv (postprocessed quantity value) that is a float
|
|
9
|
+
:param dat_file_path: Full path to .pos file, including file name and extension.
|
|
10
|
+
:return: nothing, keeps attribute pqv (postprocessed quantity value)
|
|
11
|
+
"""
|
|
12
|
+
pqn = 'pqn' # postprocessed quantity name
|
|
13
|
+
delimiter = ' '
|
|
14
|
+
columns = ['NaN', pqn]
|
|
15
|
+
df = pd.read_csv(dat_file_path, delimiter=delimiter, header=None, engine='python', names=columns, skipinitialspace=True)
|
|
16
|
+
self.pqv = float(df[pqn][0])
|
|
@@ -1,212 +1,212 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
|
|
3
|
-
import re
|
|
4
|
-
import math
|
|
5
|
-
from typing import Literal
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class ParserGetDPOnSection:
|
|
9
|
-
"""
|
|
10
|
-
This class parses GetDP's TimeTable format output files.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
def __init__(self, filePath, data_type: Literal["scalar", "vector"], depth):
|
|
14
|
-
self.time_values = []
|
|
15
|
-
self.data_type = data_type
|
|
16
|
-
self.depth = depth
|
|
17
|
-
|
|
18
|
-
if self.depth not in [0, 1]:
|
|
19
|
-
raise NotImplementedError("Only depth = 0 and depth = 1 is implemented.")
|
|
20
|
-
|
|
21
|
-
# Check GMSH documentation for object types:
|
|
22
|
-
if self.depth == 0:
|
|
23
|
-
if self.data_type == "scalar":
|
|
24
|
-
lineName = "SP" # scalar point
|
|
25
|
-
elif self.data_type == "vector":
|
|
26
|
-
lineName = "VP" # vector point
|
|
27
|
-
elif self.depth == 1:
|
|
28
|
-
if self.data_type == "scalar":
|
|
29
|
-
lineName = "ST"
|
|
30
|
-
elif self.data_type == "vector":
|
|
31
|
-
lineName = "VT"
|
|
32
|
-
|
|
33
|
-
# Parse data:
|
|
34
|
-
with open(filePath) as file:
|
|
35
|
-
data = file.read()
|
|
36
|
-
|
|
37
|
-
time_values_line = re.search(r"TIME\{(.*)\}", data)[0]
|
|
38
|
-
time_values = re.findall(r"TIME\{(.*)\}", time_values_line)
|
|
39
|
-
self.time_values = [
|
|
40
|
-
float(time_value) for time_value in time_values[0].split(",")
|
|
41
|
-
]
|
|
42
|
-
data = data.replace(time_values_line, "")
|
|
43
|
-
|
|
44
|
-
points = re.findall(lineName + r"\((.*)\){.*\..*}", data)
|
|
45
|
-
points = [point.split(",") for point in points]
|
|
46
|
-
self.points = np.array(points, dtype=float)
|
|
47
|
-
|
|
48
|
-
if self.depth == 1:
|
|
49
|
-
length = np.shape(self.points)[1]
|
|
50
|
-
step = int(length / 3)
|
|
51
|
-
self.points = (
|
|
52
|
-
self.points[:, 0:step]
|
|
53
|
-
+ self.points[:, step : 2 * step]
|
|
54
|
-
+ self.points[:, 2 * step : 3 * step]
|
|
55
|
-
) / 3
|
|
56
|
-
|
|
57
|
-
values = re.findall(lineName + r"\(.*\){(.*\..*)}", data)
|
|
58
|
-
values = [value.split(",") for value in values]
|
|
59
|
-
self.values = np.array(values, dtype=float)
|
|
60
|
-
|
|
61
|
-
if self.depth == 1:
|
|
62
|
-
length = np.shape(self.values)[1]
|
|
63
|
-
step = int(length / 3)
|
|
64
|
-
self.values = (
|
|
65
|
-
self.values[:, 0:step]
|
|
66
|
-
+ self.values[:, step : 2 * step]
|
|
67
|
-
+ self.values[:, 2 * step : 3 * step]
|
|
68
|
-
) / 3
|
|
69
|
-
|
|
70
|
-
# Somehow, even with depth 0, we get duplicate magnitudes for different points.
|
|
71
|
-
# We need to remove them for better plotting:
|
|
72
|
-
_, unique_indices = np.unique(
|
|
73
|
-
self.values[:, np.shape(self.values)[1] // 2], return_index=True
|
|
74
|
-
)
|
|
75
|
-
self.values = self.values[unique_indices, :]
|
|
76
|
-
self.points = self.points[unique_indices, :]
|
|
77
|
-
|
|
78
|
-
def get_values_at_time(self, time):
|
|
79
|
-
"""
|
|
80
|
-
Returns the values at the specified time.
|
|
81
|
-
"""
|
|
82
|
-
if self.points.shape[1] != 2:
|
|
83
|
-
raise ValueError(
|
|
84
|
-
"Use project_values_on_a_plane() before calling get_values_at_time()!"
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
for index, time_value in enumerate(self.time_values):
|
|
88
|
-
if math.isclose(float(time_value), float(time), abs_tol=1e-10):
|
|
89
|
-
return self.values[:, index]
|
|
90
|
-
|
|
91
|
-
def get_values_at_time_step(self, time_step):
|
|
92
|
-
"""
|
|
93
|
-
Returns the values at the specified time step.
|
|
94
|
-
"""
|
|
95
|
-
if self.points.shape[1] != 2:
|
|
96
|
-
raise ValueError(
|
|
97
|
-
"Use project_values_on_a_plane() before calling get_values_at_time()!"
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
if self.data_type == "scalar":
|
|
101
|
-
return self.values[:, time_step]
|
|
102
|
-
elif self.data_type == "vector":
|
|
103
|
-
return self.values[:, time_step * 2 : time_step * 2 + 2]
|
|
104
|
-
|
|
105
|
-
def project_values_on_a_plane(self, plane_normal, plane_x_axis_unit_vector):
|
|
106
|
-
""" """
|
|
107
|
-
|
|
108
|
-
class unitVector:
|
|
109
|
-
def __init__(self, u, v, w) -> None:
|
|
110
|
-
length = math.sqrt(u**2 + v**2 + w**2)
|
|
111
|
-
self.u = u / length
|
|
112
|
-
self.v = v / length
|
|
113
|
-
self.w = w / length
|
|
114
|
-
|
|
115
|
-
def rotate(self, theta, withRespectTo):
|
|
116
|
-
# Rotate with respect to the withRespectTo vector by theta degrees:
|
|
117
|
-
# https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle
|
|
118
|
-
a = withRespectTo.u
|
|
119
|
-
b = withRespectTo.v
|
|
120
|
-
c = withRespectTo.w
|
|
121
|
-
|
|
122
|
-
rotationMatrix = np.array(
|
|
123
|
-
[
|
|
124
|
-
[
|
|
125
|
-
math.cos(theta) + a**2 * (1 - math.cos(theta)),
|
|
126
|
-
a * b * (1 - math.cos(theta)) - c * math.sin(theta),
|
|
127
|
-
a * c * (1 - math.cos(theta)) + b * math.sin(theta),
|
|
128
|
-
],
|
|
129
|
-
[
|
|
130
|
-
b * a * (1 - math.cos(theta)) + c * math.sin(theta),
|
|
131
|
-
math.cos(theta) + b**2 * (1 - math.cos(theta)),
|
|
132
|
-
b * c * (1 - math.cos(theta)) - a * math.sin(theta),
|
|
133
|
-
],
|
|
134
|
-
[
|
|
135
|
-
c * a * (1 - math.cos(theta)) - b * math.sin(theta),
|
|
136
|
-
c * b * (1 - math.cos(theta)) + a * math.sin(theta),
|
|
137
|
-
math.cos(theta) + c**2 * (1 - math.cos(theta)),
|
|
138
|
-
],
|
|
139
|
-
]
|
|
140
|
-
)
|
|
141
|
-
vector = np.array([[self.u], [self.v], [self.w]])
|
|
142
|
-
rotatedVector = rotationMatrix @ vector
|
|
143
|
-
return unitVector(
|
|
144
|
-
rotatedVector[0][0],
|
|
145
|
-
rotatedVector[1][0],
|
|
146
|
-
rotatedVector[2][0],
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
def __pow__(self, otherUnitVector):
|
|
150
|
-
# Cross product:
|
|
151
|
-
u = self.v * otherUnitVector.w - self.w * otherUnitVector.v
|
|
152
|
-
v = self.w * otherUnitVector.u - self.u * otherUnitVector.w
|
|
153
|
-
w = self.u * otherUnitVector.v - self.v * otherUnitVector.u
|
|
154
|
-
return unitVector(u, v, w)
|
|
155
|
-
|
|
156
|
-
def __mul__(self, otherUnitVector) -> float:
|
|
157
|
-
# Dot product:
|
|
158
|
-
return (
|
|
159
|
-
self.u * otherUnitVector.u
|
|
160
|
-
+ self.v * otherUnitVector.v
|
|
161
|
-
+ self.w * otherUnitVector.w
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
if len(plane_normal) != 3:
|
|
165
|
-
raise ValueError(
|
|
166
|
-
"planeNormal for magneticFieldOnCutPlane must be a list of"
|
|
167
|
-
" three numbers!"
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
if len(plane_x_axis_unit_vector) != 3:
|
|
171
|
-
raise ValueError(
|
|
172
|
-
"planeXAxis for magneticFieldOnCutPlane must be a list of"
|
|
173
|
-
" three numbers!"
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
plane_normal = unitVector(plane_normal[0], plane_normal[1], plane_normal[2])
|
|
177
|
-
plane_x_axis = unitVector(
|
|
178
|
-
plane_x_axis_unit_vector[0],
|
|
179
|
-
plane_x_axis_unit_vector[1],
|
|
180
|
-
plane_x_axis_unit_vector[2],
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
# Rotate perperndicular vector with respect to the plane's normal vector
|
|
184
|
-
# by 90 degrees to find the second perpendicular vector:
|
|
185
|
-
plane_y_axis = plane_x_axis.rotate(math.pi / 2, plane_normal)
|
|
186
|
-
|
|
187
|
-
# Build the transformation matrix to change from the global coordinate
|
|
188
|
-
# system to the plane's coordinate system:
|
|
189
|
-
transformationMatrix = np.array(
|
|
190
|
-
[
|
|
191
|
-
[plane_x_axis.u, plane_x_axis.v, plane_x_axis.w],
|
|
192
|
-
[plane_y_axis.u, plane_y_axis.v, plane_y_axis.w],
|
|
193
|
-
[plane_normal.u, plane_normal.v, plane_normal.w],
|
|
194
|
-
]
|
|
195
|
-
)
|
|
196
|
-
points = self.points.transpose()
|
|
197
|
-
new_points = transformationMatrix @ points
|
|
198
|
-
new_points = new_points.transpose()
|
|
199
|
-
self.points = new_points[:, 0:2]
|
|
200
|
-
|
|
201
|
-
if self.data_type == "vector":
|
|
202
|
-
reshaped_values = self.values
|
|
203
|
-
reshaped_values = reshaped_values.reshape(
|
|
204
|
-
(len(self.values) * len(self.time_values), 3)
|
|
205
|
-
)
|
|
206
|
-
reshaped_values = reshaped_values.transpose()
|
|
207
|
-
new_values = transformationMatrix @ reshaped_values
|
|
208
|
-
new_values = new_values.transpose()
|
|
209
|
-
new_values = new_values[:, 0:2]
|
|
210
|
-
self.values = new_values.reshape(
|
|
211
|
-
np.shape(self.values)[0], int(np.shape(self.values)[1] / 3 * 2)
|
|
212
|
-
)
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import math
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ParserGetDPOnSection:
|
|
9
|
+
"""
|
|
10
|
+
This class parses GetDP's TimeTable format output files.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, filePath, data_type: Literal["scalar", "vector"], depth):
|
|
14
|
+
self.time_values = []
|
|
15
|
+
self.data_type = data_type
|
|
16
|
+
self.depth = depth
|
|
17
|
+
|
|
18
|
+
if self.depth not in [0, 1]:
|
|
19
|
+
raise NotImplementedError("Only depth = 0 and depth = 1 is implemented.")
|
|
20
|
+
|
|
21
|
+
# Check GMSH documentation for object types:
|
|
22
|
+
if self.depth == 0:
|
|
23
|
+
if self.data_type == "scalar":
|
|
24
|
+
lineName = "SP" # scalar point
|
|
25
|
+
elif self.data_type == "vector":
|
|
26
|
+
lineName = "VP" # vector point
|
|
27
|
+
elif self.depth == 1:
|
|
28
|
+
if self.data_type == "scalar":
|
|
29
|
+
lineName = "ST"
|
|
30
|
+
elif self.data_type == "vector":
|
|
31
|
+
lineName = "VT"
|
|
32
|
+
|
|
33
|
+
# Parse data:
|
|
34
|
+
with open(filePath) as file:
|
|
35
|
+
data = file.read()
|
|
36
|
+
|
|
37
|
+
time_values_line = re.search(r"TIME\{(.*)\}", data)[0]
|
|
38
|
+
time_values = re.findall(r"TIME\{(.*)\}", time_values_line)
|
|
39
|
+
self.time_values = [
|
|
40
|
+
float(time_value) for time_value in time_values[0].split(",")
|
|
41
|
+
]
|
|
42
|
+
data = data.replace(time_values_line, "")
|
|
43
|
+
|
|
44
|
+
points = re.findall(lineName + r"\((.*)\){.*\..*}", data)
|
|
45
|
+
points = [point.split(",") for point in points]
|
|
46
|
+
self.points = np.array(points, dtype=float)
|
|
47
|
+
|
|
48
|
+
if self.depth == 1:
|
|
49
|
+
length = np.shape(self.points)[1]
|
|
50
|
+
step = int(length / 3)
|
|
51
|
+
self.points = (
|
|
52
|
+
self.points[:, 0:step]
|
|
53
|
+
+ self.points[:, step : 2 * step]
|
|
54
|
+
+ self.points[:, 2 * step : 3 * step]
|
|
55
|
+
) / 3
|
|
56
|
+
|
|
57
|
+
values = re.findall(lineName + r"\(.*\){(.*\..*)}", data)
|
|
58
|
+
values = [value.split(",") for value in values]
|
|
59
|
+
self.values = np.array(values, dtype=float)
|
|
60
|
+
|
|
61
|
+
if self.depth == 1:
|
|
62
|
+
length = np.shape(self.values)[1]
|
|
63
|
+
step = int(length / 3)
|
|
64
|
+
self.values = (
|
|
65
|
+
self.values[:, 0:step]
|
|
66
|
+
+ self.values[:, step : 2 * step]
|
|
67
|
+
+ self.values[:, 2 * step : 3 * step]
|
|
68
|
+
) / 3
|
|
69
|
+
|
|
70
|
+
# Somehow, even with depth 0, we get duplicate magnitudes for different points.
|
|
71
|
+
# We need to remove them for better plotting:
|
|
72
|
+
_, unique_indices = np.unique(
|
|
73
|
+
self.values[:, np.shape(self.values)[1] // 2], return_index=True
|
|
74
|
+
)
|
|
75
|
+
self.values = self.values[unique_indices, :]
|
|
76
|
+
self.points = self.points[unique_indices, :]
|
|
77
|
+
|
|
78
|
+
def get_values_at_time(self, time):
|
|
79
|
+
"""
|
|
80
|
+
Returns the values at the specified time.
|
|
81
|
+
"""
|
|
82
|
+
if self.points.shape[1] != 2:
|
|
83
|
+
raise ValueError(
|
|
84
|
+
"Use project_values_on_a_plane() before calling get_values_at_time()!"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
for index, time_value in enumerate(self.time_values):
|
|
88
|
+
if math.isclose(float(time_value), float(time), abs_tol=1e-10):
|
|
89
|
+
return self.values[:, index]
|
|
90
|
+
|
|
91
|
+
def get_values_at_time_step(self, time_step):
|
|
92
|
+
"""
|
|
93
|
+
Returns the values at the specified time step.
|
|
94
|
+
"""
|
|
95
|
+
if self.points.shape[1] != 2:
|
|
96
|
+
raise ValueError(
|
|
97
|
+
"Use project_values_on_a_plane() before calling get_values_at_time()!"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if self.data_type == "scalar":
|
|
101
|
+
return self.values[:, time_step]
|
|
102
|
+
elif self.data_type == "vector":
|
|
103
|
+
return self.values[:, time_step * 2 : time_step * 2 + 2]
|
|
104
|
+
|
|
105
|
+
def project_values_on_a_plane(self, plane_normal, plane_x_axis_unit_vector):
|
|
106
|
+
""" """
|
|
107
|
+
|
|
108
|
+
class unitVector:
|
|
109
|
+
def __init__(self, u, v, w) -> None:
|
|
110
|
+
length = math.sqrt(u**2 + v**2 + w**2)
|
|
111
|
+
self.u = u / length
|
|
112
|
+
self.v = v / length
|
|
113
|
+
self.w = w / length
|
|
114
|
+
|
|
115
|
+
def rotate(self, theta, withRespectTo):
|
|
116
|
+
# Rotate with respect to the withRespectTo vector by theta degrees:
|
|
117
|
+
# https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle
|
|
118
|
+
a = withRespectTo.u
|
|
119
|
+
b = withRespectTo.v
|
|
120
|
+
c = withRespectTo.w
|
|
121
|
+
|
|
122
|
+
rotationMatrix = np.array(
|
|
123
|
+
[
|
|
124
|
+
[
|
|
125
|
+
math.cos(theta) + a**2 * (1 - math.cos(theta)),
|
|
126
|
+
a * b * (1 - math.cos(theta)) - c * math.sin(theta),
|
|
127
|
+
a * c * (1 - math.cos(theta)) + b * math.sin(theta),
|
|
128
|
+
],
|
|
129
|
+
[
|
|
130
|
+
b * a * (1 - math.cos(theta)) + c * math.sin(theta),
|
|
131
|
+
math.cos(theta) + b**2 * (1 - math.cos(theta)),
|
|
132
|
+
b * c * (1 - math.cos(theta)) - a * math.sin(theta),
|
|
133
|
+
],
|
|
134
|
+
[
|
|
135
|
+
c * a * (1 - math.cos(theta)) - b * math.sin(theta),
|
|
136
|
+
c * b * (1 - math.cos(theta)) + a * math.sin(theta),
|
|
137
|
+
math.cos(theta) + c**2 * (1 - math.cos(theta)),
|
|
138
|
+
],
|
|
139
|
+
]
|
|
140
|
+
)
|
|
141
|
+
vector = np.array([[self.u], [self.v], [self.w]])
|
|
142
|
+
rotatedVector = rotationMatrix @ vector
|
|
143
|
+
return unitVector(
|
|
144
|
+
rotatedVector[0][0],
|
|
145
|
+
rotatedVector[1][0],
|
|
146
|
+
rotatedVector[2][0],
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
def __pow__(self, otherUnitVector):
|
|
150
|
+
# Cross product:
|
|
151
|
+
u = self.v * otherUnitVector.w - self.w * otherUnitVector.v
|
|
152
|
+
v = self.w * otherUnitVector.u - self.u * otherUnitVector.w
|
|
153
|
+
w = self.u * otherUnitVector.v - self.v * otherUnitVector.u
|
|
154
|
+
return unitVector(u, v, w)
|
|
155
|
+
|
|
156
|
+
def __mul__(self, otherUnitVector) -> float:
|
|
157
|
+
# Dot product:
|
|
158
|
+
return (
|
|
159
|
+
self.u * otherUnitVector.u
|
|
160
|
+
+ self.v * otherUnitVector.v
|
|
161
|
+
+ self.w * otherUnitVector.w
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
if len(plane_normal) != 3:
|
|
165
|
+
raise ValueError(
|
|
166
|
+
"planeNormal for magneticFieldOnCutPlane must be a list of"
|
|
167
|
+
" three numbers!"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
if len(plane_x_axis_unit_vector) != 3:
|
|
171
|
+
raise ValueError(
|
|
172
|
+
"planeXAxis for magneticFieldOnCutPlane must be a list of"
|
|
173
|
+
" three numbers!"
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
plane_normal = unitVector(plane_normal[0], plane_normal[1], plane_normal[2])
|
|
177
|
+
plane_x_axis = unitVector(
|
|
178
|
+
plane_x_axis_unit_vector[0],
|
|
179
|
+
plane_x_axis_unit_vector[1],
|
|
180
|
+
plane_x_axis_unit_vector[2],
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# Rotate perperndicular vector with respect to the plane's normal vector
|
|
184
|
+
# by 90 degrees to find the second perpendicular vector:
|
|
185
|
+
plane_y_axis = plane_x_axis.rotate(math.pi / 2, plane_normal)
|
|
186
|
+
|
|
187
|
+
# Build the transformation matrix to change from the global coordinate
|
|
188
|
+
# system to the plane's coordinate system:
|
|
189
|
+
transformationMatrix = np.array(
|
|
190
|
+
[
|
|
191
|
+
[plane_x_axis.u, plane_x_axis.v, plane_x_axis.w],
|
|
192
|
+
[plane_y_axis.u, plane_y_axis.v, plane_y_axis.w],
|
|
193
|
+
[plane_normal.u, plane_normal.v, plane_normal.w],
|
|
194
|
+
]
|
|
195
|
+
)
|
|
196
|
+
points = self.points.transpose()
|
|
197
|
+
new_points = transformationMatrix @ points
|
|
198
|
+
new_points = new_points.transpose()
|
|
199
|
+
self.points = new_points[:, 0:2]
|
|
200
|
+
|
|
201
|
+
if self.data_type == "vector":
|
|
202
|
+
reshaped_values = self.values
|
|
203
|
+
reshaped_values = reshaped_values.reshape(
|
|
204
|
+
(len(self.values) * len(self.time_values), 3)
|
|
205
|
+
)
|
|
206
|
+
reshaped_values = reshaped_values.transpose()
|
|
207
|
+
new_values = transformationMatrix @ reshaped_values
|
|
208
|
+
new_values = new_values.transpose()
|
|
209
|
+
new_values = new_values[:, 0:2]
|
|
210
|
+
self.values = new_values.reshape(
|
|
211
|
+
np.shape(self.values)[0], int(np.shape(self.values)[1] / 3 * 2)
|
|
212
|
+
)
|