Tests

Contents

Tests#

Overview#

This module is a collection of files that test the functions in the MrfmSim package. To run the unit tests, open up a terminal in the MrfmSim directory and run the following command:

python -m pytest

Signal verification#

We can test the signal-calculation modules by computing the signal from a single spin. Consider a magnet-tipped cantilever operated in the hangdown geometry:

  • a magnetic field is applied along the \(z\) direction to polarize the cantilever’s magnet and to define the quantization axis of the sample’s spins;

  • the cantilever oscillates in the \(x\) direction;

  • the long axis of the cantilever is parallel to the \(z\) direction;

  • the magnet at the tip of the cantilever is a sphere of radius \(r\), polarized along the \(z\) direction, with saturation magnetization \(M_s\) and saturation field \(\mu_0 M_s\);

  • the center of the spherical magnet lies at the origin, \((0,0,0)\);

  • the sample lies in the \(xy\) plane; for simplicity, we further assume that the spin lies along the \(y = 0\) line;

  • the distance between the center of the magnet and the sample plane is \(z\).

Some notes on the additional tests

Magnet Test#

The RectMagnet is tested through its symmetry factors: it is symmetric in the \(z\) direction when x and y are 0. The gradient functions are tested against a numerical derivative calculation from the field. \(B_z\) is an even function in the \(x\) direction, \(B_{zx}\) is an odd function in the x-direction and :math: B_{zxx} is an even function in the \(x\) direction.

Polarization Test#

The polarization test for irrad_periodic function is taken from John’s notebook

Experiment Test#

