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.
Files changed (147) hide show
  1. anisotropy/__init__.py +28 -0
  2. anisotropy/anisotropy.py +13 -0
  3. anisotropy/evaluation.py +70 -0
  4. anisotropy/model.py +200 -0
  5. anisotropy/predefined.py +202 -0
  6. anisotropy/py.typed +1 -0
  7. anisotropy/values.py +84 -0
  8. mag_material/__init__.py +3 -0
  9. mag_material/mag_material.py +231 -0
  10. mag_material/parameters.py +150 -0
  11. mag_material/py.typed +1 -0
  12. nmag/__init__.py +36 -0
  13. nmag/backends.py +493 -0
  14. nmag/checkpoint.py +327 -0
  15. nmag/config.py +174 -0
  16. nmag/demag/__init__.py +39 -0
  17. nmag/demag/bem_operator.py +149 -0
  18. nmag/demag/geometry.py +134 -0
  19. nmag/demag/lindholm.py +133 -0
  20. nmag/demag/lindholm_fast.py +463 -0
  21. nmag/demag/linear.py +489 -0
  22. nmag/dynamics/__init__.py +255 -0
  23. nmag/output.py +27 -0
  24. nmag/parallel.py +45 -0
  25. nmag/py.typed +1 -0
  26. nmag/resources.py +38 -0
  27. nmag/simulation/__init__.py +519 -0
  28. nmag/simulation/anisotropy/__init__.py +5 -0
  29. nmag/simulation/anisotropy/fields.py +56 -0
  30. nmag/simulation/anisotropy/materials.py +138 -0
  31. nmag/simulation/demag/__init__.py +1 -0
  32. nmag/simulation/demag/bem/__init__.py +11 -0
  33. nmag/simulation/demag/bem/diagnostics.py +73 -0
  34. nmag/simulation/demag/bem/dirichlet.py +85 -0
  35. nmag/simulation/demag/bem/hierarchical.py +74 -0
  36. nmag/simulation/demag/bem/operator.py +292 -0
  37. nmag/simulation/demag/fem/__init__.py +11 -0
  38. nmag/simulation/demag/fem/assembly.py +114 -0
  39. nmag/simulation/demag/fem/charges.py +38 -0
  40. nmag/simulation/demag/fem/geometry.py +261 -0
  41. nmag/simulation/demag/fields/__init__.py +11 -0
  42. nmag/simulation/demag/fields/auxiliary.py +186 -0
  43. nmag/simulation/demag/fields/probe.py +77 -0
  44. nmag/simulation/demag/fields/recovery.py +207 -0
  45. nmag/simulation/demag/solver.py +17 -0
  46. nmag/simulation/dynamics/__init__.py +92 -0
  47. nmag/simulation/dynamics/advance.py +199 -0
  48. nmag/simulation/dynamics/integrator.py +263 -0
  49. nmag/simulation/exchange/__init__.py +13 -0
  50. nmag/simulation/exchange/coefficients.py +185 -0
  51. nmag/simulation/exchange/fields.py +106 -0
  52. nmag/simulation/exchange/llg_rhs.py +218 -0
  53. nmag/simulation/fields/__init__.py +19 -0
  54. nmag/simulation/fields/arrays.py +111 -0
  55. nmag/simulation/fields/availability.py +188 -0
  56. nmag/simulation/fields/averages.py +293 -0
  57. nmag/simulation/fields/derived.py +164 -0
  58. nmag/simulation/fields/maxangle.py +163 -0
  59. nmag/simulation/fields/probes.py +106 -0
  60. nmag/simulation/implicit_dynamics.py +221 -0
  61. nmag/simulation/mesh/__init__.py +13 -0
  62. nmag/simulation/mesh/geometry.py +153 -0
  63. nmag/simulation/mesh/materials.py +299 -0
  64. nmag/simulation/mesh/probe.py +234 -0
  65. nmag/simulation/restart.py +103 -0
  66. nmag/simulation/support.py +224 -0
  67. nmag_python_3-0.0.2.dist-info/METADATA +157 -0
  68. nmag_python_3-0.0.2.dist-info/RECORD +147 -0
  69. nmag_python_3-0.0.2.dist-info/WHEEL +5 -0
  70. nmag_python_3-0.0.2.dist-info/licenses/LICENSE +339 -0
  71. nmag_python_3-0.0.2.dist-info/top_level.txt +8 -0
  72. nmesh/__init__.py +130 -0
  73. nmesh/backend.py +286 -0
  74. nmesh/geometry/__init__.py +52 -0
  75. nmesh/geometry/boolean_operations.py +157 -0
  76. nmesh/geometry/primitives.py +453 -0
  77. nmesh/geometry/transform.py +126 -0
  78. nmesh/io/__init__.py +50 -0
  79. nmesh/io/ascii.py +132 -0
  80. nmesh/io/legacy_nmesh_hdf5.py +318 -0
  81. nmesh/io/meshio_support.py +170 -0
  82. nmesh/mesh_generation.py +182 -0
  83. nmesh/mesh_io.py +227 -0
  84. nmesh/mesh_model.py +147 -0
  85. nmesh/mesh_utilities.py +79 -0
  86. nmesh/mesher/__init__.py +21 -0
  87. nmesh/mesher/driver.py +146 -0
  88. nmesh/mesher/meshing_defaults.py +252 -0
  89. nmesh/mesher/meshing_parameters.py +185 -0
  90. nmesh/mesher/parity.py +21 -0
  91. nmesh/mesher/parity_canonical.py +142 -0
  92. nmesh/mesher/parity_comparison.py +191 -0
  93. nmesh/mesher/parity_metrics.py +114 -0
  94. nmesh/mesher/periodic.py +97 -0
  95. nmesh/mesher/relaxation/__init__.py +14 -0
  96. nmesh/mesher/relaxation/_constants.py +20 -0
  97. nmesh/mesher/relaxation/_types.py +15 -0
  98. nmesh/mesher/relaxation/density.py +170 -0
  99. nmesh/mesher/relaxation/engine/__init__.py +18 -0
  100. nmesh/mesher/relaxation/engine/state.py +155 -0
  101. nmesh/mesher/relaxation/engine/steps.py +248 -0
  102. nmesh/mesher/relaxation/engine/topology.py +230 -0
  103. nmesh/mesher/relaxation/forces/__init__.py +96 -0
  104. nmesh/mesher/relaxation/forces/jit.py +102 -0
  105. nmesh/mesher/relaxation/forces/neighbors.py +186 -0
  106. nmesh/mesher/relaxation/forces/simplex.py +302 -0
  107. nmesh/mesher/relaxation/forces/summary.py +207 -0
  108. nmesh/mesher/relaxation/forces/types.py +92 -0
  109. nmesh/mesher/relaxation/geometry/__init__.py +6 -0
  110. nmesh/mesher/relaxation/geometry/builder.py +154 -0
  111. nmesh/mesher/relaxation/geometry/model.py +194 -0
  112. nmesh/mesher/relaxation/seeding/__init__.py +74 -0
  113. nmesh/mesher/relaxation/seeding/periodic.py +88 -0
  114. nmesh/mesher/relaxation/seeding/points.py +88 -0
  115. nmesh/mesher/relaxation/seeding/sampling.py +142 -0
  116. nmesh/mesher/relaxation/topology/__init__.py +297 -0
  117. nmesh/mesher/relaxation/topology/finalize.py +78 -0
  118. nmesh/mesher/relaxation/topology/recovery.py +310 -0
  119. nmesh/mesher/sectioned_config.py +70 -0
  120. nmesh/nmesh.py +99 -0
  121. nmesh/py.typed +1 -0
  122. nmesh/utils/__init__.py +33 -0
  123. nmesh/utils/array_list_utils.py +128 -0
  124. nmesh/utils/constants.py +22 -0
  125. nmesh/utils/timing_memory_utils.py +51 -0
  126. nmesh/utils/types.py +13 -0
  127. si/constants.py +49 -0
  128. si/physical.py +722 -0
  129. si/py.typed +1 -0
  130. simulation/__init__.py +1 -0
  131. simulation/clock.py +237 -0
  132. simulation/data_writer.py +273 -0
  133. simulation/data_writer_collection.py +267 -0
  134. simulation/hysteresis.py +74 -0
  135. simulation/hysteresis_runner.py +286 -0
  136. simulation/hysteresis_schedule.py +180 -0
  137. simulation/inference/__init__.py +3 -0
  138. simulation/inference/inference.py +95 -0
  139. simulation/py.typed +1 -0
  140. simulation/quantity.py +88 -0
  141. simulation/simulation_core.py +458 -0
  142. throttler/__init__.py +3 -0
  143. throttler/py.typed +1 -0
  144. throttler/throttler.py +55 -0
  145. when/__init__.py +3 -0
  146. when/py.typed +1 -0
  147. 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