1111from numba import njit
1212from tqdm import tqdm
1313
14- from ..utils import VectorObj , gamma , gamma_rad , mu0 , perturb_position
14+ from ..utils import Constants , VectorObj , perturb_position
1515from .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
0 commit comments