Some of the tests are calculated against some of the old notebook examples from John and Corinne. Note to see the notebook and examples go to the MrfmSim-archived package’s git commit (c74d504).

  • IBMCyclic dF_spin -> test_experiment from John Marohn.

  • IBMCyclic dF2_spin -> value taken from test-ibmexpt-1.ipynb simulation 1, #4 - 9, matching with relative 5e-5 precision.

  • CermitARP is taken from “test_cornellexptobject.py”

  • CermitESR is tested against value from “test-cornellexpt-4.ipynb” (Moore experiment - local peak, # 0 - 5, Issac Experiment, # 24 - 28) the difference in value is expected because the min_abs_offset function fixed a previous issue.

  • CermitARP_SmallTip is tested against “test-cornellexpt-8-Hickman_nanomagnet_simulations.ipynb”, first simulation.

  • CermitARP_SmallTip is compared with CermitARP, in two cases using a rectangular magnet, one is when the amplitude is small, they should be the same, and when tip_sep is large, the effect of amplitude is also small (There is a comparison in “test-micrometertip_smallampapprox_vs_exactsol.ipynb” on small amplitude, however, the result is unchecked).

  • CmeritSingleSpinESR_Approx is compared with CermitARP_SmallTip.

  • SingleSpinESR
    • The approximation is tested against both SPAM and hangdown geometry of the analytical solution

Force detection#

The force produced by a single spin is

\[\delta F_{\mathrm{spin}} = \mu_{\mathrm{spin}} \: G_{zx}(r_{\mathrm{spin}})\]

with \(\mu_{\mathrm{spin}}\) the spin magnetic moment and \(G_{zx} = \partial B_z / \partial x\) and \(r_{\mathrm{spin}}\) the location of the spin. The gradient experienced by a spin located in the \(y = 0\) plane is

\[G_{zx}(x,0,z) = 4 \mu_0 M_s r^3 \: \frac{x \: (x^2 - 4 z^2)}{(x^2 + z^2)^{7/2}}\]

We see that the gradient \(G_{zx}\) is zero for a spin directly below the tip at \((0,0,z)\). For the single-spin force experiment, we must therefore place the spin “off to the side”, at a location \((x,0,z)\). The gradient is maximized at (approximately) \(x_{\mathrm{opt}} = \pm 0.389295 \: z\). For a spin placed at this optimal location,

\[G_{zx}(x_{\mathrm{opt}}, 0, z) \approx 0.914269 \: \frac{\mu_0 M_s}{r} \left( \frac{r}{z} \right)^{4}\]

In the hangdown-geometry experiment delineated above, the force acting on a magnet-tipped cantilever from a single spin placed at an optimal location “off to the side” of the cantilever tip is

\[\delta F_{\mathrm{spin}} \approx 0.914269 \: \mu_{\mathrm{spin}} \: \frac{\mu_0 M_s}{r} \left( \frac{r}{z} \right)^{4}\]

With

  • tip magnetization \(\mu_0 M_s = 1800 \: \mathrm{mT}\) (cobalt)

  • tip radius \(r = 50 \: \mathrm{nm}\)

  • tip-sample separation \(20 \: \mathrm{nm}\), that is, \(z = 70 \: \mathrm{nm}\)

  • a single (fully polarized) electron spin placed at \((27.2507, 0.0000, 70.0000) \: \mathrm{nm}\)

the force is

\[\delta F_{\mathrm{spin}} = -79.231 \: \mathrm{aN}\]

Force-gradient detection#

The force-gradient produced by a single spin is

\[\delta k_{\mathrm{spin}} = \mu_{\mathrm{spin}} \: G_{zxx}(r_{\mathrm{spin}})\]

with \(G_{zxx} = \partial^2 B_z / \partial x^2\). For a single spin directly below the tip at \((0,0,z)\),

\[G_{zxx}(0,0,z) = - \frac{4 \mu_0 M_s r^3}{z^5} = - \frac{4 \mu_0 M_s}{r^2} \left( \frac{r}{z} \right)^{5}\]

In the hangdown-geometry experiment delineated above, the force gradient acting on a magnet-tipped cantilever from a single spin placed directly below the cantilever tip is

\[\delta k_{\mathrm{spin}} = - \mu_{\mathrm{spin}} \: \frac{4 \mu_0 M_s}{r^2} \left( \frac{r}{z} \right)^{5}\]

With

  • tip magnetization \(\mu_0 M_s = 1800 \: \mathrm{mT}\) (cobalt)

  • tip radius \(r = 50 \: \mathrm{nm}\)

  • tip-sample separation \(20 \: \mathrm{nm}\), that is, \(z = 70 \: \mathrm{nm}\)

  • a single (fully polarized) electron spin placed at \((0.0, 0.0, 70.0) \: \mathrm{nm}\)

the force gradient is

\[\delta k_{\mathrm{spin}} = 4.95203 \: \mathrm{aN} \: \mathrm{nm}^{-1} = 4.95203 \: \mathrm{nN} \: \mathrm{m}^{-1}\]

Testing Lists#

class tests.test_experiment.test_cermitesr.TestCERMITESR[source]#

Bases: object

Test cermitesr experiment.

test_cermitesr_SPAM(sample, cantilever)[source]#

Test the result in SPAM geometry, Moore parameters, local peak.

test_cermitesr_hangdown(sample, cantilever)[source]#

Test the result in SPAM geometry, Issac parameters.

tests.test_experiment.test_cermitesr.cantilever()[source]#

Return the cantilever object.

tests.test_experiment.test_cermitesr.sample()[source]#

Return the sample object.

class tests.test_experiment.test_cermitarp.TestCERMITARP[source]#

Bases: object

Test cermitarp experiments.

grid = GridObject(
    L = [0.5E-3,0.5E-3,0.5E-3],
    dL= [0.5E-3,0.5E-3,0.5E-3],
    o=[0.0, 0.0, 0.0])

The resulting grid is

  • centered at zero,

  • contains \(2 \times 2 \times 2 = 8\) grid points, and

  • runs from -0.5e-3 to +0.5e-3 nm in each direction.

So that the grid contains 1 spin total, we need to fudge the spin density to be \(1.0 \times 10^{9} \: \mathrm{spins} \: \mathrm{nm}^{3}\). Other notable features of the calculations:

  • We work at 10 millikelvin and 10.0 tesla so that the electron spin is nearly fully polarized

  • In the ARP experiment, we set the irradiation frequency to be in resonance with spins directly below the tip. These spins experience a total field of 10437.318 mT. The assumed \(B_1\) is 10 mT, and the FM sweep is ten times that (in field units). In setting up expt1 below, we must take care to set B_0_start to 10000.0 mT and not 10437.318 mT.

We check to see that the single-spin signal and the pulse time agree with expected values to be within a 2 percent (relative) error.

grid()[source]#

Setup grid.

magnet()[source]#

Setup magnet.

sample()[source]#

Setup sample.

test_cermitarp(grid, magnet, sample)[source]#

Test Cornell ARP full signal simulation.

class tests.test_experiment.test_cermitarp.TestCERMITESRSmallTip[source]#

Bases: object

Compare different limits between experiment settings.

sample()[source]#

Setup sample.

test_smallamp_arp_vs_exact_solution_large_sep(sample)[source]#

Test cermitarp_smalltip vs cermitarp

Test that when tip-sample separation is large the amplitude can also be ignored

test_smallamp_arp_vs_exact_solution_small_amp(sample)[source]#

Test smalltip_arp vs. cornellcermit_arp

Test that in small amplitude conditions, the approximation is the same as the small tip, which does not ignore the amplitude.

Test the group of CERMIT ESR experiments.

class tests.test_experiment.test_cermitsinglespin.TestCermitSinglespin[source]#

Bases: object

Test the CERMIT ESR experiment with a single spin.

See test_misc.py for the test of the numerical solution. Here, we test that the approximated solution is similar to the exact solution.

n_pts()[source]#

Number of points in the trapezoid approximation.

sample()[source]#

Return the sample object.

test_cermitesr_singlespin_hangdown(sample, n_pts, x_0p)[source]#

Compare the numerical solution with the exact solution.

test_cermitesr_singlespin_spam(sample, n_pts, x_0p)[source]#

Compare the numerical solution with the exact solution.

class tests.test_experiment.test_ibmcyclic.TestIBMCyclic[source]#

Bases: object

Test IBMCyclic Experimental method.

test_IBMCyclic_dF2_spin()[source]#

Test IBM cyclic force variance signal for nucleus.

Values are taken from “test-ibmexpt-1.ipynb” simulation 1 #4- #9 based on John’s calculation.

test_IBMCyclic_dF_spin()[source]#

Test IBM curie law signal.

class tests.test_formula.test_field.TestXTrapzFieldGradient[source]#

Bases: object

Test trapz field gradient.

test_xtrapz_field_gradient_large_distance()[source]#

Test gradient against a large tip-sample separation.

When the distance is very large, the change of Bzx in between the grid points are small.

test_xtrapz_field_gradient_smallamp()[source]#

Test gradient against a small x0p.

When the 0 to the peak is very small compared to the magnet, the field is equivalent to Bzxx. Here we “fake” a small grid step for the x direction, because the grid step here is only used to calculate x_0p. The cantilever is 150 nm away from the sample.

class tests.test_formula.test_field.TestXTrapzFxDtheta[source]#

Bases: object

Test xtrapz_fxdtheta.

test_xtrapz_fxdtheta_cos()[source]#

Test xtrapz_fxdtheta against :math: cos{x}dx

Let’s consider the field method returns 1, the Trapezoid integral should return the value of

\[\int_{-\pi}^{\pi}{x_0 \cos\theta d\theta}\]

The results: (-pi/2 -> 0): x0 (-pi -> 0): 0

test_xtrapz_fxdtheta_multidim()[source]#

Test xtrapz_fxdtheta against :math: x cos{x} for multi-dimensions.

test_xtrapz_xtrapz_fxdtheta_x2cos()[source]#

Test xtrapz_fxdtheta against :math: x2cos{x}.

Let’s consider a case where the field f(x) = x

\[\int_{-\pi}^{\pi}{(x-x_0 \cos\theta)x_0 \cos\theta d\theta}\]

The results: (-pi/2 -> 0): x^2*x0 - pi/2*x*x0^2 + 2/3 * x0^3 (-pi -> 0): - pi*x*x0^2

test_xtrapz_xtrapz_fxdtheta_xcos()[source]#

Test xtrapz_fxdtheta against :math: x cos{x}.

Let’s consider a case where the field f(x) = x

\[\int_{-\pi}^{\pi}{(x-x_0 \cos\theta)x_0 \cos\theta d\theta}\]

The results: (-pi/2 -> 0): x*x0 - x0^2*pi/4 (-pi -> 0): - x0^2 * pi/2

tests.test_formula.test_field.test_B_offset()[source]#

Test B_offset.

Test the nucleus.

tests.test_formula.test_field.test_field_func()[source]#

Test field.

Test the field calculation against ogrid and mgrid.

tests.test_formula.test_field.test_field_func_singularity()[source]#

Test field when the grid has one point in one direction.

In this example, the z direction has only one point. The resulting grid is effectively a 2d plane.

tests.test_formula.test_field.test_min_abs_offset()[source]#

Test min_abs_x.

tests.test_formula.test_field.test_min_abs_offset_symmetry()[source]#

Test if min_abs_x result is symmetric. If a matrix is flipped, the result should also be flipped

tests.test_formula.test_math.test_as_strided_x()[source]#

Testing if as_strided_x maximum matches running max.

tests.test_formula.test_math.test_slice_matrix()[source]#

Test slice matrix cuts the matrix in the center.

tests.test_formula.test_magnetization.test_mz2_eq_H()[source]#

Test proton magnetization variance at equilibrium.

tests.test_formula.test_magnetization.test_mz_eq_high_susceptibility(Gamma, J, temperature, mz_eq_true)[source]#

Test mz_eq function on different types of samples at low temperature. The total field is set to 100 T

tests.test_formula.test_magnetization.test_mz_eq_low_susceptibility(B_tot)[source]#

Test mz_eq on 71Ga at high temperature.

tests.test_formula.test_misc.test_convert_grid_pts()[source]#

Test convert_grid_pts.

Make sure the final value is an int and is the floor value.

tests.test_formula.test_misc.test_sum_of_product()[source]#

Test sum_of_product.

Make sure the final value is an int and is the floor value.

Test the relative change in polarization.

tests.test_formula.test_polarization.sample_e()[source]#

Electron sample.

tests.test_formula.test_polarization.sample_h()[source]#

Nucleus sample.

tests.test_formula.test_polarization.test_rel_dol_sat_td_smallsteps(sample_e)[source]#

Test rel_dol_sat_td_smallsteps.

Small steps approximation should have the same result as regular when Bzx stays the same, given that delta_B_offset has the same sign as Bzx.

tests.test_formula.test_polarization.test_rel_dpol_arp(sample_h)[source]#

Test rel_dpol_arp.

if f_fm is close to infinity, the relative change in polarization is -2.0 if b1 and the modulation frequency is small and b_offset is large the spin does not flip therefore the relative change in polarization is close to 0.0 (off-resonance case)

tests.test_formula.test_polarization.test_rel_dpol_ibm_cyclic(sample_h)[source]#

Test rpol_arp_ibm.

Sample -> proton spin -> rpol_arp_ibm() limiting cases Sample -> h -> proton spin

tests.test_formula.test_polarization.test_rel_dpol_multipulse_long(sample_e)[source]#

Test rel_dpol_multipulse when the time between pulses is long.

In this case, the final polarization should be relaxed to 1 and the chang in polarization is 0.

tests.test_formula.test_polarization.test_rel_dpol_multipulse_no_pol(sample_e)[source]#

Test rel_dpol_multipulse when relative polarization is 0.

tests.test_formula.test_polarization.test_rel_dpol_multipulse_short(sample_e)[source]#

Test rel_dpol_multipulse when the pulse time difference is short.

Because the time between pulses is short and the equation ignores relaxation during pulses,

tests.test_formula.test_polarization.test_rel_dpol_nut(sample_h)[source]#

Test rel_dpol_nut.

Sample -> proton spin -> rpol_nut(), on resonance pulses Sample -> h -> proton spin

tests.test_formula.test_polarization.test_rel_dpol_periodic_irrad_cont(sample_e)[source]#

Test rel_dpol_periodic_irrad in the continuous case.

When the if t_off is 0, the intermittent irradiation becomes continuous irradiation. This should be the same as rel_dpol_sat When t_on is 0, the signal should vanish

tests.test_formula.test_polarization.test_rel_dpol_periodic_irrad_no_irrad(sample_e)[source]#

Test rel_dpol_periodic_irrad in no irradiation case.

When t_on is 0, the signal should vanish

tests.test_formula.test_polarization.test_rel_dpol_periodic_irrad_off_res(sample_e)[source]#

Test rel_dpol_periodic_irrad off-resonance case.

When is it off-resonance (B_offset is large), the signal is 0

tests.test_formula.test_polarization.test_rel_dpol_sat_steadystate(sample_e)[source]#

Test the rel_dpol_sat absolute tolerance.

Sample -> e -> electron spin, J = 1/2, high field limit

tests.test_formula.test_polarization.test_rel_dpol_sat_td(sample_e)[source]#

Test rel_dpol_sat_td when offset is 0.

When the offset is 0, the result should be 0

Here we construct a 1D grid size of 3, and extended grid size of 5

tests.test_formula.test_polarization.test_rel_dpol_sat_td_symmetry(sample_e)[source]#

Test rel_dpol_sat_td is symmetric around the initial and final offset.

Here we construct a 1D grid size of 3, and an extended grid size of 5

tests.test_formula.test_polarization.test_rel_dpol_sat_td_without_td(sample_e)[source]#

Test rel_dpol_sat_td completely saturate spins if no td component.

Here we construct a 1D grid size of 3 and an extended grid size of 5