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
when/when.py ADDED
@@ -0,0 +1,416 @@
1
+ """
2
+ Time specification utilities for specifying when to do things
3
+ (such as saving fields or averages).
4
+ """
5
+
6
+ import abc
7
+ import math
8
+ from typing import Any, Protocol
9
+
10
+
11
+ class TimeDict(Protocol):
12
+ def __getitem__(self, key: str, /) -> Any: ...
13
+
14
+ def get(self, key: str, default: Any = None, /) -> Any: ...
15
+
16
+ def copy(self) -> dict[str, Any]: ...
17
+
18
+
19
+ NextTime = Any
20
+
21
+
22
+ def _float_is_integer(f: float) -> bool:
23
+ return math.isclose(f, round(f))
24
+
25
+
26
+ # --- Abstract Base Class for Specification Logic ---
27
+
28
+
29
+ class _WhenSpec(abc.ABC):
30
+ """
31
+ Abstract base class for a time specification.
32
+ This is an internal implementation detail. The user interacts
33
+ with the 'When' class, which wraps this.
34
+ """
35
+
36
+ @abc.abstractmethod
37
+ def __repr__(self) -> str:
38
+ """Return the string representation."""
39
+ raise NotImplementedError
40
+
41
+ @abc.abstractmethod
42
+ def match_time(self, this_time: TimeDict) -> bool:
43
+ """Check if the current time matches this specification."""
44
+ raise NotImplementedError
45
+
46
+ @abc.abstractmethod
47
+ def next_time(self, identifier: str, this_time: TimeDict) -> NextTime:
48
+ """Calculate the next matching time for the given identifier."""
49
+ raise NotImplementedError
50
+
51
+
52
+ # --- Concrete Specification Implementations ---
53
+
54
+
55
+ class _AtSpec(_WhenSpec):
56
+ """Specification for a single point in time."""
57
+
58
+ def __init__(self, identifier: str, value: Any) -> None:
59
+ self.identifier = identifier
60
+ self.value = value
61
+
62
+ def __repr__(self) -> str:
63
+ return f"at({self.identifier!r}, {self.value!r})"
64
+
65
+ def match_time(self, this_time: TimeDict) -> bool:
66
+ return this_time.get(self.identifier) == self.value
67
+
68
+ def next_time(self, identifier: str, this_time: TimeDict) -> NextTime:
69
+ if self.identifier != identifier:
70
+ return True # Irrelevant to this identifier, so don't block
71
+
72
+ t = this_time.get(identifier)
73
+ if isinstance(t, bool):
74
+ if self.value is True:
75
+ return True
76
+ else:
77
+ return t == self.value
78
+ else:
79
+ if t < self.value:
80
+ return self.value
81
+ else:
82
+ return False
83
+
84
+
85
+ class _EverySpec(_WhenSpec):
86
+ """Specification for a periodic event."""
87
+
88
+ def __init__(
89
+ self,
90
+ identifier: str,
91
+ delta: Any | None,
92
+ first: Any,
93
+ last: Any | None,
94
+ ) -> None:
95
+ self.identifier = identifier
96
+ self.delta = delta
97
+ self.first = first
98
+ self.last = last
99
+
100
+ def __repr__(self) -> str:
101
+ opts = ""
102
+ if self.first != 0:
103
+ opts += f", first={self.first}"
104
+ if self.last is not None:
105
+ opts += f", last={self.last}"
106
+
107
+ delta_str = str(self.delta) if self.delta is not None else "None"
108
+ return f"every({delta_str}, {self.identifier!r}{opts})"
109
+
110
+ def match_time(self, this_time: TimeDict) -> bool:
111
+ t = this_time.get(self.identifier)
112
+ if t is None:
113
+ return False
114
+
115
+ if t < self.first:
116
+ return False
117
+ if self.last is not None and t > self.last:
118
+ return False
119
+ if self.delta is None:
120
+ return True
121
+
122
+ if self.delta <= 0:
123
+ return False
124
+
125
+ pos = float((t - self.first) / self.delta)
126
+ return _float_is_integer(pos)
127
+
128
+ def next_time(self, identifier: str, this_time: TimeDict) -> NextTime:
129
+ if self.identifier != identifier:
130
+ return self.match_time(this_time)
131
+
132
+ t = this_time.get(identifier)
133
+ if t is None:
134
+ return False
135
+
136
+ if self.last is not None and t >= self.last:
137
+ return False
138
+
139
+ if t < self.first and self.delta is not None:
140
+ return self.first
141
+
142
+ if self.delta is None:
143
+ return True
144
+
145
+ if self.delta <= 0:
146
+ return False
147
+
148
+ pos = float((t - self.first) / self.delta)
149
+
150
+ if _float_is_integer(pos):
151
+ next_pos = int(round(pos)) + 1
152
+ else:
153
+ next_pos = int(pos) + 1
154
+
155
+ next_t = self.delta * next_pos + self.first
156
+
157
+ if self.last is not None and next_t > self.last:
158
+ return False
159
+
160
+ return next_t
161
+
162
+
163
+ class _OrSpec(_WhenSpec):
164
+ """Specification for a logical OR of two specifications."""
165
+
166
+ def __init__(self, spec1: _WhenSpec, spec2: _WhenSpec) -> None:
167
+ self.spec1 = spec1
168
+ self.spec2 = spec2
169
+
170
+ def __repr__(self) -> str:
171
+ return f"({self.spec1!r} | {self.spec2!r})"
172
+
173
+ def match_time(self, this_time: TimeDict) -> bool:
174
+ return self.spec1.match_time(this_time) or self.spec2.match_time(this_time)
175
+
176
+ def next_time(self, identifier: str, this_time: TimeDict) -> NextTime:
177
+ nt1 = self.spec1.next_time(identifier, this_time)
178
+ nt2 = self.spec2.next_time(identifier, this_time)
179
+
180
+ nt1_is_bool = isinstance(nt1, bool)
181
+ nt2_is_bool = isinstance(nt2, bool)
182
+
183
+ if nt1_is_bool and nt2_is_bool:
184
+ return nt1 or nt2
185
+
186
+ if nt1 is False:
187
+ return nt2
188
+ if nt2 is False:
189
+ return nt1
190
+
191
+ if nt1 is True:
192
+ return True
193
+ if nt2 is True:
194
+ return True
195
+
196
+ return min(nt1, nt2)
197
+
198
+
199
+ class _AndSpec(_WhenSpec):
200
+ """Specification for a logical AND of two specifications."""
201
+
202
+ def __init__(self, spec1: _WhenSpec, spec2: _WhenSpec) -> None:
203
+ self.spec1 = spec1
204
+ self.spec2 = spec2
205
+
206
+ def __repr__(self) -> str:
207
+ return f"({self.spec1!r} & {self.spec2!r})"
208
+
209
+ def match_time(self, this_time: TimeDict) -> bool:
210
+ return self.spec1.match_time(this_time) and self.spec2.match_time(this_time)
211
+
212
+ def next_time(self, identifier: str, this_time: TimeDict) -> NextTime:
213
+ # Use a copy to avoid side effects on the original time dict
214
+ temp_time = this_time.copy()
215
+ save_t = temp_time.get(identifier)
216
+ if save_t is None:
217
+ return False # Can't calculate if identifier is missing
218
+
219
+ ntmax: int | float | None = None
220
+
221
+ both_match = False
222
+ while not both_match:
223
+ nt1 = self.spec1.next_time(identifier, temp_time)
224
+ nt2 = self.spec2.next_time(identifier, temp_time)
225
+
226
+ if nt1 is False or nt2 is False:
227
+ return False
228
+
229
+ if nt1 is True:
230
+ return nt2
231
+ if nt2 is True:
232
+ return nt1
233
+
234
+ if nt1 > nt2:
235
+ ntmax, argmin = nt1, self.spec2
236
+ else:
237
+ ntmax, argmin = nt2, self.spec1
238
+
239
+ temp_time[identifier] = ntmax
240
+ both_match = argmin.match_time(temp_time)
241
+
242
+ assert ntmax is not None, "Logic error: 'and' loop exited without setting ntmax"
243
+
244
+ return ntmax
245
+
246
+
247
+ class _NeverSpec(_WhenSpec):
248
+ """Specification that never matches."""
249
+
250
+ def __repr__(self) -> str:
251
+ return "never"
252
+
253
+ def match_time(self, this_time: TimeDict) -> bool:
254
+ return False
255
+
256
+ def next_time(self, identifier: str, this_time: TimeDict) -> NextTime:
257
+ return False
258
+
259
+
260
+ # --- Public-Facing 'When' Class ---
261
+
262
+
263
+ class When:
264
+ """Combine a schedule condition with ``|`` or ``&`` operators.
265
+
266
+ Users normally create instances with :func:`at` and :func:`every` rather
267
+ than calling this constructor directly.
268
+ """
269
+
270
+ def __init__(self, spec: _WhenSpec) -> None:
271
+ self.spec = spec
272
+
273
+ def __repr__(self) -> str:
274
+ return f"<When: {self.spec!r}>"
275
+
276
+ def __str__(self) -> str:
277
+ return repr(self.spec)
278
+
279
+ def match_time(self, this_time: TimeDict) -> bool:
280
+ """Return whether this condition matches a simulation clock mapping."""
281
+ return self.spec.match_time(this_time)
282
+
283
+ def next_time(
284
+ self, identifier: str, this_time: TimeDict, tols: dict[str, Any] | None = None
285
+ ) -> NextTime:
286
+ """Return the next match for one clock identifier.
287
+
288
+ Args:
289
+ identifier: Clock key such as ``step``, ``time``, or ``stage_time``.
290
+ this_time: Current clock-like mapping.
291
+ tols: Optional per-identifier tolerance preventing a floating-point
292
+ boundary from triggering repeatedly.
293
+
294
+ Returns:
295
+ Next matching value or a boolean sentinel used by schedule merging.
296
+ """
297
+ nt = self.spec.next_time(identifier, this_time)
298
+
299
+ # Apply tolerance logic from the original class
300
+ if tols is not None and identifier in tols and type(nt) is not bool:
301
+ tol = tols[identifier]
302
+ tt = this_time[identifier]
303
+
304
+ if tol > 0.0 and abs(nt - tt) < tol:
305
+ # We are too close to the current time.
306
+ # Advance time slightly and recalculate.
307
+ temp_time = this_time.copy()
308
+ temp_time[identifier] = nt + tol
309
+ nt = self.spec.next_time(identifier, temp_time)
310
+
311
+ return nt
312
+
313
+ def __or__(self, other: object) -> "When":
314
+ """Combines two 'When' objects with a logical OR."""
315
+ if not isinstance(other, When):
316
+ return NotImplemented
317
+ return When(_OrSpec(self.spec, other.spec))
318
+
319
+ def __and__(self, other: object) -> "When":
320
+ """
321
+ Combines two 'When' objects with a logical AND.
322
+
323
+ WARNING: As in the original, this can lead to infinite loops
324
+ if the two conditions are mutually exclusive (e.g.,
325
+ every('step', 2) & every('step', 2, first=1)).
326
+ Use with care.
327
+ """
328
+ if not isinstance(other, When):
329
+ return NotImplemented
330
+ return When(_AndSpec(self.spec, other.spec))
331
+
332
+
333
+ # --- Factory Functions (Public API) ---
334
+
335
+
336
+ def at(identifier: str, value: Any = True) -> When:
337
+ """Create a condition matching one exact clock value or event.
338
+
339
+ Args:
340
+ identifier: Clock key or event such as ``"step"``, ``"time"``,
341
+ ``"convergence"``, or ``"stage_end"``.
342
+ value: Exact value to match. Boolean events default to true.
343
+
344
+ Returns:
345
+ Schedule condition, for example ``at("step", 10)``.
346
+ """
347
+ return When(_AtSpec(identifier, value))
348
+
349
+
350
+ def every(
351
+ arg1: Any,
352
+ arg2: Any | None = None,
353
+ first: Any = 0.0,
354
+ last: Any | None = None,
355
+ ) -> When:
356
+ """Create a periodic clock condition.
357
+
358
+ Args:
359
+ arg1: Preferred clock identifier, such as ``"step"`` or ``"time"``.
360
+ A legacy delta-first call is also accepted.
361
+ arg2: Positive interval in clock units, or the identifier in the legacy
362
+ argument order.
363
+ first: First value eligible to match.
364
+ last: Optional final eligible value; it must exceed ``first``.
365
+
366
+ Returns:
367
+ Periodic condition, for example ``every("step", 10)``.
368
+
369
+ Raises:
370
+ ValueError: If no identifier is supplied, the interval is non-positive,
371
+ or the requested range is invalid.
372
+ """
373
+
374
+ identifier: str | None = None
375
+ delta: Any | None = None
376
+
377
+ # Handle swapped arguments with explicit type-checking
378
+ if isinstance(arg1, str):
379
+ identifier = arg1
380
+ if arg2 is not None and not isinstance(arg2, str):
381
+ delta = arg2
382
+ elif arg2 is None:
383
+ delta = None # e.g., every('step', first=10)
384
+ # If arg2 is a str, we let it fail validation below
385
+
386
+ else:
387
+ delta = arg1
388
+ if isinstance(arg2, str):
389
+ identifier = arg2
390
+ # If arg2 is numeric or None, we let it fail validation below
391
+
392
+ # --- Validation ---
393
+
394
+ # 1. Check if identifier was found and is a string
395
+ if not isinstance(identifier, str):
396
+ raise ValueError(
397
+ "Bad usage of 'every': you must specify an identifier (string). "
398
+ "Example: every('step', 10)"
399
+ )
400
+
401
+ # 2. Check 'last' vs 'first'
402
+ if last is not None and last <= first:
403
+ raise ValueError(
404
+ "Bad usage of 'every': 'last' must be greater than 'first'. "
405
+ f"Got first={first}, last={last}"
406
+ )
407
+
408
+ # 3. Check delta
409
+ if delta is not None and delta <= 0:
410
+ raise ValueError(f"Bad usage of 'every': delta must be positive. Got delta={delta}")
411
+
412
+ return When(_EverySpec(identifier, delta, first, last))
413
+
414
+
415
+ # A singleton instance for the 'never' specification
416
+ never = When(_NeverSpec())