Skip to content

Commit 42e489a

Browse files
authored
Feat/add constants (#110)
* exposing constant modification builds * tmp hotfix * add idmi for streamlit * add simulation hisotry * update constant mapping * small CI updates
1 parent 4444fdf commit 42e489a

File tree

15 files changed

+770
-122
lines changed

15 files changed

+770
-122
lines changed

.github/workflows/cibuildwheel.yml

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,6 @@
11
name: Build wheels with cibuildwheel
22

33
on:
4-
push:
5-
branches: [master]
6-
paths:
7-
- '**.cpp'
8-
- '**.hpp'
9-
- '**.py'
10-
- 'setup.py'
11-
- 'setup.cfg'
12-
- 'pyproject.toml'
13-
- '**.yml'
14-
154
pull_request:
165
types: [opened, synchronize, reopened]
176
branches: [master]
@@ -84,7 +73,7 @@ jobs:
8473
run: python -m cibuildwheel --output-dir wheelhouse
8574
env:
8675
# Build for Python 3.9, 3.10, 3.11, 3.12
87-
CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-* cp313-*"
76+
CIBW_BUILD: "cp311-* cp312-* cp313-*"
8877
# Skip PyPy builds and 32-bit builds
8978
CIBW_SKIP: "pp* *-win32 *-manylinux_i686"
9079
# Install build dependencies

cmtj/models/general_sb.py

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from numba import njit
1212
from tqdm import tqdm
1313

14-
from ..utils import VectorObj, gamma, gamma_rad, mu0, perturb_position
14+
from ..utils import Constants, VectorObj, perturb_position
1515
from .analytical_utils import (
1616
EPS,
1717
OMEGA,
@@ -309,14 +309,14 @@ def no_interaction_symbolic_energy(self, H: sym.ImmutableMatrix):
309309

310310
alpha = sym.ImmutableMatrix([sym.cos(self.Kv.phi), sym.sin(self.Kv.phi), 0])
311311

312-
field_energy = -mu0 * self.Ms * m.dot(H)
313-
hdmi_energy = -mu0 * self.Ms * m.dot(self.Hdmi)
312+
field_energy = -Constants.mu0() * self.Ms * m.dot(H)
313+
hdmi_energy = -Constants.mu0() * self.Ms * m.dot(self.Hdmi)
314314
# old surface anisotropy only took into account the thin slab demag
315315
# surface_anistropy = (-self.Ks + (1.0 / 2.0) * mu0 * self.Ms**2) * (m[-1] ** 2)
316316
surface_anistropy = -self.Ks * (m[-1] ** 2)
317317
volume_anisotropy = -self.Kv.mag * (m.dot(alpha) ** 2)
318318
m_2 = sym.ImmutableMatrix([m_i**2 for m_i in m])
319-
demagnetisation_energy = 0.5 * mu0 * (self.Ms**2) * m_2.dot(self.Ndemag)
319+
demagnetisation_energy = 0.5 * Constants.mu0() * (self.Ms**2) * m_2.dot(self.Ndemag)
320320

321321
return field_energy + surface_anistropy + volume_anisotropy + hdmi_energy + demagnetisation_energy
322322

@@ -329,7 +329,7 @@ def sb_correction(self):
329329
330330
Just remember NOT to divide by 2pi when returning the roots!
331331
"""
332-
return (OMEGA / gamma) * self.Ms * sym.sin(self.theta) * self.thickness
332+
return (OMEGA / Constants.gamma()) * self.Ms * sym.sin(self.theta) * self.thickness
333333

334334
def __hash__(self) -> int:
335335
return hash(str(self))
@@ -370,7 +370,7 @@ def rhs_spherical_llg(
370370
:param U: energy expression of the layer
371371
"""
372372
# sum all components
373-
prefac = gamma_rad / (1.0 + self.alpha**2)
373+
prefac = Constants.gamma_rad() / (1.0 + self.alpha**2)
374374
inv_sin = 1.0 / (sym.sin(self.theta) + EPS)
375375
dUdtheta = sym.diff(U, self.theta)
376376
dUdphi = sym.diff(U, self.phi)
@@ -493,7 +493,7 @@ def compose_llg_jacobian(self, H, form: Literal["energy", "field"] = "energy"):
493493

494494
symbols, vecs = [], []
495495
U = self.create_energy(H=H, volumetric=False) # energy per area
496-
# mu0, gamma_rad = sym.Symbol(r"\mu_0"), sym.Symbol(r"\gamma")
496+
# mu0, Constants.gamma_rad() = sym.Symbol(r"\mu_0"), sym.Symbol(r"\gamma")
497497
for layer in self.layers:
498498
if form == "energy":
499499
symbols.extend((layer.theta, layer.phi))
@@ -502,17 +502,22 @@ def compose_llg_jacobian(self, H, form: Literal["energy", "field"] = "energy"):
502502
m = layer.get_m_sym() # (x,y,z) 3×1
503503
symbols.extend((layer.x, layer.y, layer.z))
504504
# e_i = E/(μ0 Ms_i t_i) in field units
505-
e_i = U / (mu0 * layer.thickness * layer.Ms)
505+
e_i = U / (Constants.mu0() * layer.thickness * layer.Ms)
506506
H_eff_i = self._heff_per_m(e_i, m) # 3×1
507-
expr = -mu0 * gamma_rad * m.cross(H_eff_i) # 3×1: F_i(m)
507+
expr = -Constants.mu0() * Constants.gamma_rad() * m.cross(H_eff_i) # 3×1: F_i(m)
508508
vecs.append(expr)
509509

510510
F = sym.Matrix.vstack(*vecs) # 3N × 1
511511
J = F.jacobian(symbols) # 3N × 3N
512512
return J, symbols
513513

514514
@coordinate(require="cartesian")
515-
def linearised_frequencies(self, H, linearisation_axis: Literal["x", "y", "z"]):
515+
def linearised_frequencies(
516+
self,
517+
H,
518+
linearisation_axis: Literal["x", "y", "z"],
519+
configuration: Union[list[float], None] = None,
520+
):
516521
J, symbols = self.compose_llg_jacobian(H=H, form="field")
517522

518523
# partition symbols by axis and indices by axis
@@ -526,19 +531,27 @@ def linearised_frequencies(self, H, linearisation_axis: Literal["x", "y", "z"]):
526531
subs_zero = {s: 0 for a, syms in by_axis_syms.items() if a != hold for s in syms}
527532
J0 = J.subs(subs_zero)
528533

529-
if hold == "z":
530-
P_vals = {by_axis_syms["z"][i]: 1 for i in range(n)}
531-
AP_vals = {by_axis_syms["z"][i]: (1 if i % 2 == 0 else -1) for i in range(n)}
532-
drop = by_axis_idx["z"]
533-
elif hold == "x":
534-
P_vals = {by_axis_syms["x"][i]: 1 for i in range(n)}
535-
AP_vals = {by_axis_syms["x"][i]: (1 if i % 2 == 0 else -1) for i in range(n)}
536-
drop = by_axis_idx["x"]
537-
else: # hold == "y"
538-
P_vals = {by_axis_syms["y"][i]: 1 for i in range(n)}
539-
AP_vals = {by_axis_syms["y"][i]: (1 if i % 2 == 0 else -1) for i in range(n)}
540-
drop = by_axis_idx["y"]
541-
534+
if configuration is None:
535+
if hold == "z":
536+
P_vals = {by_axis_syms["z"][i]: 1 for i in range(n)}
537+
AP_vals = {by_axis_syms["z"][i]: (1 if i % 2 == 0 else -1) for i in range(n)}
538+
drop = by_axis_idx["z"]
539+
elif hold == "x":
540+
P_vals = {by_axis_syms["x"][i]: 1 for i in range(n)}
541+
AP_vals = {by_axis_syms["x"][i]: (1 if i % 2 == 0 else -1) for i in range(n)}
542+
drop = by_axis_idx["x"]
543+
else: # hold == "y"
544+
P_vals = {by_axis_syms["y"][i]: 1 for i in range(n)}
545+
AP_vals = {by_axis_syms["y"][i]: (1 if i % 2 == 0 else -1) for i in range(n)}
546+
drop = by_axis_idx["y"]
547+
548+
else:
549+
assert len(configuration) == n, f"Incorrect configuration size. Given: {len(configuration)}, expected: {n}"
550+
P_vals = {by_axis_syms[linearisation_axis][i]: configuration[i] for i in range(n)}
551+
AP_vals = {
552+
by_axis_syms[linearisation_axis][i]: (1 if i % 2 == 0 else -1) * configuration[i] for i in range(n)
553+
}
554+
drop = by_axis_idx[linearisation_axis]
542555
J0_P = J0.subs(P_vals)
543556
J0_AP = J0.subs(AP_vals)
544557

@@ -590,14 +603,14 @@ def create_energy(
590603
mat = self.dipoleMatrix[i]
591604
# is positive, just like demag
592605
energy += (
593-
(mu0 / 2.0)
606+
(Constants.mu0() / 2.0)
594607
* l1m.dot(mat * l2m)
595608
* self.layers[i].Ms
596609
* self.layers[i + 1].Ms
597610
* self.layers[i].thickness
598611
)
599612
energy += (
600-
(mu0 / 2.0)
613+
(Constants.mu0() / 2.0)
601614
* l2m.dot(mat * l1m)
602615
* self.layers[i].Ms
603616
* self.layers[i + 1].Ms
@@ -757,11 +770,16 @@ def single_layer_resonance(self, layer_indx: int, eq_position: np.ndarray):
757770
vareps = 1e-18
758771

759772
fmr = (d2Edtheta2 * d2Edphi2 - d2Edthetaphi**2) / np.power(np.sin(theta_eq + vareps) * layer.Ms, 2)
760-
fmr = np.sqrt(float(fmr)) * gamma_rad / (2 * np.pi)
773+
fmr = np.sqrt(float(fmr)) * Constants.gamma_rad() / (2 * np.pi)
761774
return fmr
762775

763776
@coordinate(require="cartesian")
764-
def solve_linearised_frequencies(self, H: VectorObj, linearisation_axis: Literal["x", "y", "z"]):
777+
def solve_linearised_frequencies(
778+
self,
779+
H: VectorObj,
780+
linearisation_axis: Literal["x", "y", "z"],
781+
configuration: Union[list[float], None] = None,
782+
):
765783
"""Solves the linearised frequencies of the system.
766784
Select linearisation axis and solve characteristic equation to get the frequencies.
767785
Requires the system to be in cartesian coordinates.
@@ -772,9 +790,14 @@ def solve_linearised_frequencies(self, H: VectorObj, linearisation_axis: Literal
772790
773791
:param H: the magnetic field.
774792
:param linearisation_axis: the axis to linearise around.
793+
:param configuration: the configuration of the layers along the linearisation axis. Optional.
794+
Defaults to P and AP (alternating).
775795
:return: the solutions for the frequencies in the P and AP states.
776796
"""
777-
char_P, char_AP, _, _ = self.linearised_frequencies(H=H, linearisation_axis=linearisation_axis)
797+
798+
char_P, char_AP, _, _ = self.linearised_frequencies(
799+
H=H, linearisation_axis=linearisation_axis, configuration=configuration
800+
)
778801
poly_P = self.det_solver(char_P)
779802
poly_AP = self.det_solver(char_AP)
780803
roots_P = self.root_solver(poly_P, n_layers=len(self.layers), normalise_roots_by_2pi=True)
@@ -974,7 +997,7 @@ def analytical_field_scan(
974997
step_subs.update({Hsym[0]: hx, Hsym[1]: hy, Hsym[2]: hz})
975998
roots = [s.subs(step_subs) for s in global_roots]
976999
# TODO fix scaling by gamma below
977-
roots = np.asarray(roots, dtype=np.float32) * gamma_rad / (2.0 * np.pi) / 1e9
1000+
roots = np.asarray(roots, dtype=np.float32) * Constants.gamma_rad() / (2.0 * np.pi) / 1e9
9781001
yield eq, roots, Hvalue
9791002
current_position = eq
9801003

cmtj/utils/__init__.py

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
1-
import math
2-
1+
# Import constants from the unified constants module
2+
from .constants import (
3+
AmtoOe,
4+
AmtoT,
5+
Constants,
6+
OetoAm,
7+
TtoAm,
8+
bohr_magneton,
9+
boltzmann,
10+
echarge,
11+
gamma,
12+
gamma_rad,
13+
gyromagnetic_ratio,
14+
hbar,
15+
hplanck,
16+
me,
17+
mu0,
18+
)
319
from .filters import Filters
420
from .general import VectorObj, box_muller_random, perturb_position
521
from .linear import FieldScan
@@ -12,21 +28,6 @@
1228
compute_sd,
1329
)
1430

15-
# constants
16-
OetoAm = 79.57747
17-
AmtoOe = 1.0 / OetoAm
18-
TtoAm = 795774.715459
19-
AmtoT = 1.0 / TtoAm
20-
echarge = -1.602e-19
21-
mu0 = 12.566e-7
22-
hplanck = 6.6260e-34
23-
hbar = hplanck / (2 * math.pi)
24-
gyromagnetic_ratio = 2.211e5 # m/As
25-
gamma = 28024e6 # Hz/T
26-
gamma_rad = 1.76e11 # rad / (s * T)
27-
me = 9.109e-31
28-
bohr_magneton = echarge * hbar / (2 * me)
29-
3031
__all__ = [
3132
"Filters",
3233
"FieldScan",
@@ -39,4 +40,20 @@
3940
"box_muller_random",
4041
"perturb_position",
4142
"compute_gmr",
43+
# Constants
44+
"Constants",
45+
"mu0",
46+
"gyromagnetic_ratio",
47+
"gamma",
48+
"gamma_rad",
49+
"TtoAm",
50+
"AmtoT",
51+
"OetoAm",
52+
"AmtoOe",
53+
"hplanck",
54+
"hbar",
55+
"echarge",
56+
"me",
57+
"boltzmann",
58+
"bohr_magneton",
4259
]

0 commit comments

Comments
 (0)