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
nmag/demag/geometry.py
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import numpy.typing as npt
|
|
5
|
+
|
|
6
|
+
FloatArray = npt.NDArray[np.float64]
|
|
7
|
+
IntArray = npt.NDArray[np.int64]
|
|
8
|
+
|
|
9
|
+
TETRA_FACE_VERTICES = ((1, 2, 3), (0, 3, 2), (0, 1, 3), (0, 2, 1))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _validate_tetrahedral_cells(points: FloatArray, simplices: IntArray) -> None:
|
|
13
|
+
if len(simplices) == 0:
|
|
14
|
+
return
|
|
15
|
+
cell_points = points[simplices]
|
|
16
|
+
edges = cell_points[:, 1:] - cell_points[:, [0]]
|
|
17
|
+
length_scales = np.max(np.linalg.norm(edges, axis=2), axis=1)
|
|
18
|
+
determinants = np.abs(np.linalg.det(edges))
|
|
19
|
+
with np.errstate(divide="ignore", invalid="ignore"):
|
|
20
|
+
normalized_determinants = determinants / (length_scales**3)
|
|
21
|
+
invalid = (
|
|
22
|
+
~np.isfinite(normalized_determinants)
|
|
23
|
+
| (length_scales <= 0.0)
|
|
24
|
+
| (normalized_determinants <= 1.0e-12)
|
|
25
|
+
)
|
|
26
|
+
if np.any(invalid):
|
|
27
|
+
indices = np.flatnonzero(invalid).tolist()
|
|
28
|
+
raise ValueError(
|
|
29
|
+
f"Demag FEM requires non-degenerate tetrahedra; invalid cell indices: {indices}."
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _simplex_volumes(points: FloatArray, simplices: IntArray) -> FloatArray:
|
|
34
|
+
"""Return 3D simplex volumes for tetrahedral mesh cells."""
|
|
35
|
+
if len(simplices) == 0:
|
|
36
|
+
return np.empty(0, dtype=float)
|
|
37
|
+
matrices = points[simplices[:, 1:]] - points[simplices[:, [0]]]
|
|
38
|
+
return np.abs(np.linalg.det(matrices)) / 6.0
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _mean_edge_length(points: FloatArray, simplices: IntArray) -> float:
|
|
42
|
+
lengths: list[float] = []
|
|
43
|
+
for simplex in simplices:
|
|
44
|
+
for left_index, left in enumerate(simplex):
|
|
45
|
+
for right in simplex[left_index + 1 :]:
|
|
46
|
+
lengths.append(float(np.linalg.norm(points[int(left)] - points[int(right)])))
|
|
47
|
+
if not lengths:
|
|
48
|
+
return 1.0
|
|
49
|
+
return max(float(np.mean(lengths)), np.finfo(float).eps)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _tetrahedral_barycentric_coordinates(
|
|
53
|
+
tetrahedron: FloatArray,
|
|
54
|
+
point: FloatArray,
|
|
55
|
+
*,
|
|
56
|
+
tolerance: float = 1.0e-12,
|
|
57
|
+
) -> FloatArray | None:
|
|
58
|
+
matrix = np.column_stack(
|
|
59
|
+
[
|
|
60
|
+
tetrahedron[1] - tetrahedron[0],
|
|
61
|
+
tetrahedron[2] - tetrahedron[0],
|
|
62
|
+
tetrahedron[3] - tetrahedron[0],
|
|
63
|
+
]
|
|
64
|
+
)
|
|
65
|
+
try:
|
|
66
|
+
local = np.asarray(np.linalg.solve(matrix, point - tetrahedron[0]), dtype=float)
|
|
67
|
+
except np.linalg.LinAlgError:
|
|
68
|
+
return None
|
|
69
|
+
barycentric = np.asarray(
|
|
70
|
+
[1.0 - float(np.sum(local)), local[0], local[1], local[2]],
|
|
71
|
+
dtype=float,
|
|
72
|
+
)
|
|
73
|
+
if np.all(barycentric >= -tolerance) and np.all(barycentric <= 1.0 + tolerance):
|
|
74
|
+
return np.minimum(np.maximum(barycentric, 0.0), 1.0)
|
|
75
|
+
return None
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _oriented_boundary_faces(
|
|
79
|
+
points: FloatArray, simplices: IntArray
|
|
80
|
+
) -> list[tuple[int, tuple[int, int, int]]]:
|
|
81
|
+
if len(simplices) == 0:
|
|
82
|
+
return []
|
|
83
|
+
|
|
84
|
+
cell_indices = np.repeat(
|
|
85
|
+
np.arange(len(simplices), dtype=np.int64),
|
|
86
|
+
len(TETRA_FACE_VERTICES),
|
|
87
|
+
)
|
|
88
|
+
face_nodes: IntArray = np.asarray(
|
|
89
|
+
[
|
|
90
|
+
[int(simplex[first]), int(simplex[second]), int(simplex[third])]
|
|
91
|
+
for simplex in simplices
|
|
92
|
+
for first, second, third in TETRA_FACE_VERTICES
|
|
93
|
+
],
|
|
94
|
+
dtype=np.int64,
|
|
95
|
+
)
|
|
96
|
+
face_keys = np.sort(face_nodes, axis=1)
|
|
97
|
+
unique_face_keys, inverse, counts = np.unique(
|
|
98
|
+
face_keys,
|
|
99
|
+
axis=0,
|
|
100
|
+
return_inverse=True,
|
|
101
|
+
return_counts=True,
|
|
102
|
+
)
|
|
103
|
+
non_manifold_faces = unique_face_keys[counts > 2]
|
|
104
|
+
if len(non_manifold_faces):
|
|
105
|
+
raise ValueError(
|
|
106
|
+
"Non-manifold mesh: at least one face is shared by more than two tetrahedra."
|
|
107
|
+
)
|
|
108
|
+
boundary_positions = np.flatnonzero(counts[inverse] == 1)
|
|
109
|
+
if len(boundary_positions) == 0:
|
|
110
|
+
return []
|
|
111
|
+
|
|
112
|
+
boundary_cells = cell_indices[boundary_positions]
|
|
113
|
+
boundary_face_nodes: IntArray = np.asarray(
|
|
114
|
+
[face_nodes[int(position)] for position in boundary_positions],
|
|
115
|
+
dtype=np.int64,
|
|
116
|
+
)
|
|
117
|
+
face_points = points[boundary_face_nodes]
|
|
118
|
+
cell_centers = np.mean(points[simplices[boundary_cells]], axis=1)
|
|
119
|
+
face_centers = np.mean(face_points, axis=1)
|
|
120
|
+
normals = np.cross(face_points[:, 1] - face_points[:, 0], face_points[:, 2] - face_points[:, 0])
|
|
121
|
+
points_inward = np.einsum("ij,ij->i", normals, cell_centers - face_centers) > 0.0
|
|
122
|
+
for position in np.flatnonzero(points_inward):
|
|
123
|
+
position_index = int(position)
|
|
124
|
+
second = int(boundary_face_nodes[position_index, 1])
|
|
125
|
+
boundary_face_nodes[position_index, 1] = boundary_face_nodes[position_index, 2]
|
|
126
|
+
boundary_face_nodes[position_index, 2] = second
|
|
127
|
+
|
|
128
|
+
return [
|
|
129
|
+
(
|
|
130
|
+
int(cell_index),
|
|
131
|
+
(int(face[0]), int(face[1]), int(face[2])),
|
|
132
|
+
)
|
|
133
|
+
for cell_index, face in zip(boundary_cells, boundary_face_nodes, strict=True)
|
|
134
|
+
]
|
nmag/demag/lindholm.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _triangle_space_angle(
|
|
7
|
+
observer: np.ndarray,
|
|
8
|
+
p0: np.ndarray,
|
|
9
|
+
p1: np.ndarray,
|
|
10
|
+
p2: np.ndarray,
|
|
11
|
+
) -> float:
|
|
12
|
+
"""Legacy unsigned space angle of a triangle as seen from ``observer``."""
|
|
13
|
+
|
|
14
|
+
r0 = p0 - observer
|
|
15
|
+
r1 = p1 - observer
|
|
16
|
+
r2 = p2 - observer
|
|
17
|
+
r0_len = float(np.linalg.norm(r0))
|
|
18
|
+
r1_len = float(np.linalg.norm(r1))
|
|
19
|
+
r2_len = float(np.linalg.norm(r2))
|
|
20
|
+
dot01 = float(np.dot(r0, r1))
|
|
21
|
+
dot12 = float(np.dot(r1, r2))
|
|
22
|
+
dot20 = float(np.dot(r2, r0))
|
|
23
|
+
numerator = r0_len * r1_len * r2_len + r0_len * dot12 + r1_len * dot20 + r2_len * dot01
|
|
24
|
+
denominator_squared = (
|
|
25
|
+
2.0 * (r1_len * r2_len + dot12) * (r2_len * r0_len + dot20) * (r0_len * r1_len + dot01)
|
|
26
|
+
)
|
|
27
|
+
if denominator_squared <= 0.0:
|
|
28
|
+
return 0.0
|
|
29
|
+
quotient = numerator / float(np.sqrt(denominator_squared))
|
|
30
|
+
return float(2.0 * np.arccos(min(1.0, max(-1.0, quotient))))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _normalised(vector: np.ndarray) -> np.ndarray:
|
|
34
|
+
length = float(np.linalg.norm(vector))
|
|
35
|
+
if length == 0.0:
|
|
36
|
+
return np.zeros_like(vector, dtype=float)
|
|
37
|
+
return vector / length
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _safe_log_ratio(numerator: float, denominator: float) -> float:
|
|
41
|
+
if denominator <= 0.0:
|
|
42
|
+
return float("nan")
|
|
43
|
+
with np.errstate(divide="ignore", invalid="ignore"):
|
|
44
|
+
return float(np.log(numerator / denominator))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _lindholm_triangle_contributions(
|
|
48
|
+
observer: np.ndarray,
|
|
49
|
+
p0: np.ndarray,
|
|
50
|
+
p1: np.ndarray,
|
|
51
|
+
p2: np.ndarray,
|
|
52
|
+
outward_surface_normal: np.ndarray,
|
|
53
|
+
) -> np.ndarray:
|
|
54
|
+
"""Legacy Lindholm BEM row contribution for one first-order triangle."""
|
|
55
|
+
|
|
56
|
+
angle = _triangle_space_angle(observer, p0, p1, p2)
|
|
57
|
+
r0 = p0 - observer
|
|
58
|
+
r1 = p1 - observer
|
|
59
|
+
r2 = p2 - observer
|
|
60
|
+
s0 = r2 - r1
|
|
61
|
+
s1 = r0 - r2
|
|
62
|
+
s2 = r1 - r0
|
|
63
|
+
xi0 = _normalised(s0)
|
|
64
|
+
xi1 = _normalised(s1)
|
|
65
|
+
xi2 = _normalised(s2)
|
|
66
|
+
area_vector = np.cross(p1 - p0, p2 - p0)
|
|
67
|
+
area = 0.5 * float(np.linalg.norm(area_vector))
|
|
68
|
+
if area == 0.0:
|
|
69
|
+
return np.zeros(3, dtype=float)
|
|
70
|
+
|
|
71
|
+
zeta_vector = _normalised(area_vector)
|
|
72
|
+
r0_len = float(np.linalg.norm(r0))
|
|
73
|
+
r1_len = float(np.linalg.norm(r1))
|
|
74
|
+
r2_len = float(np.linalg.norm(r2))
|
|
75
|
+
s0_len = float(np.linalg.norm(s0))
|
|
76
|
+
s1_len = float(np.linalg.norm(s1))
|
|
77
|
+
s2_len = float(np.linalg.norm(s2))
|
|
78
|
+
c01 = float(np.dot(xi0, xi1))
|
|
79
|
+
c12 = float(np.dot(xi1, xi2))
|
|
80
|
+
c20 = float(np.dot(xi2, xi0))
|
|
81
|
+
zeta = float(np.dot(zeta_vector, r0))
|
|
82
|
+
eta0 = np.cross(zeta_vector, xi0)
|
|
83
|
+
eta1 = np.cross(zeta_vector, xi1)
|
|
84
|
+
eta2 = np.cross(zeta_vector, xi2)
|
|
85
|
+
log_terms = np.asarray(
|
|
86
|
+
[
|
|
87
|
+
_safe_log_ratio(r1_len + r2_len + s0_len, r1_len + r2_len - s0_len),
|
|
88
|
+
_safe_log_ratio(r2_len + r0_len + s1_len, r2_len + r0_len - s1_len),
|
|
89
|
+
_safe_log_ratio(r0_len + r1_len + s2_len, r0_len + r1_len - s2_len),
|
|
90
|
+
],
|
|
91
|
+
dtype=float,
|
|
92
|
+
)
|
|
93
|
+
gamma0 = np.asarray([1.0, c01, c20], dtype=float)
|
|
94
|
+
gamma1 = np.asarray([c01, 1.0, c12], dtype=float)
|
|
95
|
+
gamma2 = np.asarray([c20, c12, 1.0], dtype=float)
|
|
96
|
+
sign_zeta = -1.0 if zeta < 0.0 else (1.0 if zeta > 0.0 else 0.0)
|
|
97
|
+
angle_pm = angle * sign_zeta
|
|
98
|
+
denominator_factor = 1.0 / (8.0 * np.pi * area)
|
|
99
|
+
with np.errstate(invalid="ignore"):
|
|
100
|
+
contributions = np.asarray(
|
|
101
|
+
[
|
|
102
|
+
s0_len
|
|
103
|
+
* denominator_factor
|
|
104
|
+
* (angle_pm * float(np.dot(eta0, r1)) - zeta * float(np.dot(gamma0, log_terms))),
|
|
105
|
+
s1_len
|
|
106
|
+
* denominator_factor
|
|
107
|
+
* (angle_pm * float(np.dot(eta1, r2)) - zeta * float(np.dot(gamma1, log_terms))),
|
|
108
|
+
s2_len
|
|
109
|
+
* denominator_factor
|
|
110
|
+
* (angle_pm * float(np.dot(eta2, r0)) - zeta * float(np.dot(gamma2, log_terms))),
|
|
111
|
+
],
|
|
112
|
+
dtype=float,
|
|
113
|
+
)
|
|
114
|
+
contributions = np.where(np.isfinite(contributions), contributions, 0.0)
|
|
115
|
+
if float(np.dot(zeta_vector, outward_surface_normal)) < 0.0:
|
|
116
|
+
contributions = -contributions
|
|
117
|
+
return contributions
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _boundary_node_solid_angles(points: np.ndarray, simplices: np.ndarray) -> np.ndarray:
|
|
121
|
+
angles = np.zeros(len(points), dtype=float)
|
|
122
|
+
for simplex in simplices:
|
|
123
|
+
for local_index, point_index in enumerate(simplex):
|
|
124
|
+
other_indices = [index for index in range(4) if index != local_index]
|
|
125
|
+
angles[int(point_index)] += abs(
|
|
126
|
+
_triangle_space_angle(
|
|
127
|
+
points[int(point_index)],
|
|
128
|
+
points[int(simplex[other_indices[0]])],
|
|
129
|
+
points[int(simplex[other_indices[1]])],
|
|
130
|
+
points[int(simplex[other_indices[2]])],
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
return angles
|