Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ivlis/MagDots
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.1
Choose a base ref
...
head repository: ivlis/MagDots
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
  • 3 commits
  • 23 files changed
  • 2 contributors

Commits on Dec 19, 2015

  1. Add doi badge

    Ivan Lisenkov committed Dec 19, 2015
    Copy the full SHA
    d460196 View commit details
  2. Typo

    ivlis committed Dec 19, 2015
    Copy the full SHA
    363fe0d View commit details

Commits on Jul 27, 2019

  1. Unit tests (#2)

    * More logical ordering
    
    * Add tests for the Dot and DoubleDot classes
    
    * Add travis.yml
    
    * Install numpy and scipy via apt
    
    * Use system wide packages
    
    * Use miniconda for numpy and scipy
    
    * Use miniconda3
    
    * Remove addons
    
    * An attempt to build cython
    
    * An attempt to gcc from conda
    
    * Add gsl dep
    
    * Fix
    
    * Fix2
    
    * Install gsl via apt
    
    * Add coverall
    
    * Fix
    
    * Add coveralls pip
    
    * Add coverage configuration
    
    * Remove nose artifact
    
    * Omit all __init__.py
    
    * Add .covergare to gitignore
    
    * Add lattice tests
    
    * New lattice tests
    
    * Testing via make
    
    * Add additional python versions
    
    * Fix for Python <3.5
    
    * Update README
    
    * Add some utils tests
    
    * Test symmetry properties of Nk
    
    * Add construct_Nk function for convenient usage
    
    * Add tests on the symmetry properties of Fk
    
    * Multiprocess tests
    
    * Rename test directory
    
    * Add test for Toeplitz matrix generator
    
    * Add FM and AFM calculation examples as tests
    
    * Fix multiprocess on travis
    
    * Forbid multiprocess on travis
    
    * Remove unnecessary matplotlib imports
    
    * Replace all_close to almost_equal_nulp
    
    * Revert "Replace all_close to almost_equal_nulp"
    
    This reverts commit 58ff3ae.
    
    * Set an absolute tolerance for frequency comparation
    
    * Eigen solver agnostic check of eigen vectors
    
    * Test Exception for array instability
    
    * Support testing on OSX
    
    Add support for testing on Apple OS X.
    
    * Update README for say about OS X
    
    * Add test on the Ekn symmetry
    ivlis authored Jul 27, 2019
    Copy the full SHA
    f13398d View commit details
9 changes: 9 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[run]
branch = True
source = model
util
util_pyx
omit = tests/*
test_*.*
__init__.py
*/__init__.py
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -4,4 +4,4 @@ build/
util*.so
util*.pyd
util.c

.coverage
52 changes: 52 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
os:
- linux
- osx
language: generic
env:
- PYTHON_VERSION="3.3"
- PYTHON_VERSION="3.4"
- PYTHON_VERSION="3.5"
matrix:
allow_failures:
- os: osx
exclude:
- os: osx
env: PYTHON_VERSION="3.3"
- os: osx
env: PYTHON_VERSION="3.4"

addons:
apt:
packages:
- libgsl0-dev
- gsl-bin
before_install:
- >
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh;
else
wget https://repo.continuum.io/miniconda/Miniconda3-latest-MacOSX-x86_64.sh -O miniconda.sh;
fi
- chmod +x miniconda.sh
- ./miniconda.sh -b
- pwd
- export PATH=$HOME/miniconda3/bin:$PATH
- conda update --yes conda
- conda install -y -c https://conda.anaconda.org/asmeurer gsl
install:
- conda install --yes python=$PYTHON_VERSION gcc numpy scipy cython
- gcc --version
- python --version
- pip install nose2 nose-cov
- pip install coveralls
- C_INCLUDE_PATH=$HOME/miniconda3/envs/include make
# - conda install --yes -c dan_blanchard python-coveralls nose-cov
script:
- >
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
make test-coverage
else
DYLD_LIBRARY_PATH=$HOME/miniconda3/lib make test-coverage
fi
after_success:
- coveralls
9 changes: 7 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PYTHON=/usr/bin/python3
PYTHON=python3
RM=/usr/bin/rm


.PHONY: all clean test test-coverage

all:
$(PYTHON) setup.py build_ext --inplace
@@ -11,3 +11,8 @@ clean:
$(RM) util_pyx/util.c
$(RM) util_pyx/util*.so

test:
nose2 --plugin=nose2.plugins.mp

test-coverage:
coverage run -m nose2.__main__ -v
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# MagDots: Magnetization dynamics of nanoelement arrays

[![DOI](https://zenodo.org/badge/19702/ivlis/MagDots.svg)](https://zenodo.org/badge/latestdoi/19702/ivlis/MagDots)

## Purpose

The program is capable to calculate a spin-wave spectrum and a distribution of an internal magnetic
@@ -11,14 +13,21 @@ nanodots"](http://arxiv.org/abs/1511.08483).
## Installation

### Requirements
* Python 3.4 or later
* Cython and a C compiler
* Python 3.3 or later
* Cython and a C compiler (gcc)
* Make
* NumPy and SciPy
* GNU GSL library from your distribution (headers and binary)
* Matplotlib for plotting

To compile the Cython code execute the Makefile. Compilation was tested on Linux-based systems only,
To compile the Cython code execute the Makefile:
`make`

Compilation was tested on Linux-based and OS X systems only,
however, should work on other systems with Cython and a C compiler installed.

To run tests type `make tests`

Having the Cython code complied, one can run examples from the `examples` directory

## Usage
@@ -55,7 +64,7 @@ we kindly ask you cite it as:

### Copyright

The program in written by Ivan Lisenkov. The analytical theory is developed by Ivan Lisenkov, Vasyl
The program is written by Ivan Lisenkov. The analytical theory is developed by Ivan Lisenkov, Vasyl
Tyberkevych and Andrei Slavin at The Department of Physics, Oakland University, Rochester, Michigan, USA.

(C) 2015 Ivan Lisenkov
31 changes: 20 additions & 11 deletions model/dot.py
Original file line number Diff line number Diff line change
@@ -4,14 +4,14 @@
This file is part of MagDots.
(C) Ivan Lisenkov
Oakland Univerity,
Oakland Univerity,
Michigan, USA
2015
MagDots: Magnetization dynamics of nanoelement arrays
How to cite.
How to cite.
If you are using this program or/and produce scientific publications based on it,
we kindly ask you to cite it as:
@@ -47,8 +47,11 @@
from ..util.util import set_limit_value, make_block_diag
from ..util_pyx.util import mod2, norm2, dot2, real_exp, real_j1, real_close2zero, phase_shift

#from ._dot import *

def construct_Nk(dot, fn):
elements = dot.get_elements()
vals = [fn(e) for e in elements]
Nk = dot.elements_to_matrix(vals) + dot.K
return Nk

class Dot:
"""Implement demag tensor calculation for single dot"""
@@ -65,6 +68,9 @@ def __init__(self, R, h, Ms, anisotropy=None):
self._na = na
self._Ha = Ha

@property
def number_of_elements(self):
return 1

def get_elements(self):
return [self.N11, self.N22, self.N12, self.N33]
@@ -117,7 +123,7 @@ def _sigma(self, k):
return self.S
else:
return self.S * 2.0*real_j1(kr)/kr


def _nij_helper(fn):
@functools.wraps(fn)
@@ -153,7 +159,7 @@ def N22(self, k, kr, fkh):
@_nij_helper
def N33(self, k, kr, fkh):
return 1.0 - fkh


class DoubleDot(Dot):
def __init__(self, R, h, Ms, d, anisotropies=None):
@@ -178,6 +184,10 @@ def shifted(k):
return res
return shifted

@property
def number_of_elements(self):
return 2

def get_elements(self):
"""@todo: Docstring for get_elements.
:returns: @todo
@@ -186,10 +196,10 @@ def get_elements(self):
single_dot = super().get_elements()
d = self._d
elements = single_dot
elements.extend([self._shift(f, d) for f in single_dot])
elements.extend([self._shift(f, -d) for f in single_dot])
elements.extend([self._shift(f, d) for f in single_dot])
return elements

@classmethod
def elements_to_matrix(cls, e):
Nk = np.zeros((6,6), dtype=complex)
@@ -198,8 +208,8 @@ def elements_to_matrix(cls, e):
Nk21 = super().elements_to_matrix(e[8:12])
Nk[0:3,0:3] = Nk11
Nk[3:6,3:6] = Nk11
Nk[0:3,3:6] = Nk21
Nk[3:6,0:3] = Nk12
Nk[0:3,3:6] = Nk12
Nk[3:6,0:3] = Nk21
return Nk

@property
@@ -222,4 +232,3 @@ def __init__(self, R, Ms):
@set_limit_value(1,0.0,0.0)
def _fkh(self, kmod):
return 1.0

44 changes: 19 additions & 25 deletions model/dotarray.py
Original file line number Diff line number Diff line change
@@ -4,14 +4,14 @@
This file is part of MagDots.
(C) Ivan Lisenkov
Oakland Univerity,
Oakland Univerity,
Michigan, USA
2015
MagDots: Magnetization dynamics of nanoelement arrays
How to cite.
How to cite.
If you are using this program or/and produce scientific publications based on it,
we kindly ask you to cite it as:
@@ -37,8 +37,6 @@
'''


#import matplotlib.pyplot as plt
#import matplotlib.pyplot as plt
import itertools
import numpy as np
import scipy.constants as const
@@ -47,7 +45,6 @@
import scipy.optimize as OPT

from functools import wraps
from matplotlib import pyplot as plt
from multiprocessing import Pool
from numpy.fft import fft, fftshift

@@ -59,12 +56,6 @@
mu0 = const.codata.value('mag. constant')
gamma = const.codata.value('electron gyromag. ratio')

def make_toeplitz_from_blocks(B):
assert(len(B)%2 == 1)
t = int(len(B)/2 + 1)
T = np.vstack([np.hstack(B[i:i+t]) for i in range(t-1, -1, -1)])
return T

class ArrayUnstable(Exception):
pass

@@ -83,7 +74,7 @@ def magn_norm(m, J):
class Wave:
self_vectors = True
check_stability = True

@staticmethod
def __sort(w, v):
w = -np.imag(w)
@@ -167,7 +158,7 @@ def mu(self, value):
@property
def Bbias(self):
return self._Bbias

@Bbias.setter
def Bbias(self, Bbias):
assert(Bbias.shape == self.mu.shape)
@@ -224,7 +215,7 @@ def equilibrium_eq(mu_B):
raise RuntimeError("Equlibrium condition cannot be found")

self.mu = sol.x[0:3*N]

B = make_block_diag([sol.x[3*N + n ]*np.identity(3) for n in range(N)])

return mu0*dot.Ms*B
@@ -256,7 +247,7 @@ def __init__(self, dot, lattice, mu, B_comp = False, B_comp_middle = False, K_MA
self._B_comp = B_comp
self._B_comp_middle = B_comp_middle
self._mu = self.__constr_magn_lattice(mu, self.N_MAX)

@staticmethod
def __constr_magn_lattice(ma_la, N_MAX):
unrolled = [mu for i,mu in zip(range(0, N_MAX+1), ma_la)]
@@ -302,21 +293,21 @@ def _get_J(self):
#@profile
def Fk(self, k):
Eks = self._Ek(k)
Eks = make_toeplitz_from_blocks(Eks)
Eks = _make_toeplitz_from_blocks(Eks)
return Eks

def _Ek(self, k):
dot = self._dot
N_funcs = dot.get_elements()

E_comps = [self._Ek_component(k, func) for func in N_funcs]
E_comps = [self._Ek_component(k, func) for func in N_funcs]

K = dot.K

Eks = [dot.elements_to_matrix(Ek) for Ek in zip(*E_comps)]

Eks[self.N_MAX] += K

return Eks

def _Ek_component(self, k, n_func):
@@ -327,7 +318,7 @@ def _Ek_component(self, k, n_func):
def func(l, t):
K = k_shift(b1, b2, k, l, t)# (k/(2*np.pi) + l)*b1 + t*b2
return n_func(K)

Ek33 = self._calculate_sum(func)

Ek33 /= self._la.S0
@@ -371,14 +362,14 @@ def _calculate_sum(self, func):
#@profile
def _calculate_fft(self, func, T, tick, N, NK):
N_MAX = self.N_MAX

F = map_array(T, func)
FFT = fft(F)*tick*2

W = np.fft.fftfreq(N, d=tick)
FFT = np.exp(-1.0j*np.pi*W*tick*(N))*FFT
FFT = np.exp(-1.0j*np.pi*W*tick*(N))*FFT
FFT = np.fft.fftshift(FFT)

s_fft = FFT[N//2-2*N_MAX*N//NK:N//2+2*(N_MAX+1)*N//NK:2*N//NK]

return s_fft
@@ -389,6 +380,9 @@ def integrant_cos(alpha):

I = complex_quad(integrant_cos, -np.inf, np.inf, limit=1000, epsabs=1e-5)[0]
return I



def _make_toeplitz_from_blocks(B):
assert(len(B)%2 == 1)
t = int(len(B)/2 + 1)
T = np.vstack([np.hstack(B[i:i+t]) for i in range(t-1, -1, -1)])
return T
1 change: 1 addition & 0 deletions test/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
'''Unit tests'''
Binary file added test/fixtures/bulk_CAFM.npz
Binary file not shown.
Binary file added test/fixtures/bulk_FM.npz
Binary file not shown.
76 changes: 76 additions & 0 deletions test/test_Ekn_symmetry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env python3

import unittest
import numpy as np
import numpy.testing as npt

import scipy.constants as const
mu0 = const.codata.value('mag. constant')

from nose2.tools import params

from ..model.dot import Dot, DoubleDot
from ..model.dotarray import EdgeWave
from ..model.lattice import Lattice
from ..model.magn_lattice import FM_state


R = 1.0
p = dict(R = R,
h = 30.0*R,
Ms = 1.0
)
cylindricalDot = Dot(**p)
p = dict(R = R,
h = 30.0*R,
Ms = 1.0,
d = np.array([3.0*R,0,0])
)
doubleDot = DoubleDot(**p)

a1 = np.array([3.0, 0.0, 0.0])
a2 = np.array([0.0, 3.0, 0.0])
square_la = Lattice(a1, a2)


a1 = np.array([3.0, 0.0, 0.0])
a2 = np.array([0.0, 6.0, 0.0])
rectangular_la = Lattice(a1, a2)

a1 = np.array([3.0, 0.0, 0.0])
a2 = 3.0*np.array([0.5, np.sqrt(3.0)/2.0, 0.0])
triangular_la = Lattice(a1, a2)

matrix = [(dot, la) for dot in [cylindricalDot, doubleDot]
for la in [square_la, rectangular_la, triangular_la]]

@params(*matrix)
def test_E00_trace(dot, la):
mu = np.array([0.0,0.0,1.0])
magn_lattice = FM_state(mu)

L_MAX = 60
N_MAX = 60

edge = EdgeWave(dot, la, magn_lattice, L_MAX = L_MAX, N_MAX = N_MAX)

Ek0 = edge._Ek(0)
cell_dim = dot.number_of_elements
Ek00 = Ek0[N_MAX]
Ek02 = Ek0[N_MAX+2]
Ek0m2 = Ek0[N_MAX-2]
npt.assert_allclose(np.trace(Ek00), 1*cell_dim, atol=1e-1)
npt.assert_allclose(np.trace(Ek02), 0, atol=1e-2)
npt.assert_allclose(Ek02, np.conj(Ek0m2).T, atol=1e-3)

# @params(*matrix)
# def test_Fk(dot, la):
# mu = None
# bulk = BulkWave(dot, la, mu, K_MAX = 100)
# k = np.array([0.3, 0.6, 0])
# Fk = bulk.Fk(k)
# Fminusk = bulk.Fk(-k)
# cell_dim = dot.number_of_elements
# npt.assert_allclose(np.trace(Fk), 1*cell_dim, atol=1e-2)
# npt.assert_allclose(Fk, np.conj(Fk).T, atol=1e-2)
# npt.assert_allclose(Fk, Fminusk.T, atol=1e-2)
65 changes: 65 additions & 0 deletions test/test_Fk_symmetry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env python3

import unittest
import numpy as np
import numpy.testing as npt

import scipy.constants as const
mu0 = const.codata.value('mag. constant')

from nose2.tools import params

from ..model.dot import Dot, DoubleDot
from ..model.dotarray import BulkWave
from ..model.lattice import Lattice


R = 1.0
p = dict(R = R,
h = 30.0*R,
Ms = 1.0
)
cylindricalDot = Dot(**p)
p = dict(R = R,
h = 30.0*R,
Ms = 1.0,
d = np.array([3.0*R,0,0])
)
doubleDot = DoubleDot(**p)

a1 = np.array([3.0, 0.0, 0.0])
a2 = np.array([0.0, 3.0, 0.0])
square_la = Lattice(a1, a2)


a1 = np.array([3.0, 0.0, 0.0])
a2 = np.array([0.0, 6.0, 0.0])
rectangular_la = Lattice(a1, a2)

a1 = np.array([3.0, 0.0, 0.0])
a2 = 3.0*np.array([0.5, np.sqrt(3.0)/2.0, 0.0])
triangular_la = Lattice(a1, a2)

matrix = [(dot, la) for dot in [cylindricalDot, doubleDot]
for la in [square_la, rectangular_la, triangular_la]]

@params(*matrix)
def test_F0_trace(dot, la):
mu = None
bulk = BulkWave(dot, la, mu, K_MAX = 100)
F0 = bulk.F0
cell_dim = dot.number_of_elements
npt.assert_allclose(np.trace(F0), 1*cell_dim, atol=1e-2)
npt.assert_allclose(np.imag(F0), np.zeros((3*cell_dim, 3*cell_dim)), atol=1e-3)

@params(*matrix)
def test_Fk(dot, la):
mu = None
bulk = BulkWave(dot, la, mu, K_MAX = 100)
k = np.array([0.3, 0.6, 0])
Fk = bulk.Fk(k)
Fminusk = bulk.Fk(-k)
cell_dim = dot.number_of_elements
npt.assert_allclose(np.trace(Fk), 1*cell_dim, atol=1e-2)
npt.assert_allclose(Fk, np.conj(Fk).T, atol=1e-2)
npt.assert_allclose(Fk, Fminusk.T, atol=1e-2)
38 changes: 38 additions & 0 deletions test/test_Nk_symmetry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env python3

import unittest
import numpy as np
import numpy.testing as npt

import scipy.constants as const
mu0 = const.codata.value('mag. constant')

from nose2.tools import params

from ..model.dot import Dot, DoubleDot, construct_Nk

R = 1.0
p = dict(R = R,
h = 30.0*R,
Ms = 1.0
)
cylindricalDot = Dot(**p)
p = dict(R = R,
h = 30.0*R,
Ms = 1.0,
d = np.array([3.0*R,0,0])
)
doubleDot = DoubleDot(**p)

@params(cylindricalDot, doubleDot)
def test_minus_k(dot):
KX = np.linspace(-10,10, num=20)
KY = np.linspace(-10,10, num=20)
for kx in KX:
for ky in KY:
k = np.array([kx, ky, 0])
Nk = construct_Nk(dot, lambda e: e(k))
Nminusk = construct_Nk(dot, lambda e: e(-k))
Nkconj = np.conj(Nk).T
npt.assert_allclose(Nk, Nminusk.T, atol=1e-7)
npt.assert_allclose(Nk, Nkconj, atol=1e-7)
70 changes: 70 additions & 0 deletions test/test_bulk_CAFM.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import sys
sys.path.append("../..")

import unittest
import numpy as np
import scipy.constants as const

import numpy.testing as npt

mu0 = const.codata.value('mag. constant')
gamma = const.codata.value('electron gyromag. ratio')

from ..model.dot import Dot, DoubleDot
from ..model.lattice import Lattice, ContourIterator
from ..model.dotarray import BulkWave, magn_norm
from ..util.util import calculate_bulk, BulkData

def _calc_norms(V, J):
a, b_ = J.shape
norms = [magn_norm(m, J) for m in np.reshape(np.swapaxes(V,1,2), (-1,a))]
return norms

class TestBulkCAFM(unittest.TestCase):
def test_bulk_cafm(self):

# Setting array's physical parameters

R = 1.0
a = 4.1*R
h = 5*a

a1 = np.array([1.0*a, -1.0*a, 0.0])
a2 = np.array([1.0*a, 1.0*a, 0.0])


d = np.array([1.0*a, 0.0*a, 0.0])

Ms = 1.0

# Setting array's magnetic parameters

mu = np.array([0.0, 0.0, 1.0, 0.0, 0.0, -1.0])

Ha = 0
na = np.array([0,0,1])

dot = DoubleDot(R=R, h=h, Ms=Ms, d=d)
la = Lattice(a1=a1, a2=a2)
bulk = BulkWave(dot = dot, lattice = la, mu=mu, Bbias = np.array([0, 0, 0, 0, 0,0]), K_MAX=30)

# Setting points on the Brillouin zone to calculate
points = 4
G,X,M = la.GXM
vertices = [G, X, M, G]

K_Kmod = list(ContourIterator(vertices, points))
K, K_mod = zip(*K_Kmod)

#Perform calculations
result = calculate_bulk(bulk, K, K_mod, points, parallel=False)

result_should_be = BulkData.load_from_file("test/fixtures/bulk_CAFM.npz")

npt.assert_allclose(result.W/(gamma*mu0*Ms), result_should_be.W/(gamma*mu0*Ms), atol=1e-3)
npt.assert_allclose(result.J, result_should_be.J, atol=1e-3)
npt.assert_allclose(result.B, result_should_be.B, atol=1e-3)

norms = _calc_norms(result.V, result.J)
norms_should_be = _calc_norms(result_should_be.V, result_should_be.J)
npt.assert_allclose(norms, norms_should_be, atol=1e-3)
62 changes: 62 additions & 0 deletions test/test_bulk_FM.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import sys
sys.path.append("../..")

import unittest
import numpy as np
import scipy.constants as const

import numpy.testing as npt

mu0 = const.codata.value('mag. constant')
gamma = const.codata.value('electron gyromag. ratio')

from ..model.dot import Dot, DoubleDot
from ..model.lattice import Lattice, ContourIterator
from ..model.dotarray import BulkWave, magn_norm
from ..util.util import calculate_bulk, BulkData

def _calc_norms(V, J):
a, b_ = J.shape
norms = [magn_norm(m, J) for m in np.reshape(np.swapaxes(V,1,2), (-1,a))]
return norms

class TestBulkFM(unittest.TestCase):
def test_bulk_fm(self):

# Setting array's physical parameters

R = 1.0
a = 4.1*R
a1 = np.array([1.0*a, 0.0*a, 0.0])
a2 = np.array([0.0*a, 1.0*a, 0.0])
Ms = 1.0/mu0 # mu0 Ms = 1T
h = 5.0*R

# Setting array's magnetic parameters

mu = np.array([0.0,0.0,1.0])

dot = Dot(R=R, h=h, Ms=Ms)
la = Lattice(a1=a1, a2=a2)
bulk = BulkWave(dot = dot, lattice = la, mu=mu, K_MAX=30)

# Setting points on the Brillouin zone to calculate
points = 4
G,X,M = la.GXM
vertices = [G, X, M, G]

K_Kmod = list(ContourIterator(vertices, points))
K, K_mod = zip(*K_Kmod)

#Perform calculations
result = calculate_bulk(bulk, K, K_mod, points, parallel=False)

result_should_be = BulkData.load_from_file("test/fixtures/bulk_FM.npz")

npt.assert_allclose(result.W/(gamma*mu0*Ms), result_should_be.W/(gamma*mu0*Ms), atol=1e-3)
npt.assert_allclose(result.J, result_should_be.J, atol=1e-3)
npt.assert_allclose(result.B, result_should_be.B, atol=1e-3)

norms = _calc_norms(result.V, result.J)
norms_should_be = _calc_norms(result_should_be.V, result_should_be.J)
npt.assert_allclose(norms, norms_should_be, atol=1e-3)
53 changes: 53 additions & 0 deletions test/test_bulk_unstable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import sys
sys.path.append("../..")

import unittest
import numpy as np
import scipy.constants as const

import numpy.testing as npt

mu0 = const.codata.value('mag. constant')
gamma = const.codata.value('electron gyromag. ratio')

from ..model.dot import Dot, DoubleDot
from ..model.lattice import Lattice, ContourIterator
from ..model.dotarray import BulkWave, magn_norm, ArrayUnstable
from ..util.util import calculate_bulk, BulkData

def _calc_norms(V, J):
a, b_ = J.shape
norms = [magn_norm(m, J) for m in np.reshape(np.swapaxes(V,1,2), (-1,a))]
return norms

class TestBulkFM(unittest.TestCase):
def test_bulk_negative_norm(self):

# Setting array's physical parameters

R = 1.0
a = 4.1*R
a1 = np.array([1.0*a, 0.0*a, 0.0])
a2 = np.array([0.0*a, 1.0*a, 0.0])
Ms = 1.0/mu0 # mu0 Ms = 1T
h = 2.1*R

# Setting array's magnetic parameters

mu = np.array([0.0,0.0,1.0])

dot = Dot(R=R, h=h, Ms=Ms)
la = Lattice(a1=a1, a2=a2)
bulk = BulkWave(dot = dot, lattice = la, mu=mu, K_MAX=30)

# Setting points on the Brillouin zone to calculate
points = 4
G,X,M = la.GXM
vertices = [G, X, M, G]

K_Kmod = list(ContourIterator(vertices, points))
K, K_mod = zip(*K_Kmod)

#Perform calculations
with self.assertRaises(ArrayUnstable):
result = calculate_bulk(bulk, K, K_mod, points, parallel=False)
116 changes: 116 additions & 0 deletions test/test_dot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#!/usr/bin/env python3

import unittest
import copy
import numpy as np
import scipy.special as sf

import scipy.constants as const
mu0 = const.codata.value('mag. constant')

from ..model.dot import Dot, DoubleDot

def mod(k):
return np.sqrt(k.dot(k))

def norm(k):
return k.dot(k)

class TestInit(unittest.TestCase):
def test_cylinder(self):
R = 1.0
h = 30.0*R
Ms = 1.0
dot = Dot(R, h, Ms)
self.assertEqual(dot.h, h)
self.assertEqual(dot.R, R)
self.assertEqual(dot.Ms, Ms)
def test_cylinder_with_anisotropy(self):
R = 1.0
h = 30.0*R
Ms = 1.0
Ha = 1.0
na = np.array([1,0,0])
dot = Dot(R, h, Ms, anisotropy=(na,Ha))
K_should_be = -Ha*np.outer(na,na)/(2.0*mu0*Ms)
self.assertTrue(np.allclose(dot.K, K_should_be))


class TestElements(unittest.TestCase):
def setUp(self):
R = 1.0
params = dict(R = R,
h = 30.0*R,
Ms = 1.0,
)
self.params = params
def test_single_dot(self):
dot = Dot(**self.params)
e = [1,2,3,4]
Nk = dot.elements_to_matrix(e)
Nk_should_be = np.array(
[[1, 3, 0],
[3, 2, 0],
[0, 0, 4]]
)
self.assertTrue(np.allclose(Nk, Nk_should_be))
def test_double_dot(self):
d = np.array([1.0,0.0,0.0])
params = self.params.copy()
params['d'] = d
dot = DoubleDot(**params)
e = list(range(1,4*3+1))
Nk = dot.elements_to_matrix(e)
Nk_should_be = np.array(
[
[ 1, 3, 0, 5, 7, 0],
[ 3, 2, 0, 7, 6, 0],
[ 0, 0, 4, 0, 0, 8],
[ 9, 11, 0, 1, 3, 0],
[11, 10, 0, 3, 2, 0],
[ 0, 0, 12, 0, 0, 4],

]
)
self.assertTrue(np.allclose(Nk, Nk_should_be))

class TestNij(unittest.TestCase):
def setUp(self):
R = 1.0
h = 30.0*R
Ms = 1.0
self.dot = Dot(R, h, Ms)
self.k = np.array([1.1,2.3])
self.h = h
self.R = R
def test_N11(self):
dot = self.dot
self.assertEqual(dot.N11(np.array([0.0, 0.0, 0.0])), 0.0)
self.assertAlmostEqual(dot.N11(self.k), 0.0833650226208)
def test_N22(self):
dot = self.dot
self.assertEqual(dot.N22(np.array([0.0, 0.0, 0.0])), 0.0)
self.assertAlmostEqual(dot.N22(self.k), 0.364463611293)
def test_N12(self):
dot = self.dot
self.assertEqual(dot.N12(np.array([0.0, 0.0, 0.0])), 0.0)
self.assertAlmostEqual(dot.N12(self.k), 0.174308683662)
def test_N33(self):
dot = self.dot
self.assertEqual(dot.N33(np.array([0.0, 0.0, 0.0])), np.pi)
self.assertAlmostEqual(dot.N33(self.k), 0.0059326607591)
def test_fkh(self):
k = np.array([0.5, 0, 0])
kh = mod(k)*self.h
self.assertEqual(self.dot._fkh(0.0), 0.0)
self.assertEqual(self.dot._fkh(mod(k)), 1.0-(1.0-np.exp(-kh))/kh)
def test_Nk(self):
R = self.R
k = np.array([0.5, 0, 0])
kr = mod(k)*R
fkh = self.dot._fkh(mod(k))
N11 = (2.0*np.pi*R*R*sf.j1(kr)/kr)**2/(np.pi*R*R)*k[0]*k[0]/norm(k)*fkh
N33 = (2.0*np.pi*R*R*sf.j1(kr)/kr)**2/(np.pi*R*R)*(1.0-fkh)
self.assertAlmostEqual(self.dot.N11(k), N11)
self.assertAlmostEqual(self.dot.N33(k), N33)
self.assertAlmostEqual(self.dot.N22(k), 0.0)
56 changes: 56 additions & 0 deletions test/test_lattice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env python3

import numpy as np
import numpy.testing as npt
import unittest

from ..model.lattice import Lattice

class TestLattice(unittest.TestCase):
def test_properties(self):
a1 = np.array([2.0, 0.0, 0.0])
a2 = np.array([0.0, 0.5, 0.0])
la = Lattice(a1, a2)
npt.assert_allclose(a1, la.a1, atol=1e-7)
npt.assert_allclose(a2, la.a2, atol=1e-7)

def test_rectangular(self):
a1 = np.array([2.0, 0.0, 0.0])
a2 = np.array([0.0, 0.5, 0.0])
la = Lattice(a1, a2)
b1 = la.b1
b2 = la.b2
S0 = la.S0
self.assertTrue(np.allclose(b1, 2.0*np.pi*np.array([0.5, 0.0, 0.0])))
self.assertTrue(np.allclose(b2, 2.0*np.pi*np.array([0.0, 2.0, 0.0])))
self.assertEqual(S0, a1[0]*a2[1])

def test_triangular(self):
a1 = np.array([1.0, 0.0, 0.0])
a2 = np.array([0.5, np.sqrt(3.0)/2.0, 0.0])
la = Lattice(a1, a2)
b1 = la.b1
b2 = la.b2
S0 = la.S0
self.assertTrue(S0 - 0.5*np.sqrt(3) < 1e-8)
self.assertTrue(np.allclose(b1, 2.0*np.pi/S0*np.array([np.sqrt(3.0)/2.0, -0.5, 0.0])))
self.assertTrue(np.allclose(b2, 2.0*np.pi/S0*np.array([0.0, 1.0, 0.0])))

def test_arbitary(self):
a1 = np.array([1.4, 8.2, 0.0])
a2 = np.array([-2.2, 2.5, 0.0])
la = Lattice(a1, a2)
b1 = la.b1
b2 = la.b2
npt.assert_allclose([a1.dot(b2), a2.dot(b1)], np.zeros(2), atol=1e-7)
npt.assert_allclose([a1.dot(b1), a1.dot(b1)], [2*np.pi, 2*np.pi], atol=1e-7)

def test_GXM(self):
a1 = np.array([1.0, 0.0, 0.0])
a2 = np.array([0.0, 1.0, 0.0])
la = Lattice(a1, a2)
(G, X, M) = la.GXM
X_should_be = la.b1/2
M_should_be = (la.b1 + la.b2)/2
npt.assert_allclose(X,X_should_be)
npt.assert_allclose(M,M_should_be)
23 changes: 23 additions & 0 deletions test/test_toeplitz.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env python3

import unittest
import numpy as np
import numpy.testing as npt

from ..model.dotarray import _make_toeplitz_from_blocks

class TestToeplitz(unittest.TestCase):
def test_toeplitz(self):
B = [np.array([[i,i], [i,i]]) for i in range(-3, 4)]
T = _make_toeplitz_from_blocks(B)
T_should_be = np.array(
[[ 0, 0, 1, 1, 2, 2, 3, 3],
[ 0, 0, 1, 1, 2, 2, 3, 3],
[-1, -1, 0, 0, 1, 1, 2, 2],
[-1, -1, 0, 0, 1, 1, 2, 2],
[-2, -2, -1, -1, 0, 0, 1, 1],
[-2, -2, -1, -1, 0, 0, 1, 1],
[-3, -3, -2, -2, -1, -1, 0, 0],
[-3, -3, -2, -2, -1, -1, 0, 0]]
)
npt.assert_allclose(T, T_should_be)
27 changes: 27 additions & 0 deletions test/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env python3

import unittest

from ..util import *

class TestSetLimitValue(unittest.TestCase):
def test_arg(self):
@set_limit_value(0,0,0)
def fn(a):
return 1
self.assertEqual(fn(0), 0)
self.assertEqual(fn(1), 1)
def test_kwarg(self):
@set_limit_value("y", 1, 1)
def fn(y=0):
return 0
self.assertEqual(fn(y=0), 0)
self.assertEqual(fn(y=1), 1)

class TestNorm(unittest.TestCase):
def test_norm(self):
v = np.array([3.0,4.0])
self.assertEqual(norm(v), 25.0)
def test_mod(self):
v = np.array([3.0,4.0])
self.assertEqual(mod(v), 5.0)
5 changes: 5 additions & 0 deletions unittest.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[unittest]
#plugins = nose2.plugins.mp

[multiprocess]
always-on = True
1 change: 1 addition & 0 deletions util/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/usr/bin/env python3
# encoding: utf-8

from .util import *
27 changes: 16 additions & 11 deletions util/util.py
Original file line number Diff line number Diff line change
@@ -4,14 +4,14 @@
This file is part of MagDots.
(C) Ivan Lisenkov
Oakland Univerity,
Oakland Univerity,
Michigan, USA
2015
MagDots: Magnetization dynamics of nanoelement arrays
How to cite.
How to cite.
If you are using this program or/and produce scientific publications based on it,
we kindly ask you to cite it as:
@@ -99,7 +99,7 @@ def rotate(v, a):
M = np.array([[ np.cos(a), -np.sin(a), 0],
[ np.sin(a), np.cos(a), 0],
[ 0, 0, 1]])

return M.dot(v)


@@ -167,7 +167,7 @@ def save_to_file(self, filename):

np.savez(filename, K = self.K, K_mod = self.K_mod, W = self.W, points = self.points, V =
self.V, J = self.J, B = self.B)


class EdgeData:
def __init__(self, W, V, K, J, points, B):
@@ -200,14 +200,21 @@ def save_to_file(self, filename):



def calculate_bulk(bulk, K, K_mod, points):
def calculate_bulk(bulk, K, K_mod, points, parallel=True):



#Perform calculations
W, V = parallel_calculation(bulk, K)
if parallel:
W, V = parallel_calculation(bulk, K)
else:
W_V = list(map(bulk.omega, K))
W, V = zip(*W_V)
W = np.array(W)
V = np.array(V)


K = np.array(K)

J = bulk.J
B = bulk.B

@@ -216,13 +223,11 @@ def calculate_bulk(bulk, K, K_mod, points):

def calculate_edge(edge, K, points):


#Perform calculations
W, V = parallel_calculation(edge, K)

J = edge.J
B = edge.B

return EdgeData(W=W, V=V, K=K, J = J, points = points, B = B)