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
@@ -0,0 +1,180 @@
1
+ """Schedule parsing and counter arithmetic for hysteresis runs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from collections.abc import Callable
7
+ from typing import Any, cast
8
+
9
+ from when import When
10
+
11
+ from .clock import SimulationClock
12
+
13
+ Action = Callable[[Any], Any]
14
+ ScheduleInput = list[tuple[object, ...]]
15
+ ScheduledAction = tuple[Action, When]
16
+ CounterTriple = tuple[Any | None, Any | None, Any | None]
17
+
18
+ _subsequent_spaces = re.compile(r"[ \t_]+")
19
+
20
+
21
+ def _string_normalise(s: str, lower: bool = True, spaces: str | None = "_") -> str:
22
+ """
23
+ Used by `_append_x_list` to obtain a standard string format.
24
+ Examples:
25
+ 'save fields' --> 'save_fields'
26
+ 'Save_Fields' --> 'save_fields'
27
+ """
28
+ ns = s
29
+ if lower:
30
+ ns = s.lower()
31
+ if spaces is not None:
32
+ ns = re.sub(_subsequent_spaces, spaces, ns)
33
+ return ns
34
+
35
+
36
+ def _append_x_list(
37
+ target_list: list[ScheduledAction],
38
+ input_list: ScheduleInput,
39
+ prefix: str = "",
40
+ predefined_actions: dict[str, Action] | None = None,
41
+ ) -> None:
42
+ """
43
+ Internally used by `_join_save_and_do_lists` to parse the
44
+ multi-argument 'save' and 'do' tuples.
45
+ """
46
+ if predefined_actions is None:
47
+ predefined_actions = {}
48
+
49
+ try:
50
+ for tuple_item in input_list:
51
+ list_item = list(tuple_item)
52
+ if not list_item:
53
+ continue
54
+
55
+ things = list_item[0:-1]
56
+ when = list_item[-1]
57
+
58
+ for thing in things:
59
+ if callable(thing):
60
+ target_list.append((cast(Action, thing), cast(When, when)))
61
+ continue
62
+
63
+ if isinstance(thing, str):
64
+ normalised_thing = _string_normalise(f"{prefix} {thing}")
65
+ if normalised_thing in predefined_actions:
66
+ action = predefined_actions[normalised_thing]
67
+ target_list.append((action, cast(When, when)))
68
+ continue
69
+ else:
70
+ # Try without prefix
71
+ normalised_thing = _string_normalise(thing)
72
+ if normalised_thing in predefined_actions:
73
+ action = predefined_actions[normalised_thing]
74
+ target_list.append((action, cast(When, when)))
75
+ continue
76
+
77
+ # If we are here, the 'thing' is not a callable and not
78
+ # a recognized string.
79
+ msg = (
80
+ f"Error in optional argument '{prefix}' of method "
81
+ f"'hysteresis': you want to {prefix} '{thing}' "
82
+ f"but I don't know how to do it. Hint: when specifying "
83
+ f"what to {prefix} you can use a string or a function. "
84
+ f"Available strings are: {', '.join(predefined_actions.keys())}."
85
+ )
86
+ raise ValueError(msg)
87
+
88
+ except TypeError as e:
89
+ msg = (
90
+ f"Bad syntax for argument '{prefix}' of the method 'hysteresis': "
91
+ f"remember that you should provide a list of tuples "
92
+ f"(things_to_{prefix}, when). You can also provide tuples "
93
+ f"with many things to save, such as (thing1, thing2, "
94
+ f"..., when)."
95
+ )
96
+ raise ValueError(msg) from e
97
+
98
+
99
+ def _join_save_and_do_lists(
100
+ save_list: ScheduleInput,
101
+ do_list: ScheduleInput,
102
+ predefined_actions: dict[str, Action] | None = None,
103
+ ) -> list[ScheduledAction]:
104
+ """
105
+ Takes 'save' and 'do' parameters from the 'hysteresis' method
106
+ and joins them into a unique list.
107
+ Tags (e.g., "save averages") are replaced with the appropriate functions.
108
+ Also checks that the provided tags actually exist.
109
+ """
110
+ if predefined_actions is None:
111
+ predefined_actions = {}
112
+
113
+ joint_list: list[ScheduledAction] = []
114
+
115
+ # Note: It is important to process 'do' before 'save'.
116
+ # A 'do' command might be 'next_stage', and 'save' commands
117
+ # need to know if this is the last step of the stage.
118
+ _append_x_list(joint_list, do_list, prefix="do", predefined_actions=predefined_actions)
119
+ _append_x_list(joint_list, save_list, prefix="save", predefined_actions=predefined_actions)
120
+
121
+ # XXX Matteo, can we at this point order the save_ entries such that
122
+ # the save_restart is the last? See my explanation in ticket:169.
123
+ # Hans, 11/ll/2008
124
+
125
+ return joint_list
126
+
127
+
128
+ def _next_deltas(
129
+ event: When,
130
+ clock: SimulationClock,
131
+ suggest: CounterTriple | None = None,
132
+ tols: dict[str, Any] | None = None,
133
+ ) -> CounterTriple:
134
+ """
135
+ Return the next occurrence of the given event as a triple of deltas:
136
+ - (delta_step, delta_time, delta_real_time)
137
+ """
138
+
139
+ def delta(name: str) -> Any | None:
140
+ n = event.next_time(name, clock, tols=tols)
141
+ return n - clock[name] if not isinstance(n, bool) else None
142
+
143
+ def minimum(ls: list[Any | None]) -> Any | None:
144
+ return min((item for item in ls if item is not None), default=None)
145
+
146
+ delta_step, delta_time, delta_real_time = suggest or (None, None, None)
147
+
148
+ delta_step = minimum([delta("step"), delta("stage_step"), delta_step])
149
+ delta_time = minimum([delta("time"), delta("stage_time"), delta_time])
150
+ delta_real_time = minimum([delta("real_time"), delta_real_time])
151
+
152
+ return (delta_step, delta_time, delta_real_time)
153
+
154
+
155
+ def _next_time(
156
+ event: When,
157
+ clock: SimulationClock,
158
+ tols: dict[str, Any] | None = None,
159
+ ) -> CounterTriple:
160
+ """
161
+ Returns a triple of absolute counter values for the next event:
162
+ - (next_step, next_time, next_real_time)
163
+ """
164
+
165
+ def delta(name: str, starting_time: str | None = None) -> Any | None:
166
+ n = event.next_time(name, clock, tols=tols)
167
+ if isinstance(n, bool):
168
+ return None
169
+ return n - clock[starting_time] if starting_time else n
170
+
171
+ def minimum(ls: list[Any | None]) -> Any | None:
172
+ return min((item for item in ls if item is not None), default=None)
173
+
174
+ next_time = minimum([delta("time", "zero_stage_time"), delta("stage_time")])
175
+
176
+ next_step = minimum([delta("step", "zero_stage_step"), delta("stage_step")])
177
+
178
+ next_real_time = delta("real_time")
179
+
180
+ return (next_step, next_time, next_real_time)
@@ -0,0 +1,3 @@
1
+ from .inference import InferenceEngine, InferenceEntity
2
+
3
+ __all__ = ["InferenceEngine", "InferenceEntity"]
@@ -0,0 +1,95 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from collections.abc import Callable
5
+ from dataclasses import dataclass, field
6
+ from typing import Any
7
+
8
+ log = logging.getLogger("nmagmake")
9
+
10
+
11
+ @dataclass
12
+ class InferenceEntity:
13
+ name: str
14
+ depends_on: list[str] = field(default_factory=lambda: [])
15
+ how_to_make: list[Callable[..., Any]] = field(default_factory=lambda: [])
16
+ also_updates: list[str] = field(default_factory=lambda: [])
17
+ _is_prerequisite: list[str] = field(default_factory=lambda: [], init=False)
18
+ _is_uptodate: bool = field(default=False, init=False)
19
+
20
+
21
+ class InferenceEngine:
22
+ def __init__(self, entities: list[dict[str, Any]] | None = None) -> None:
23
+ self.entities: dict[str, InferenceEntity] = {}
24
+ if entities:
25
+ for e_desc in entities:
26
+ ie = InferenceEntity(**e_desc)
27
+ self.entities[ie.name] = ie
28
+
29
+ self._build_backlinks()
30
+ self._check_for_cycles()
31
+
32
+ def _build_backlinks(self) -> None:
33
+ """Connects dependencies to their dependents and validates existence."""
34
+ for name, ie in self.entities.items():
35
+ for dep in ie.depends_on:
36
+ if (ie_dep := self.entities.get(dep)) is None:
37
+ raise KeyError(f"Dependency '{dep}' required by '{name}' not found.")
38
+ ie_dep._is_prerequisite.append(name)
39
+
40
+ def _check_for_cycles(self) -> None:
41
+ """Detects circular dependencies using DFS to prevent infinite recursion."""
42
+ visited: set[str] = set()
43
+ rec_stack: set[str] = set()
44
+
45
+ def has_cycle(name: str) -> bool:
46
+ visited.add(name)
47
+ rec_stack.add(name)
48
+
49
+ for neighbor in self.entities[name].depends_on:
50
+ if neighbor not in visited:
51
+ if has_cycle(neighbor):
52
+ return True
53
+ elif neighbor in rec_stack:
54
+ return True
55
+
56
+ rec_stack.remove(name)
57
+ return False
58
+
59
+ for node in self.entities:
60
+ if node not in visited:
61
+ if has_cycle(node):
62
+ raise ValueError(f"Circular dependency detected involving '{node}'")
63
+
64
+ def invalidate(self, name: str) -> None:
65
+ """Recursively marks an entity and its dependents as dirty."""
66
+ if (ie := self.entities.get(name)) is None:
67
+ raise KeyError(f"Entity '{name}' is unknown.")
68
+
69
+ if ie._is_uptodate:
70
+ ie._is_uptodate = False
71
+ log.debug(f"Invalidated: {name}")
72
+ for dependent_name in ie._is_prerequisite:
73
+ self.invalidate(dependent_name)
74
+
75
+ def make(self, name: str, **make_args: Any) -> None:
76
+ """Builds target and dependencies recursively."""
77
+ if (ie := self.entities.get(name)) is None:
78
+ raise KeyError(f"Cannot build unknown entity '{name}'.")
79
+
80
+ if ie._is_uptodate:
81
+ log.debug(f"'{name}' is up-to-date.")
82
+ return
83
+
84
+ for dep_name in ie.depends_on:
85
+ self.make(dep_name, **make_args)
86
+
87
+ log.info(f"Building: {name}")
88
+ for step in ie.how_to_make:
89
+ step(**make_args)
90
+
91
+ for also_name in ie.also_updates:
92
+ if other := self.entities.get(also_name):
93
+ other._is_uptodate = True
94
+
95
+ ie._is_uptodate = True
simulation/py.typed ADDED
@@ -0,0 +1 @@
1
+ # Marker for PEP 561 inline type information.
simulation/quantity.py ADDED
@@ -0,0 +1,88 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+
4
+ from si.physical import SI
5
+
6
+
7
+ @dataclass(frozen=True)
8
+ class Quantity:
9
+ """A dataclass describing a physical quantity for data saving.
10
+
11
+ A quantity is a description of any thing that may be saved inside
12
+ a ndt file. It may just represent an integer-floating point number
13
+ or a vector field. The Quantity class is a descriptor for such a thing.
14
+ It contains details about the corresponding type, SI unit, the id name,
15
+ the signature (indices and per-material).
16
+ """
17
+
18
+ name: str
19
+ type: str # 'int', 'float', 'field', 'pfield', 'date'
20
+ units: SI | None
21
+ signature: str | None = None
22
+ context: str | None = None
23
+ parent: Optional["Quantity"] = None
24
+
25
+ def __post_init__(self) -> None:
26
+ if self.signature is None:
27
+ object.__setattr__(self, "signature", self.name)
28
+
29
+ def sub_quantity(self, name: str) -> "Quantity":
30
+ """Creates a new Quantity representing a sub-component."""
31
+ return self.__class__(
32
+ name=name,
33
+ type=self.type,
34
+ units=self.units,
35
+ signature=self.signature,
36
+ context=self.context,
37
+ parent=self,
38
+ )
39
+
40
+
41
+ UNIT_DIMENSIONLESS = SI(1)
42
+ UNIT_SECONDS = SI("s")
43
+ UNIT_A_PER_M = SI("A/m")
44
+ UNIT_A_PER_M2 = SI("A/m^2")
45
+ UNIT_A_PER_M_PER_S = SI("A/m/s")
46
+ UNIT_INVERSE_SECONDS = SI("1/s")
47
+ UNIT_J_PER_M3 = SI("J/m^3")
48
+ UNIT_AMPERE = SI("A")
49
+
50
+
51
+ known_quantities: list[Quantity] = [
52
+ # name type unit signature context
53
+ Quantity("id", "int", UNIT_DIMENSIONLESS, None),
54
+ Quantity("step", "int", UNIT_DIMENSIONLESS, None),
55
+ Quantity("stage_step", "int", UNIT_DIMENSIONLESS, None),
56
+ Quantity("stage", "int", UNIT_DIMENSIONLESS, None),
57
+ Quantity("last_step_dt", "float", UNIT_SECONDS, None),
58
+ Quantity("time", "float", UNIT_SECONDS, None),
59
+ Quantity("stage_time", "float", UNIT_SECONDS, None),
60
+ Quantity("real_time", "float", UNIT_SECONDS, None),
61
+ Quantity("unixtime", "float", UNIT_SECONDS, None),
62
+ Quantity("maxangle", "float", UNIT_DIMENSIONLESS, None),
63
+ Quantity("localtime", "date", None, None),
64
+ Quantity("H_total", "field", UNIT_A_PER_M, "_?_*"),
65
+ Quantity("M", "field", UNIT_A_PER_M, "_?_*"),
66
+ Quantity("m", "pfield", UNIT_DIMENSIONLESS, "_?_*"),
67
+ Quantity("pin", "pfield", UNIT_DIMENSIONLESS, None),
68
+ Quantity("current_density", "pfield", UNIT_A_PER_M2, "_*", "stt"),
69
+ Quantity("dmdt", "field", UNIT_A_PER_M_PER_S, "_?_*"),
70
+ Quantity("dm_dcurrent", "field", UNIT_INVERSE_SECONDS, "_?_*", "stt"),
71
+ Quantity("H_ext", "field", UNIT_A_PER_M, "_*"),
72
+ Quantity("H_anis", "field", UNIT_A_PER_M, "_?_*", "anis"),
73
+ Quantity("H_exch", "field", UNIT_A_PER_M, "_?_*", "exch"),
74
+ Quantity("H_demag", "field", UNIT_A_PER_M, "_*", "demag"),
75
+ Quantity("E_total", "field", UNIT_J_PER_M3, "_?"),
76
+ Quantity("E_ext", "field", UNIT_J_PER_M3, "_?"),
77
+ Quantity("E_anis", "field", UNIT_J_PER_M3, "_?", "anis"),
78
+ Quantity("E_exch", "field", UNIT_J_PER_M3, "_?", "exch"),
79
+ Quantity("E_demag", "field", UNIT_J_PER_M3, "_?", "demag"),
80
+ Quantity("phi", "field", UNIT_AMPERE, None, "demag"),
81
+ Quantity("rho", "field", UNIT_A_PER_M2, None, "demag"),
82
+ ]
83
+
84
+ known_quantities_by_name: dict[str, Quantity] = {q.name: q for q in known_quantities}
85
+
86
+ known_field_quantities: list[Quantity] = [
87
+ q for q in known_quantities if q.type in ("field", "pfield")
88
+ ]