-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Method to set the initial magnetization to equilibrium value prior to excitation #139
Comments
We need to allow the direct import of a given magnetization profile so we need to slightly modify the
|
I solved the problem, at least for the case where only one magnetic layer exists. I did this by defining
delays_pre is an array for defining the time before time zero and layer size is the number of unit cells in the single magnetic layer. Then strain_pre and temp_pre are initialized. By using calc_magnetization_map, the magnetization dynamic is calculated. The last entries in the calculated magnetization_map are returned, as this is the equilibrium magnetization. Here is an example how I used it:
As one can see, the calculated initial_magnetization is the equilibrium magnetization since there is nothing happening before time zero. |
Agree something along this line is what I have in mind. The more I look into it, the harder it is to provide a generalized function that allows to reach the equilibrium state prior to excitation. Maybe it is best that we write a function that works in the scenarios we are using with the LLG equation in this branch and add it into this class. With the LLB model I still receive error messages when I try to insert an artificially generated homogeneous temp-map. I was thinking to make a method out of this code snipped, which work for the LLG example but somehow fail for the LLB example:
however, if I try it with the LLB I get errors that I am still trying to understand. |
If I understand you correctly, your Idea is to implement something that checks whether the magnetization is stable, and then returns the magnetization. So it's not just calculating 10000ps but rather stops after i.e. 2000 since the magnetization is stable? |
Yes, I would like to have a method that runs the equilibration N times in steps of 1ps and at the end of each run checks if the magnetization is stable (i.e. all changes are below a user defined threshold). The user sets the threshold, and the number N of iterations until the magnetization is stable. Just to avoid that the precession doesn't cease after a sensible amount of time because the damping is too low. The method should print the coordinates of the average A, phi and gamma after each iteration just to see for the user if the magnetization is stable. |
Hi @AvonReppert & @friweber many thanks for your inputs!
This is of course a first step and I have not thought about that in the beginning - but it should be very easy to implement.
Could you please post some of them in order to get some insights?
I guess we can do that already within the There one can define an At the same time one should check the @Nilodirf also suggested temporally increasing the damping parameters of the sample stack to faster converge to the equilibrium state. |
Well essentially I wonder why the following minimal "example" case does not run in the LLB class. I wanted to provide a homogenous temperature map initialized at 300K directly, but it does not work but rather runs into mathematical errors. import matplotlib.pyplot as plt
import numpy as np
import udkm1Dsim as ud
u = ud.u # import the pint unit registry from udkm1Dsim
u.setup_matplotlib() # use matplotlib with pint units
# %% Layer properties
# Loading atoms
Ni = ud.Atom('Ni', mag_amplitude=1, mag_gamma=90*u.deg, mag_phi=0*u.deg)
Si = ud.Atom('Si')
prop_Ni = {}
# parameters for a two-temperture model
prop_Ni['heat_capacity'] = ['0.1*T', 532*u.J/u.kg/u.K, ]
prop_Ni['therm_cond'] = [20*u.W/(u.m*u.K), 80*u.W/(u.m*u.K), ]
g = 4.0e18 # electron-phonon coupling
prop_Ni['sub_system_coupling'] = \
['-{:f}*(T_0-T_1)'.format(g),
'{:f}*(T_0-T_1)'.format(g)
]
prop_Ni['lin_therm_exp'] = [0, 11.8e-6]
prop_Ni['opt_ref_index'] = 2.9174+3.3545j
# LLB parameters
prop_Ni['eff_spin'] = 0.5
prop_Ni['curie_temp'] = 630*u.K
prop_Ni['lamda'] = 0.5
prop_Ni['mag_moment'] = 0.393*u.bohr_magneton
prop_Ni['aniso_exponent'] = 3
prop_Ni['anisotropy'] = [0.45e6, 0.45e6, 0.45e6]*u.J/u.m**3
prop_Ni['exch_stiffness'] = [0.1e-15, 1e-15, 0.01e-15]*u.J/u.m
prop_Ni['mag_saturation'] = 500e3*u.J/u.T/u.m**3
# build the layer
layer_Ni = ud.AmorphousLayer('Ni', 'Ni amorphous', thickness=1*u.nm, density=7000*u.kg/u.m**3, atom=Ni, **prop_Ni)
prop_Si = {}
prop_Si['heat_capacity'] = [100*u.J/u.kg/u.K, 603*u.J/u.kg/u.K]
prop_Si['therm_cond'] = [0, 100*u.W/(u.m*u.K)]
prop_Si['sub_system_coupling'] = [0, 0]
prop_Si['lin_therm_exp'] = [0, 2.6e-6]
prop_Si['sound_vel'] = 8.433*u.nm/u.ps
prop_Si['opt_ref_index'] = 3.6941+0.0065435j
layer_Si = ud.AmorphousLayer('Si', "Si amorphous", thickness=1*u.nm, density=2336*u.kg/u.m**3,
atom=Si, **prop_Si)
# %% Building the structure
S = ud.Structure('Ni')
S.add_sub_structure(layer_Ni, 20)
S.add_sub_structure(layer_Si, 200)
S.visualize()
# %% Heat simulation without excitation
init_temp = 300 # K
fluence = 0 # mJ/cm^2
# temporal and spatial grid
delays = np.r_[-100:200:1]*u.ps
_, _, distances = S.get_distances_of_layers()
h = ud.Heat(S, force_recalc=False)
h.save_data = True
h.disp_messages = True
h.excitation = {'fluence': [fluence]*u.mJ/u.cm**2,
'delay_pump': [0]*u.ps,
'pulse_width': [0]*u.ps,
'multilayer_absorption': True,
'wavelength': 800*u.nm,
'theta': 45*u.deg}
h.heat_diffusion = True
h.boundary_conditions = {'top_type': 'isolator', 'bottom_type': 'isolator'}
temp_map, delta_temp = h.get_temp_map(delays, init_temp)
# %% LLB simulation without excitation
H_ext = np.array([1, 0, 1])
delays_equilibrium = np.r_[-100:200:1]*u.ps
temp_map_equilibrium = np.full((len(delays_equilibrium), len(distances), 2), init_temp)
is_magnetic = S.get_layer_property_vector('_curie_temp') > 0
llb = ud.LLB(S, True)
llb.save_data = True
llb.disp_messages = True
mean_field_mag_map_1 = llb.get_mean_field_mag_map(temp_map_equilibrium[:, :, 0])
init_mag_1 = np.array([1, (0.*u.deg).to('rad').magnitude, (0*u.deg).to('rad').magnitude])
magnetization_map_1 = llb.get_magnetization_map(
delays_equilibrium, temp_map=temp_map_equilibrium, init_mag=init_mag_1, H_ext=H_ext)
magnetization_map_xyz_1 = ud.LLB.convert_polar_to_cartesian(magnetization_map_1)
plt.figure(figsize=[6, 8])
plt.subplot(2, 1, 1)
plt.plot(delays, np.mean(magnetization_map_xyz_1[:, is_magnetic, 2], axis=1), label=r'$M_z$')
plt.legend()
plt.xlabel('Delay (ps)')
plt.ylabel('Magnetization')
plt.subplot(2, 1, 2)
plt.plot(delays, np.mean(magnetization_map_xyz_1[:, is_magnetic, 0], axis=1), label=r'$M_x$')
plt.plot(delays, np.mean(magnetization_map_xyz_1[:, is_magnetic, 1], axis=1), label=r'$M_y$')
plt.legend()
plt.xlabel('Delay (ps)')
plt.ylabel('Magnetization')
plt.show()
# %% Reduce the damping by a factor 1000
prop_Ni['lamda'] = 0.0005
layer_Ni = ud.AmorphousLayer('Ni', 'Ni amorphous', thickness=1*u.nm, density=7000*u.kg/u.m**3, atom=Ni, **prop_Ni)
S = ud.Structure('Ni')
S.add_sub_structure(layer_Ni, 20)
S.add_sub_structure(layer_Si, 200)
# %% Heat simulation with excitation
init_temp = 300 # K
fluence = 15 # mJ/cm^2
_, _, distances = S.get_distances_of_layers()
h = ud.Heat(S, force_recalc=False)
h.save_data = True
h.disp_messages = True
h.excitation = {'fluence': [fluence]*u.mJ/u.cm**2,
'delay_pump': [0]*u.ps,
'pulse_width': [0.15]*u.ps,
'multilayer_absorption': True,
'wavelength': 800*u.nm,
'theta': 45*u.deg}
# enable heat diffusion
h.heat_diffusion = True
# set the boundary conditions
h.boundary_conditions = {'top_type': 'isolator', 'bottom_type': 'isolator'}
# The resulting temperature profile is calculated in one line:
temp_map_2, delta_temp_2 = h.get_temp_map(delays, init_temp)
# %%
llb = ud.LLB(S, True)
llb.save_data = True
llb.disp_messages = True
mean_field_mag_map_2 = llb.get_mean_field_mag_map(temp_map_2[:, :, 0])
init_mag_2 = magnetization_map_1[-1, :]
magnetization_map_2 = llb.get_magnetization_map(delays, temp_map=temp_map_2, init_mag=init_mag_2, H_ext=H_ext)
magnetization_map_xyz_2 = ud.LLB.convert_polar_to_cartesian(magnetization_map_2)
plt.figure(figsize=[6, 8])
plt.subplot(2, 1, 1)
plt.plot(delays, np.mean(magnetization_map_xyz_2[:, is_magnetic, 2], axis=1), label=r'$M_z$')
plt.legend()
plt.xlabel('Delay (ps)')
plt.ylabel('Magnetization')
plt.subplot(2, 1, 2)
plt.plot(delays, np.mean(magnetization_map_xyz_2[:, is_magnetic, 0], axis=1), label=r'$M_x$')
plt.plot(delays, np.mean(magnetization_map_xyz_2[:, is_magnetic, 1], axis=1), label=r'$M_y$')
plt.legend()
plt.xlabel('Delay (ps)')
plt.ylabel('Magnetization')
plt.show() It yields error messages like this: udkm1Dsim\udkm1Dsim\simulations\magnetization.py:1049: RuntimeWarning: divide by zero encountered in divide
dBdx = 1 / (two_eff_spins**2 * np.sinh(x / (two_eff_spins))**2) \
udkm1Dsim\udkm1Dsim\simulations\magnetization.py:1050: RuntimeWarning: divide by zero encountered in divide
- (two_eff_spins + 1)**2 / \
udkm1Dsim\udkm1Dsim\simulations\magnetization.py:1049: RuntimeWarning: invalid value encountered in subtract
dBdx = 1 / (two_eff_spins**2 * np.sinh(x / (two_eff_spins))**2) \
code\udkm1Dsim\udkm1Dsim\simulations\magnetization.py:1219: RuntimeWarning: invalid value encountered in cast
chi_long[under_tc] = np.divide(
code\udkm1Dsim\udkm1Dsim\simulations\magnetization.py:970: RuntimeWarning: divide by zero encountered in divide
H_th[under_tc] = 1/(2 * chi_long[under_tc]) * (
code\udkm1Dsim\udkm1Dsim\simulations\magnetization.py:971: RuntimeWarning: divide by zero encountered in divide
1 - mag_map_squared[under_tc]/mf_magnetizations[under_tc]**2
code\udkm1Dsim\udkm1Dsim\simulations\magnetization.py:970: RuntimeWarning: invalid value encountered in cast
H_th[under_tc] = 1/(2 * chi_long[under_tc]) * (
code\udkm1Dsim\udkm1Dsim\simulations\magnetization.py:1091: RuntimeWarning: divide by zero encountered in divide
np.divide(lambdas[under_tc], mf_magnetizations[under_tc]), (
code\udkm1Dsim\udkm1Dsim\simulations\magnetization.py:1090: RuntimeWarning: invalid value encountered in cast
alpha_trans[under_tc] = np.multiply( The funny thing is, that a code where I generate the temp_map from an excitation with 0 fluence works although the resulting temp_map is identical. So the following works: import matplotlib.pyplot as plt
import numpy as np
import udkm1Dsim as ud
u = ud.u # import the pint unit registry from udkm1Dsim
u.setup_matplotlib() # use matplotlib with pint units
# %% Layer properties
# Loading atoms
Ni = ud.Atom('Ni', mag_amplitude=1, mag_gamma=90*u.deg, mag_phi=0*u.deg)
Si = ud.Atom('Si')
prop_Ni = {}
# parameters for a two-temperture model
prop_Ni['heat_capacity'] = ['0.1*T', 532*u.J/u.kg/u.K, ]
prop_Ni['therm_cond'] = [20*u.W/(u.m*u.K), 80*u.W/(u.m*u.K), ]
g = 4.0e18 # electron-phonon coupling
prop_Ni['sub_system_coupling'] = \
['-{:f}*(T_0-T_1)'.format(g),
'{:f}*(T_0-T_1)'.format(g)
]
prop_Ni['lin_therm_exp'] = [0, 11.8e-6]
prop_Ni['opt_ref_index'] = 2.9174+3.3545j
# LLB parameters
prop_Ni['eff_spin'] = 0.5
prop_Ni['curie_temp'] = 630*u.K
prop_Ni['lamda'] = 0.5
prop_Ni['mag_moment'] = 0.393*u.bohr_magneton
prop_Ni['aniso_exponent'] = 3
prop_Ni['anisotropy'] = [0.45e6, 0.45e6, 0.45e6]*u.J/u.m**3
prop_Ni['exch_stiffness'] = [0.1e-15, 1e-15, 0.01e-15]*u.J/u.m
prop_Ni['mag_saturation'] = 500e3*u.J/u.T/u.m**3
# build the layer
layer_Ni = ud.AmorphousLayer('Ni', 'Ni amorphous', thickness=1*u.nm, density=7000*u.kg/u.m**3, atom=Ni, **prop_Ni)
prop_Si = {}
prop_Si['heat_capacity'] = [100*u.J/u.kg/u.K, 603*u.J/u.kg/u.K]
prop_Si['therm_cond'] = [0, 100*u.W/(u.m*u.K)]
prop_Si['sub_system_coupling'] = [0, 0]
prop_Si['lin_therm_exp'] = [0, 2.6e-6]
prop_Si['sound_vel'] = 8.433*u.nm/u.ps
prop_Si['opt_ref_index'] = 3.6941+0.0065435j
layer_Si = ud.AmorphousLayer('Si', "Si amorphous", thickness=1*u.nm, density=2336*u.kg/u.m**3,
atom=Si, **prop_Si)
# %% Building the structure
S = ud.Structure('Ni')
S.add_sub_structure(layer_Ni, 20)
S.add_sub_structure(layer_Si, 200)
S.visualize()
# %% Heat simulation without excitation
init_temp = 300 # K
fluence = 0 # mJ/cm^2
# temporal and spatial grid
delays = np.r_[-100:200:1]*u.ps
_, _, distances = S.get_distances_of_layers()
h = ud.Heat(S, force_recalc=False)
h.save_data = True
h.disp_messages = True
h.excitation = {'fluence': [fluence]*u.mJ/u.cm**2,
'delay_pump': [0]*u.ps,
'pulse_width': [0]*u.ps,
'multilayer_absorption': True,
'wavelength': 800*u.nm,
'theta': 45*u.deg}
h.heat_diffusion = True
h.boundary_conditions = {'top_type': 'isolator', 'bottom_type': 'isolator'}
temp_map, delta_temp = h.get_temp_map(delays, init_temp)
# %% LLB simulation without excitation
H_ext = np.array([1, 0, 1])
is_magnetic = S.get_layer_property_vector('_curie_temp') > 0
llb = ud.LLB(S, True)
llb.save_data = True
llb.disp_messages = True
mean_field_mag_map_1 = llb.get_mean_field_mag_map(temp_map[:, :, 0])
init_mag_1 = np.array([1, (0.*u.deg).to('rad').magnitude, (0*u.deg).to('rad').magnitude])
magnetization_map_1 = llb.get_magnetization_map(delays, temp_map=temp_map, init_mag=init_mag_1, H_ext=H_ext)
magnetization_map_xyz_1 = ud.LLB.convert_polar_to_cartesian(magnetization_map_1)
plt.figure(figsize=[6, 8])
plt.subplot(2, 1, 1)
plt.plot(delays, np.mean(magnetization_map_xyz_1[:, is_magnetic, 2], axis=1), label=r'$M_z$')
plt.legend()
plt.xlabel('Delay (ps)')
plt.ylabel('Magnetization')
plt.subplot(2, 1, 2)
plt.plot(delays, np.mean(magnetization_map_xyz_1[:, is_magnetic, 0], axis=1), label=r'$M_x$')
plt.plot(delays, np.mean(magnetization_map_xyz_1[:, is_magnetic, 1], axis=1), label=r'$M_y$')
plt.legend()
plt.xlabel('Delay (ps)')
plt.ylabel('Magnetization')
plt.show()
# %% Reduce the damping by a factor 1000
prop_Ni['lamda'] = 0.0005
layer_Ni = ud.AmorphousLayer('Ni', 'Ni amorphous', thickness=1*u.nm, density=7000*u.kg/u.m**3, atom=Ni, **prop_Ni)
S = ud.Structure('Ni')
S.add_sub_structure(layer_Ni, 20)
S.add_sub_structure(layer_Si, 200)
# %% Heat simulation with excitation
fluence = 15 # mJ/cm^2
_, _, distances = S.get_distances_of_layers()
h = ud.Heat(S, force_recalc=False)
h.save_data = True
h.disp_messages = True
h.excitation = {'fluence': [fluence]*u.mJ/u.cm**2,
'delay_pump': [0]*u.ps,
'pulse_width': [0.15]*u.ps,
'multilayer_absorption': True,
'wavelength': 800*u.nm,
'theta': 45*u.deg}
# enable heat diffusion
h.heat_diffusion = True
# set the boundary conditions
h.boundary_conditions = {'top_type': 'isolator', 'bottom_type': 'isolator'}
# The resulting temperature profile is calculated in one line:
temp_map_2, delta_temp_2 = h.get_temp_map(delays, init_temp)
# %%
llb = ud.LLB(S, True)
llb.save_data = True
llb.disp_messages = True
mean_field_mag_map_2 = llb.get_mean_field_mag_map(temp_map_2[:, :, 0])
init_mag_2 = magnetization_map_1[-1, :]
magnetization_map_2 = llb.get_magnetization_map(delays, temp_map=temp_map_2, init_mag=init_mag_2, H_ext=H_ext)
magnetization_map_xyz_2 = ud.LLB.convert_polar_to_cartesian(magnetization_map_2)
plt.figure(figsize=[6, 8])
plt.subplot(2, 1, 1)
plt.plot(delays, np.mean(magnetization_map_xyz_2[:, is_magnetic, 2], axis=1), label=r'$M_z$')
plt.legend()
plt.xlabel('Delay (ps)')
plt.ylabel('Magnetization')
plt.subplot(2, 1, 2)
plt.plot(delays, np.mean(magnetization_map_xyz_2[:, is_magnetic, 0], axis=1), label=r'$M_x$')
plt.plot(delays, np.mean(magnetization_map_xyz_2[:, is_magnetic, 1], axis=1), label=r'$M_y$')
plt.legend()
plt.xlabel('Delay (ps)')
plt.ylabel('Magnetization')
plt.show() and this I do not understand. |
Hi @friweber , Thanks for the continuation! Maybe you should move the second part of your example to a dedicated issue and if so, it would be good to know, whether the problem depends on the thickness of the magnetic layer. Although your code regarding the equilibrium magnetization seems to work properly, I am wondering if the time steps/duration could be adapted somehow? Thanks Daniel |
In the newly implemented LLB class that solves the Landau Lifshitz Bloch equation it would be very useful to have a method that determines the equilibrium magnetization direction for a given external field.
Using this method one could initialize the magnetization state to the equilibrium value prior to laser-excitation. One possibility would be to run the LLB simulation without laser-excitation with an increased damping until the change in the magnetization between two timesteps falls below a certain threshold or a time-limit is reached.
In the process of implementing the Landau-Lifshitz-Gilbert LLG equation that accounts also for the magneto-elastic coupling we run into the same issue, which we approach by letting the simulation run for many nanoseconds prior to the laser excitation at t = 0 so that the magnetization reaches has reached its equilibrium value as shown in the following image:
This process is however very costly as it creates huge magnetization map arrays with irrelevant data that only reflects the precession towards the equilibrium state starting from a user-defined definition. Since the same equilibration problem affects the LLB and the m3TM I wonder if you could provide a method that initializes the magnetization map of this structure to its equilibrium value for a given initial Temperature, initial strain state and initial external field direction.
The text was updated successfully, but these errors were encountered: