nmag-python-3 0.0.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.
- anisotropy/__init__.py +28 -0
- anisotropy/anisotropy.py +13 -0
- anisotropy/evaluation.py +70 -0
- anisotropy/model.py +200 -0
- anisotropy/predefined.py +202 -0
- anisotropy/py.typed +1 -0
- anisotropy/values.py +84 -0
- mag_material/__init__.py +3 -0
- mag_material/mag_material.py +231 -0
- mag_material/parameters.py +150 -0
- mag_material/py.typed +1 -0
- nmag/__init__.py +36 -0
- nmag/backends.py +493 -0
- nmag/checkpoint.py +327 -0
- nmag/config.py +174 -0
- nmag/demag/__init__.py +39 -0
- nmag/demag/bem_operator.py +149 -0
- nmag/demag/geometry.py +134 -0
- nmag/demag/lindholm.py +133 -0
- nmag/demag/lindholm_fast.py +463 -0
- nmag/demag/linear.py +489 -0
- nmag/dynamics/__init__.py +255 -0
- nmag/output.py +27 -0
- nmag/parallel.py +45 -0
- nmag/py.typed +1 -0
- nmag/resources.py +38 -0
- nmag/simulation/__init__.py +519 -0
- nmag/simulation/anisotropy/__init__.py +5 -0
- nmag/simulation/anisotropy/fields.py +56 -0
- nmag/simulation/anisotropy/materials.py +138 -0
- nmag/simulation/demag/__init__.py +1 -0
- nmag/simulation/demag/bem/__init__.py +11 -0
- nmag/simulation/demag/bem/diagnostics.py +73 -0
- nmag/simulation/demag/bem/dirichlet.py +85 -0
- nmag/simulation/demag/bem/hierarchical.py +74 -0
- nmag/simulation/demag/bem/operator.py +292 -0
- nmag/simulation/demag/fem/__init__.py +11 -0
- nmag/simulation/demag/fem/assembly.py +114 -0
- nmag/simulation/demag/fem/charges.py +38 -0
- nmag/simulation/demag/fem/geometry.py +261 -0
- nmag/simulation/demag/fields/__init__.py +11 -0
- nmag/simulation/demag/fields/auxiliary.py +186 -0
- nmag/simulation/demag/fields/probe.py +77 -0
- nmag/simulation/demag/fields/recovery.py +207 -0
- nmag/simulation/demag/solver.py +17 -0
- nmag/simulation/dynamics/__init__.py +92 -0
- nmag/simulation/dynamics/advance.py +199 -0
- nmag/simulation/dynamics/integrator.py +263 -0
- nmag/simulation/exchange/__init__.py +13 -0
- nmag/simulation/exchange/coefficients.py +185 -0
- nmag/simulation/exchange/fields.py +106 -0
- nmag/simulation/exchange/llg_rhs.py +218 -0
- nmag/simulation/fields/__init__.py +19 -0
- nmag/simulation/fields/arrays.py +111 -0
- nmag/simulation/fields/availability.py +188 -0
- nmag/simulation/fields/averages.py +293 -0
- nmag/simulation/fields/derived.py +164 -0
- nmag/simulation/fields/maxangle.py +163 -0
- nmag/simulation/fields/probes.py +106 -0
- nmag/simulation/implicit_dynamics.py +221 -0
- nmag/simulation/mesh/__init__.py +13 -0
- nmag/simulation/mesh/geometry.py +153 -0
- nmag/simulation/mesh/materials.py +299 -0
- nmag/simulation/mesh/probe.py +234 -0
- nmag/simulation/restart.py +103 -0
- nmag/simulation/support.py +224 -0
- nmag_python_3-0.0.2.dist-info/METADATA +157 -0
- nmag_python_3-0.0.2.dist-info/RECORD +147 -0
- nmag_python_3-0.0.2.dist-info/WHEEL +5 -0
- nmag_python_3-0.0.2.dist-info/licenses/LICENSE +339 -0
- nmag_python_3-0.0.2.dist-info/top_level.txt +8 -0
- nmesh/__init__.py +130 -0
- nmesh/backend.py +286 -0
- nmesh/geometry/__init__.py +52 -0
- nmesh/geometry/boolean_operations.py +157 -0
- nmesh/geometry/primitives.py +453 -0
- nmesh/geometry/transform.py +126 -0
- nmesh/io/__init__.py +50 -0
- nmesh/io/ascii.py +132 -0
- nmesh/io/legacy_nmesh_hdf5.py +318 -0
- nmesh/io/meshio_support.py +170 -0
- nmesh/mesh_generation.py +182 -0
- nmesh/mesh_io.py +227 -0
- nmesh/mesh_model.py +147 -0
- nmesh/mesh_utilities.py +79 -0
- nmesh/mesher/__init__.py +21 -0
- nmesh/mesher/driver.py +146 -0
- nmesh/mesher/meshing_defaults.py +252 -0
- nmesh/mesher/meshing_parameters.py +185 -0
- nmesh/mesher/parity.py +21 -0
- nmesh/mesher/parity_canonical.py +142 -0
- nmesh/mesher/parity_comparison.py +191 -0
- nmesh/mesher/parity_metrics.py +114 -0
- nmesh/mesher/periodic.py +97 -0
- nmesh/mesher/relaxation/__init__.py +14 -0
- nmesh/mesher/relaxation/_constants.py +20 -0
- nmesh/mesher/relaxation/_types.py +15 -0
- nmesh/mesher/relaxation/density.py +170 -0
- nmesh/mesher/relaxation/engine/__init__.py +18 -0
- nmesh/mesher/relaxation/engine/state.py +155 -0
- nmesh/mesher/relaxation/engine/steps.py +248 -0
- nmesh/mesher/relaxation/engine/topology.py +230 -0
- nmesh/mesher/relaxation/forces/__init__.py +96 -0
- nmesh/mesher/relaxation/forces/jit.py +102 -0
- nmesh/mesher/relaxation/forces/neighbors.py +186 -0
- nmesh/mesher/relaxation/forces/simplex.py +302 -0
- nmesh/mesher/relaxation/forces/summary.py +207 -0
- nmesh/mesher/relaxation/forces/types.py +92 -0
- nmesh/mesher/relaxation/geometry/__init__.py +6 -0
- nmesh/mesher/relaxation/geometry/builder.py +154 -0
- nmesh/mesher/relaxation/geometry/model.py +194 -0
- nmesh/mesher/relaxation/seeding/__init__.py +74 -0
- nmesh/mesher/relaxation/seeding/periodic.py +88 -0
- nmesh/mesher/relaxation/seeding/points.py +88 -0
- nmesh/mesher/relaxation/seeding/sampling.py +142 -0
- nmesh/mesher/relaxation/topology/__init__.py +297 -0
- nmesh/mesher/relaxation/topology/finalize.py +78 -0
- nmesh/mesher/relaxation/topology/recovery.py +310 -0
- nmesh/mesher/sectioned_config.py +70 -0
- nmesh/nmesh.py +99 -0
- nmesh/py.typed +1 -0
- nmesh/utils/__init__.py +33 -0
- nmesh/utils/array_list_utils.py +128 -0
- nmesh/utils/constants.py +22 -0
- nmesh/utils/timing_memory_utils.py +51 -0
- nmesh/utils/types.py +13 -0
- si/constants.py +49 -0
- si/physical.py +722 -0
- si/py.typed +1 -0
- simulation/__init__.py +1 -0
- simulation/clock.py +237 -0
- simulation/data_writer.py +273 -0
- simulation/data_writer_collection.py +267 -0
- simulation/hysteresis.py +74 -0
- simulation/hysteresis_runner.py +286 -0
- simulation/hysteresis_schedule.py +180 -0
- simulation/inference/__init__.py +3 -0
- simulation/inference/inference.py +95 -0
- simulation/py.typed +1 -0
- simulation/quantity.py +88 -0
- simulation/simulation_core.py +458 -0
- throttler/__init__.py +3 -0
- throttler/py.typed +1 -0
- throttler/throttler.py +55 -0
- when/__init__.py +3 -0
- when/py.typed +1 -0
- when/when.py +416 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from functools import wraps
|
|
5
|
+
from typing import Any, ParamSpec, TypeVar, cast
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
_JIT_FUNCTIONS: list[Callable[..., Any]] = []
|
|
10
|
+
_jit_compiled = False
|
|
11
|
+
P = ParamSpec("P")
|
|
12
|
+
R = TypeVar("R")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def __getattr__(name: str) -> Any:
|
|
16
|
+
if name == "_JIT_COMPILED":
|
|
17
|
+
return _jit_compiled
|
|
18
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _compile_jit_functions() -> None:
|
|
22
|
+
global _jit_compiled
|
|
23
|
+
if _jit_compiled:
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
from numba import njit
|
|
28
|
+
except ImportError: # pragma: no cover - numba is a project dependency.
|
|
29
|
+
compiled_functions = {function.__name__: function for function in _JIT_FUNCTIONS}
|
|
30
|
+
else:
|
|
31
|
+
compiled_functions = {
|
|
32
|
+
function.__name__: njit(cache=True)(function) for function in _JIT_FUNCTIONS
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
globals().update(compiled_functions)
|
|
36
|
+
_jit_compiled = True
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _jit(function: Callable[P, R]) -> Callable[P, R]:
|
|
40
|
+
_JIT_FUNCTIONS.append(function)
|
|
41
|
+
|
|
42
|
+
@wraps(function)
|
|
43
|
+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
|
44
|
+
_compile_jit_functions()
|
|
45
|
+
compiled = cast(Callable[P, R], globals()[function.__name__])
|
|
46
|
+
return compiled(*args, **kwargs)
|
|
47
|
+
|
|
48
|
+
cast(Any, wrapper).py_func = function
|
|
49
|
+
return wrapper
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@_jit
|
|
53
|
+
def _dot3(left: np.ndarray, right: np.ndarray) -> float:
|
|
54
|
+
return left[0] * right[0] + left[1] * right[1] + left[2] * right[2]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@_jit
|
|
58
|
+
def _cross3(left: np.ndarray, right: np.ndarray) -> np.ndarray:
|
|
59
|
+
result = np.empty(3, dtype=np.float64)
|
|
60
|
+
result[0] = left[1] * right[2] - left[2] * right[1]
|
|
61
|
+
result[1] = left[2] * right[0] - left[0] * right[2]
|
|
62
|
+
result[2] = left[0] * right[1] - left[1] * right[0]
|
|
63
|
+
return result
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@_jit
|
|
67
|
+
def _subtract3(left: np.ndarray, right: np.ndarray) -> np.ndarray:
|
|
68
|
+
result = np.empty(3, dtype=np.float64)
|
|
69
|
+
result[0] = left[0] - right[0]
|
|
70
|
+
result[1] = left[1] - right[1]
|
|
71
|
+
result[2] = left[2] - right[2]
|
|
72
|
+
return result
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@_jit
|
|
76
|
+
def _norm3(vector: np.ndarray) -> float:
|
|
77
|
+
return np.sqrt(_dot3(vector, vector))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@_jit
|
|
81
|
+
def _normalised3(vector: np.ndarray) -> np.ndarray:
|
|
82
|
+
result = np.zeros(3, dtype=np.float64)
|
|
83
|
+
length = _norm3(vector)
|
|
84
|
+
if length == 0.0:
|
|
85
|
+
return result
|
|
86
|
+
result[0] = vector[0] / length
|
|
87
|
+
result[1] = vector[1] / length
|
|
88
|
+
result[2] = vector[2] / length
|
|
89
|
+
return result
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@_jit
|
|
93
|
+
def _triangle_space_angle_fast(
|
|
94
|
+
observer: np.ndarray,
|
|
95
|
+
p0: np.ndarray,
|
|
96
|
+
p1: np.ndarray,
|
|
97
|
+
p2: np.ndarray,
|
|
98
|
+
) -> float:
|
|
99
|
+
r0 = _subtract3(p0, observer)
|
|
100
|
+
r1 = _subtract3(p1, observer)
|
|
101
|
+
r2 = _subtract3(p2, observer)
|
|
102
|
+
r0_len = _norm3(r0)
|
|
103
|
+
r1_len = _norm3(r1)
|
|
104
|
+
r2_len = _norm3(r2)
|
|
105
|
+
dot01 = _dot3(r0, r1)
|
|
106
|
+
dot12 = _dot3(r1, r2)
|
|
107
|
+
dot20 = _dot3(r2, r0)
|
|
108
|
+
numerator = r0_len * r1_len * r2_len + r0_len * dot12 + r1_len * dot20 + r2_len * dot01
|
|
109
|
+
denominator_squared = (
|
|
110
|
+
2.0 * (r1_len * r2_len + dot12) * (r2_len * r0_len + dot20) * (r0_len * r1_len + dot01)
|
|
111
|
+
)
|
|
112
|
+
if denominator_squared <= 0.0:
|
|
113
|
+
return 0.0
|
|
114
|
+
quotient = numerator / np.sqrt(denominator_squared)
|
|
115
|
+
if quotient < -1.0:
|
|
116
|
+
quotient = -1.0
|
|
117
|
+
elif quotient > 1.0:
|
|
118
|
+
quotient = 1.0
|
|
119
|
+
return 2.0 * np.arccos(quotient)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@_jit
|
|
123
|
+
def _safe_log_ratio_fast(numerator: float, denominator: float) -> float:
|
|
124
|
+
if denominator <= 0.0:
|
|
125
|
+
return np.nan
|
|
126
|
+
return np.log(numerator / denominator)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@_jit
|
|
130
|
+
def _lindholm_triangle_contributions_fast(
|
|
131
|
+
observer: np.ndarray,
|
|
132
|
+
p0: np.ndarray,
|
|
133
|
+
p1: np.ndarray,
|
|
134
|
+
p2: np.ndarray,
|
|
135
|
+
outward_surface_normal: np.ndarray,
|
|
136
|
+
) -> np.ndarray:
|
|
137
|
+
angle = _triangle_space_angle_fast(observer, p0, p1, p2)
|
|
138
|
+
r0 = _subtract3(p0, observer)
|
|
139
|
+
r1 = _subtract3(p1, observer)
|
|
140
|
+
r2 = _subtract3(p2, observer)
|
|
141
|
+
s0 = _subtract3(r2, r1)
|
|
142
|
+
s1 = _subtract3(r0, r2)
|
|
143
|
+
s2 = _subtract3(r1, r0)
|
|
144
|
+
xi0 = _normalised3(s0)
|
|
145
|
+
xi1 = _normalised3(s1)
|
|
146
|
+
xi2 = _normalised3(s2)
|
|
147
|
+
area_vector = _cross3(_subtract3(p1, p0), _subtract3(p2, p0))
|
|
148
|
+
area = 0.5 * _norm3(area_vector)
|
|
149
|
+
contributions = np.zeros(3, dtype=np.float64)
|
|
150
|
+
if area == 0.0:
|
|
151
|
+
return contributions
|
|
152
|
+
|
|
153
|
+
zeta_vector = _normalised3(area_vector)
|
|
154
|
+
r0_len = _norm3(r0)
|
|
155
|
+
r1_len = _norm3(r1)
|
|
156
|
+
r2_len = _norm3(r2)
|
|
157
|
+
s0_len = _norm3(s0)
|
|
158
|
+
s1_len = _norm3(s1)
|
|
159
|
+
s2_len = _norm3(s2)
|
|
160
|
+
c01 = _dot3(xi0, xi1)
|
|
161
|
+
c12 = _dot3(xi1, xi2)
|
|
162
|
+
c20 = _dot3(xi2, xi0)
|
|
163
|
+
zeta = _dot3(zeta_vector, r0)
|
|
164
|
+
eta0 = _cross3(zeta_vector, xi0)
|
|
165
|
+
eta1 = _cross3(zeta_vector, xi1)
|
|
166
|
+
eta2 = _cross3(zeta_vector, xi2)
|
|
167
|
+
log0 = _safe_log_ratio_fast(r1_len + r2_len + s0_len, r1_len + r2_len - s0_len)
|
|
168
|
+
log1 = _safe_log_ratio_fast(r2_len + r0_len + s1_len, r2_len + r0_len - s1_len)
|
|
169
|
+
log2 = _safe_log_ratio_fast(r0_len + r1_len + s2_len, r0_len + r1_len - s2_len)
|
|
170
|
+
sign_zeta = 0.0
|
|
171
|
+
if zeta < 0.0:
|
|
172
|
+
sign_zeta = -1.0
|
|
173
|
+
elif zeta > 0.0:
|
|
174
|
+
sign_zeta = 1.0
|
|
175
|
+
angle_pm = angle * sign_zeta
|
|
176
|
+
denominator_factor = 1.0 / (8.0 * np.pi * area)
|
|
177
|
+
gamma0_log = log0 + c01 * log1 + c20 * log2
|
|
178
|
+
gamma1_log = c01 * log0 + log1 + c12 * log2
|
|
179
|
+
gamma2_log = c20 * log0 + c12 * log1 + log2
|
|
180
|
+
values = np.empty(3, dtype=np.float64)
|
|
181
|
+
values[0] = s0_len * denominator_factor * (angle_pm * _dot3(eta0, r1) - zeta * gamma0_log)
|
|
182
|
+
values[1] = s1_len * denominator_factor * (angle_pm * _dot3(eta1, r2) - zeta * gamma1_log)
|
|
183
|
+
values[2] = s2_len * denominator_factor * (angle_pm * _dot3(eta2, r0) - zeta * gamma2_log)
|
|
184
|
+
if _dot3(zeta_vector, outward_surface_normal) < 0.0:
|
|
185
|
+
values[0] = -values[0]
|
|
186
|
+
values[1] = -values[1]
|
|
187
|
+
values[2] = -values[2]
|
|
188
|
+
for index in range(3):
|
|
189
|
+
if np.isfinite(values[index]):
|
|
190
|
+
contributions[index] = values[index]
|
|
191
|
+
return contributions
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _normalised_rows(vectors: np.ndarray) -> np.ndarray:
|
|
195
|
+
lengths = np.linalg.norm(vectors, axis=1)
|
|
196
|
+
result = np.zeros_like(vectors, dtype=float)
|
|
197
|
+
positive = lengths > 0.0
|
|
198
|
+
result[positive] = vectors[positive] / lengths[positive, np.newaxis]
|
|
199
|
+
return result
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _precompute_lindholm_face_geometry(
|
|
203
|
+
points: np.ndarray,
|
|
204
|
+
face_nodes: np.ndarray,
|
|
205
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
|
206
|
+
face_points = points[face_nodes]
|
|
207
|
+
if len(face_nodes) == 0:
|
|
208
|
+
return (
|
|
209
|
+
face_points,
|
|
210
|
+
np.empty((0, 3), dtype=float),
|
|
211
|
+
np.empty((0, 3, 3), dtype=float),
|
|
212
|
+
np.empty((0, 3), dtype=float),
|
|
213
|
+
np.empty((0, 3), dtype=float),
|
|
214
|
+
np.empty(0, dtype=float),
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
edge0 = face_points[:, 2] - face_points[:, 1]
|
|
218
|
+
edge1 = face_points[:, 0] - face_points[:, 2]
|
|
219
|
+
edge2 = face_points[:, 1] - face_points[:, 0]
|
|
220
|
+
edge_vectors = np.stack([edge0, edge1, edge2], axis=1)
|
|
221
|
+
edge_lengths = np.linalg.norm(edge_vectors, axis=2)
|
|
222
|
+
xis = np.zeros_like(edge_vectors, dtype=float)
|
|
223
|
+
for index in range(3):
|
|
224
|
+
xis[:, index] = _normalised_rows(edge_vectors[:, index])
|
|
225
|
+
|
|
226
|
+
area_vectors = np.cross(
|
|
227
|
+
face_points[:, 1] - face_points[:, 0],
|
|
228
|
+
face_points[:, 2] - face_points[:, 0],
|
|
229
|
+
)
|
|
230
|
+
double_areas = np.linalg.norm(area_vectors, axis=1)
|
|
231
|
+
zeta_vectors = np.zeros_like(area_vectors, dtype=float)
|
|
232
|
+
positive_area = double_areas > 0.0
|
|
233
|
+
zeta_vectors[positive_area] = (
|
|
234
|
+
area_vectors[positive_area] / double_areas[positive_area, np.newaxis]
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
eta_vectors = np.stack(
|
|
238
|
+
[
|
|
239
|
+
np.cross(zeta_vectors, xis[:, 0]),
|
|
240
|
+
np.cross(zeta_vectors, xis[:, 1]),
|
|
241
|
+
np.cross(zeta_vectors, xis[:, 2]),
|
|
242
|
+
],
|
|
243
|
+
axis=1,
|
|
244
|
+
)
|
|
245
|
+
corner_cosines = np.stack(
|
|
246
|
+
[
|
|
247
|
+
np.einsum("ij,ij->i", xis[:, 0], xis[:, 1]),
|
|
248
|
+
np.einsum("ij,ij->i", xis[:, 1], xis[:, 2]),
|
|
249
|
+
np.einsum("ij,ij->i", xis[:, 2], xis[:, 0]),
|
|
250
|
+
],
|
|
251
|
+
axis=1,
|
|
252
|
+
)
|
|
253
|
+
denominator_factors = np.zeros(len(face_nodes), dtype=float)
|
|
254
|
+
denominator_factors[positive_area] = 1.0 / (4.0 * np.pi * double_areas[positive_area])
|
|
255
|
+
return face_points, zeta_vectors, eta_vectors, edge_lengths, corner_cosines, denominator_factors
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
@_jit
|
|
259
|
+
def _lindholm_triangle_contributions_precomputed_fast(
|
|
260
|
+
observer: np.ndarray,
|
|
261
|
+
p0: np.ndarray,
|
|
262
|
+
p1: np.ndarray,
|
|
263
|
+
p2: np.ndarray,
|
|
264
|
+
zeta_vector: np.ndarray,
|
|
265
|
+
eta0: np.ndarray,
|
|
266
|
+
eta1: np.ndarray,
|
|
267
|
+
eta2: np.ndarray,
|
|
268
|
+
edge_lengths: np.ndarray,
|
|
269
|
+
corner_cosines: np.ndarray,
|
|
270
|
+
denominator_factor: float,
|
|
271
|
+
) -> np.ndarray:
|
|
272
|
+
contributions = np.zeros(3, dtype=np.float64)
|
|
273
|
+
if denominator_factor == 0.0:
|
|
274
|
+
return contributions
|
|
275
|
+
|
|
276
|
+
r0 = _subtract3(p0, observer)
|
|
277
|
+
r1 = _subtract3(p1, observer)
|
|
278
|
+
r2 = _subtract3(p2, observer)
|
|
279
|
+
r0_len = _norm3(r0)
|
|
280
|
+
r1_len = _norm3(r1)
|
|
281
|
+
r2_len = _norm3(r2)
|
|
282
|
+
dot01 = _dot3(r0, r1)
|
|
283
|
+
dot12 = _dot3(r1, r2)
|
|
284
|
+
dot20 = _dot3(r2, r0)
|
|
285
|
+
numerator = r0_len * r1_len * r2_len + r0_len * dot12 + r1_len * dot20 + r2_len * dot01
|
|
286
|
+
denominator_squared = (
|
|
287
|
+
2.0 * (r1_len * r2_len + dot12) * (r2_len * r0_len + dot20) * (r0_len * r1_len + dot01)
|
|
288
|
+
)
|
|
289
|
+
angle = 0.0
|
|
290
|
+
if denominator_squared > 0.0:
|
|
291
|
+
quotient = numerator / np.sqrt(denominator_squared)
|
|
292
|
+
if quotient < -1.0:
|
|
293
|
+
quotient = -1.0
|
|
294
|
+
elif quotient > 1.0:
|
|
295
|
+
quotient = 1.0
|
|
296
|
+
angle = 2.0 * np.arccos(quotient)
|
|
297
|
+
|
|
298
|
+
s0_len = edge_lengths[0]
|
|
299
|
+
s1_len = edge_lengths[1]
|
|
300
|
+
s2_len = edge_lengths[2]
|
|
301
|
+
c01 = corner_cosines[0]
|
|
302
|
+
c12 = corner_cosines[1]
|
|
303
|
+
c20 = corner_cosines[2]
|
|
304
|
+
zeta = _dot3(zeta_vector, r0)
|
|
305
|
+
log0 = _safe_log_ratio_fast(r1_len + r2_len + s0_len, r1_len + r2_len - s0_len)
|
|
306
|
+
log1 = _safe_log_ratio_fast(r2_len + r0_len + s1_len, r2_len + r0_len - s1_len)
|
|
307
|
+
log2 = _safe_log_ratio_fast(r0_len + r1_len + s2_len, r0_len + r1_len - s2_len)
|
|
308
|
+
sign_zeta = 0.0
|
|
309
|
+
if zeta < 0.0:
|
|
310
|
+
sign_zeta = -1.0
|
|
311
|
+
elif zeta > 0.0:
|
|
312
|
+
sign_zeta = 1.0
|
|
313
|
+
angle_pm = angle * sign_zeta
|
|
314
|
+
gamma0_log = log0 + c01 * log1 + c20 * log2
|
|
315
|
+
gamma1_log = c01 * log0 + log1 + c12 * log2
|
|
316
|
+
gamma2_log = c20 * log0 + c12 * log1 + log2
|
|
317
|
+
|
|
318
|
+
value0 = s0_len * denominator_factor * (angle_pm * _dot3(eta0, r1) - zeta * gamma0_log)
|
|
319
|
+
value1 = s1_len * denominator_factor * (angle_pm * _dot3(eta1, r2) - zeta * gamma1_log)
|
|
320
|
+
value2 = s2_len * denominator_factor * (angle_pm * _dot3(eta2, r0) - zeta * gamma2_log)
|
|
321
|
+
if np.isfinite(value0):
|
|
322
|
+
contributions[0] = value0
|
|
323
|
+
if np.isfinite(value1):
|
|
324
|
+
contributions[1] = value1
|
|
325
|
+
if np.isfinite(value2):
|
|
326
|
+
contributions[2] = value2
|
|
327
|
+
return contributions
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
@_jit
|
|
331
|
+
def _boundary_node_solid_angles_fast(points: np.ndarray, simplices: np.ndarray) -> np.ndarray:
|
|
332
|
+
angles = np.zeros(len(points), dtype=np.float64)
|
|
333
|
+
for simplex_index in range(len(simplices)):
|
|
334
|
+
simplex = simplices[simplex_index]
|
|
335
|
+
point0 = int(simplex[0])
|
|
336
|
+
point1 = int(simplex[1])
|
|
337
|
+
point2 = int(simplex[2])
|
|
338
|
+
point3 = int(simplex[3])
|
|
339
|
+
angles[point0] += abs(
|
|
340
|
+
_triangle_space_angle_fast(
|
|
341
|
+
points[point0],
|
|
342
|
+
points[point1],
|
|
343
|
+
points[point2],
|
|
344
|
+
points[point3],
|
|
345
|
+
)
|
|
346
|
+
)
|
|
347
|
+
angles[point1] += abs(
|
|
348
|
+
_triangle_space_angle_fast(
|
|
349
|
+
points[point1],
|
|
350
|
+
points[point0],
|
|
351
|
+
points[point2],
|
|
352
|
+
points[point3],
|
|
353
|
+
)
|
|
354
|
+
)
|
|
355
|
+
angles[point2] += abs(
|
|
356
|
+
_triangle_space_angle_fast(
|
|
357
|
+
points[point2],
|
|
358
|
+
points[point0],
|
|
359
|
+
points[point1],
|
|
360
|
+
points[point3],
|
|
361
|
+
)
|
|
362
|
+
)
|
|
363
|
+
angles[point3] += abs(
|
|
364
|
+
_triangle_space_angle_fast(
|
|
365
|
+
points[point3],
|
|
366
|
+
points[point0],
|
|
367
|
+
points[point1],
|
|
368
|
+
points[point2],
|
|
369
|
+
)
|
|
370
|
+
)
|
|
371
|
+
return angles
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
@_jit
|
|
375
|
+
def _build_lindholm_bem_matrix_fast(
|
|
376
|
+
points: np.ndarray,
|
|
377
|
+
simplices: np.ndarray,
|
|
378
|
+
boundary_faces: np.ndarray,
|
|
379
|
+
face_points: np.ndarray,
|
|
380
|
+
zeta_vectors: np.ndarray,
|
|
381
|
+
eta_vectors: np.ndarray,
|
|
382
|
+
edge_lengths: np.ndarray,
|
|
383
|
+
corner_cosines: np.ndarray,
|
|
384
|
+
denominator_factors: np.ndarray,
|
|
385
|
+
boundary_nodes: np.ndarray,
|
|
386
|
+
local_index_by_point: np.ndarray,
|
|
387
|
+
) -> np.ndarray:
|
|
388
|
+
bem = np.zeros((len(boundary_nodes), len(boundary_nodes)), dtype=np.float64)
|
|
389
|
+
solid_angles = _boundary_node_solid_angles_fast(points, simplices)
|
|
390
|
+
|
|
391
|
+
for row in range(len(boundary_nodes)):
|
|
392
|
+
observer_index = int(boundary_nodes[row])
|
|
393
|
+
observer = points[observer_index]
|
|
394
|
+
for face_index in range(len(boundary_faces)):
|
|
395
|
+
face = boundary_faces[face_index]
|
|
396
|
+
point0 = int(face[0])
|
|
397
|
+
point1 = int(face[1])
|
|
398
|
+
point2 = int(face[2])
|
|
399
|
+
if denominator_factors[face_index] == 0.0:
|
|
400
|
+
continue
|
|
401
|
+
contributions = _lindholm_triangle_contributions_precomputed_fast(
|
|
402
|
+
observer,
|
|
403
|
+
face_points[face_index, 0],
|
|
404
|
+
face_points[face_index, 1],
|
|
405
|
+
face_points[face_index, 2],
|
|
406
|
+
zeta_vectors[face_index],
|
|
407
|
+
eta_vectors[face_index, 0],
|
|
408
|
+
eta_vectors[face_index, 1],
|
|
409
|
+
eta_vectors[face_index, 2],
|
|
410
|
+
edge_lengths[face_index],
|
|
411
|
+
corner_cosines[face_index],
|
|
412
|
+
denominator_factors[face_index],
|
|
413
|
+
)
|
|
414
|
+
bem[row, int(local_index_by_point[point0])] += contributions[0]
|
|
415
|
+
bem[row, int(local_index_by_point[point1])] += contributions[1]
|
|
416
|
+
bem[row, int(local_index_by_point[point2])] += contributions[2]
|
|
417
|
+
bem[row, row] += solid_angles[observer_index] / (4.0 * np.pi) - 1.0
|
|
418
|
+
return bem
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
@_jit
|
|
422
|
+
def _apply_lindholm_bem_matrix_free_fast(
|
|
423
|
+
points: np.ndarray,
|
|
424
|
+
boundary_faces: np.ndarray,
|
|
425
|
+
face_points: np.ndarray,
|
|
426
|
+
zeta_vectors: np.ndarray,
|
|
427
|
+
eta_vectors: np.ndarray,
|
|
428
|
+
edge_lengths: np.ndarray,
|
|
429
|
+
corner_cosines: np.ndarray,
|
|
430
|
+
denominator_factors: np.ndarray,
|
|
431
|
+
boundary_nodes: np.ndarray,
|
|
432
|
+
local_index_by_point: np.ndarray,
|
|
433
|
+
solid_angles: np.ndarray,
|
|
434
|
+
values: np.ndarray,
|
|
435
|
+
) -> np.ndarray:
|
|
436
|
+
result = np.zeros(len(boundary_nodes), dtype=np.float64)
|
|
437
|
+
for row in range(len(boundary_nodes)):
|
|
438
|
+
observer_index = int(boundary_nodes[row])
|
|
439
|
+
observer = points[observer_index]
|
|
440
|
+
value = 0.0
|
|
441
|
+
for face_index in range(len(boundary_faces)):
|
|
442
|
+
if denominator_factors[face_index] == 0.0:
|
|
443
|
+
continue
|
|
444
|
+
face = boundary_faces[face_index]
|
|
445
|
+
contributions = _lindholm_triangle_contributions_precomputed_fast(
|
|
446
|
+
observer,
|
|
447
|
+
face_points[face_index, 0],
|
|
448
|
+
face_points[face_index, 1],
|
|
449
|
+
face_points[face_index, 2],
|
|
450
|
+
zeta_vectors[face_index],
|
|
451
|
+
eta_vectors[face_index, 0],
|
|
452
|
+
eta_vectors[face_index, 1],
|
|
453
|
+
eta_vectors[face_index, 2],
|
|
454
|
+
edge_lengths[face_index],
|
|
455
|
+
corner_cosines[face_index],
|
|
456
|
+
denominator_factors[face_index],
|
|
457
|
+
)
|
|
458
|
+
value += contributions[0] * values[int(local_index_by_point[int(face[0])])]
|
|
459
|
+
value += contributions[1] * values[int(local_index_by_point[int(face[1])])]
|
|
460
|
+
value += contributions[2] * values[int(local_index_by_point[int(face[2])])]
|
|
461
|
+
value += (solid_angles[observer_index] / (4.0 * np.pi) - 1.0) * values[row]
|
|
462
|
+
result[row] = value
|
|
463
|
+
return result
|