Exemplo n.º 1
0
    def create_tablewriter(self):

        entities = {
            'step': {'unit': '<>',
                     'get': lambda sim: sim.step,
                     'header': 'step'},
            'm': {'unit': '<>',
                  'get': lambda sim: sim.compute_average(),
                  'header': ('m_x', 'm_y', 'm_z')},
            'skx_num':{'unit': '<>', 
                      'get': lambda sim: sim.skyrmion_number(), 
                      'header': 'skx_num'}
        }

        self.saver = DataSaver(self, self.name + '.txt', entities=entities)

        self.saver.update_entity_order()
Exemplo n.º 2
0
    def __init__(self, mesh, name="unnamed", use_jac=False):
        """Simulation object.
        *Arguments*
          name : the Simulation name (used for writing data files, for examples)
        """

        self.t = 0
        self.name = name
        self.mesh = mesh
        self.n = mesh.n
        self.n_nonzero = mesh.n
        self.unit_length = mesh.unit_length
        self._alpha = np.zeros(self.n, dtype=np.float)
        self._mu_s = np.zeros(self.n, dtype=np.float)
        self._mu_s_inv = np.zeros(self.n, dtype=np.float)

        self.spin = np.ones(3 * self.n, dtype=np.float)
        self.spin_last = np.ones(3 * self.n, dtype=np.float)
        self._pins = np.zeros(self.n, dtype=np.int32)
        self.field = np.zeros(3 * self.n, dtype=np.float)
        self.dm_dt = np.zeros(3 * self.n, dtype=np.float)
        self._skx_number = np.zeros(self.n, dtype=np.float)

        self.interactions = []
        self.pin_fun = None

        self.step = 0

        self.saver = DataSaver(self, name + ".txt")

        self.saver.entities["E_total"] = {"unit": "<J>", "get": lambda sim: sim.compute_energy(), "header": "E_total"}

        self.saver.entities["m_error"] = {
            "unit": "<>",
            "get": lambda sim: sim.compute_spin_error(),
            "header": "m_error",
        }

        self.saver.entities["skx_num"] = {"unit": "<>", "get": lambda sim: sim.skyrmion_number(), "header": "skx_num"}

        self.saver.update_entity_order()

        # This is only for old C files using the xperiodic variable
        self.xperiodic, self.yperiodic = mesh.periodicity[0], mesh.periodicity[1]

        self.vtk = SaveVTK(self.mesh, name=name)

        if use_jac is not True:
            self.vode = cvode.CvodeSolver(self.spin, self.sundials_rhs)
        else:
            self.vode = cvode.CvodeSolver(self.spin, self.sundials_rhs, self.sundials_jtn)

        self.set_default_options()

        self.set_tols()
Exemplo n.º 3
0
    def __init__(self, mesh, name):

        self.name = name
        self.mesh = mesh
        self.n = mesh.n
        self.n_nonzero = mesh.n
        self.unit_length = mesh.unit_length

        self._magnetisation = np.zeros(self.n, dtype=np.float)
        # Inverse magnetisation
        self._magnetisation_inv = np.zeros(self.n, dtype=np.float)

        self.spin = np.ones(3 * self.n, dtype=np.float)
        self._pins = np.zeros(self.n, dtype=np.int32)
        self.field = np.zeros(3 * self.n, dtype=np.float)
        self._skx_number = np.zeros(self.n, dtype=np.float)
        self.interactions = []

        # This is for old C files codes using the xperiodic variables
        try:
            self.xperiodic, self.yperiodic, self.zperiodic = mesh.periodicity
        except ValueError:
            self.xperiodic, self.yperiodic = mesh.periodicity

        # To save the simulation data: ----------------------------------------

        self.data_saver = DataSaver(self, name + '.txt')

        self.data_saver.entities['E_total'] = {
            'unit': '<J>',
            'get': lambda sim: self.compute_energy(),
            'header': 'E_total'
        }

        self.data_saver.entities['m_error'] = {
            'unit': '<>',
            'get': lambda sim: self.compute_spin_error(),
            'header': 'm_error'
        }

        self.data_saver.update_entity_order()
Exemplo n.º 4
0
    def create_tablewriter(self):

        entities = {
            'step': {'unit': '<>',
                     'get': lambda sim: sim.step,
                     'header': 'step'},
            'm': {'unit': '<>',
                  'get': lambda sim: sim.compute_average(),
                  'header': ('m_x', 'm_y', 'm_z')},
            'skx_num':{'unit': '<>', 
                      'get': lambda sim: sim.skyrmion_number(), 
                      'header': 'skx_num'}
        }

        self.saver = DataSaver(self, self.name + '.txt', entities=entities)

        self.saver.update_entity_order()
Exemplo n.º 5
0
    def __init__(self, mesh, name):

        self.name = name
        self.mesh = mesh
        self.n = mesh.n
        self.n_nonzero = mesh.n
        self.unit_length = mesh.unit_length

        self._magnetisation = np.zeros(self.n, dtype=np.float)
        self.spin = np.ones(3 * self.n, dtype=np.float)
        self._pins = np.zeros(self.n, dtype=np.int32)
        self.field = np.zeros(3 * self.n, dtype=np.float)
        self._skx_number = np.zeros(self.n, dtype=np.float)
        self.interactions = []

        # This is for old C files codes using the xperiodic variables
        try:
            self.xperiodic, self.yperiodic, self.zperiodic = mesh.periodicity
        except ValueError:
            self.xperiodic, self.yperiodic = mesh.periodicity

        # To save the simulation data: ----------------------------------------

        self.data_saver = DataSaver(self, name + '.txt')

        self.data_saver.entities['E_total'] = {
            'unit': '<J>',
            'get': lambda sim: self.compute_energy(),
            'header': 'E_total'}

        self.data_saver.entities['m_error'] = {
            'unit': '<>',
            'get': lambda sim: self.compute_spin_error(),
            'header': 'm_error'}

        self.data_saver.update_entity_order()
Exemplo n.º 6
0
class LLG(object):
    def __init__(self, mesh, name="unnamed", use_jac=False):
        """Simulation object.
        *Arguments*
          name : the Simulation name (used for writing data files, for examples)
        """

        self.t = 0
        self.name = name
        self.mesh = mesh
        self.n = mesh.n
        self.n_nonzero = mesh.n
        self.unit_length = mesh.unit_length
        self._alpha = np.zeros(self.n, dtype=np.float)
        self._mu_s = np.zeros(self.n, dtype=np.float)
        self._mu_s_inv = np.zeros(self.n, dtype=np.float)

        self.spin = np.ones(3 * self.n, dtype=np.float)
        self.spin_last = np.ones(3 * self.n, dtype=np.float)
        self._pins = np.zeros(self.n, dtype=np.int32)
        self.field = np.zeros(3 * self.n, dtype=np.float)
        self.dm_dt = np.zeros(3 * self.n, dtype=np.float)
        self._skx_number = np.zeros(self.n, dtype=np.float)

        self.interactions = []
        self.pin_fun = None

        self.step = 0

        self.saver = DataSaver(self, name + ".txt")

        self.saver.entities["E_total"] = {"unit": "<J>", "get": lambda sim: sim.compute_energy(), "header": "E_total"}

        self.saver.entities["m_error"] = {
            "unit": "<>",
            "get": lambda sim: sim.compute_spin_error(),
            "header": "m_error",
        }

        self.saver.entities["skx_num"] = {"unit": "<>", "get": lambda sim: sim.skyrmion_number(), "header": "skx_num"}

        self.saver.update_entity_order()

        # This is only for old C files using the xperiodic variable
        self.xperiodic, self.yperiodic = mesh.periodicity[0], mesh.periodicity[1]

        self.vtk = SaveVTK(self.mesh, name=name)

        if use_jac is not True:
            self.vode = cvode.CvodeSolver(self.spin, self.sundials_rhs)
        else:
            self.vode = cvode.CvodeSolver(self.spin, self.sundials_rhs, self.sundials_jtn)

        self.set_default_options()

        self.set_tols()

    def set_default_options(self, gamma=1, mu_s=1, alpha=0.1):
        self.default_c = -1
        self._alpha[:] = alpha
        self._mu_s[:] = mu_s
        self.gamma = gamma
        self.do_procession = True

    def set_tols(self, rtol=1e-8, atol=1e-10):
        self.vode.set_options(rtol, atol)

    def set_options(self, rtol=1e-8, atol=1e-10):
        self.set_tols(rtol, atol)

    def set_m(self, m0=(1, 0, 0), normalise=True):

        self.spin[:] = helper.init_vector(m0, self.mesh, normalise)

        # TODO: carefully checking and requires to call set_mu first
        self.spin.shape = (-1, 3)
        for i in range(self.spin.shape[0]):
            if self._mu_s[i] == 0:
                self.spin[i, :] = 0
        self.spin.shape = (-1,)

        self.vode.set_initial_value(self.spin, self.t)

    def get_pins(self):
        return self._pins

    def set_pins(self, pin):
        self._pins[:] = helper.init_scalar(pin, self.mesh)

        for i in range(len(self._mu_s)):
            if self._mu_s[i] == 0.0:
                self._pins[i] = 1

    pins = property(get_pins, set_pins)

    def get_alpha(self):
        return self._alpha

    def set_alpha(self, value):
        self._alpha[:] = helper.init_scalar(value, self.mesh)

    alpha = property(get_alpha, set_alpha)

    def get_mu_s(self):
        return self._mu_s

    def set_mu_s(self, value):
        self._mu_s[:] = helper.init_scalar(value, self.mesh)
        nonzero = 0
        for i in range(self.n):
            if self._mu_s[i] > 0.0:
                self._mu_s_inv[i] = 1.0 / self._mu_s[i]
                nonzero += 1

        self.n_nonzero = nonzero

        for i in range(len(self._mu_s)):
            if self._mu_s[i] == 0.0:
                self._pins[i] = 1

    mu_s = property(get_mu_s, set_mu_s)

    def add(self, interaction, save_field=False):
        interaction.setup(self.mesh, self.spin, self._mu_s)

        # TODO: FIX
        for i in self.interactions:
            if i.name == interaction.name:
                interaction.name = i.name + "_2"

        self.interactions.append(interaction)

        energy_name = "E_{0}".format(interaction.name)
        self.saver.entities[energy_name] = {
            "unit": "<J>",
            "get": lambda sim: sim.get_interaction(interaction.name).compute_energy(),
            "header": energy_name,
        }

        if save_field:
            fn = "{0}".format(interaction.name)
            self.saver.entities[fn] = {
                "unit": "<>",
                "get": lambda sim: sim.get_interaction(interaction.name).average_field(),
                "header": ("%s_x" % fn, "%s_y" % fn, "%s_z" % fn),
            }

        self.saver.update_entity_order()

    def get_interaction(self, name):
        for interaction in self.interactions:
            if interaction.name == name:
                return interaction
        else:
            raise ValueError(
                "Failed to find the interaction with name '{0}', "
                "available interactions: {1}.".format(name, [x.name for x in self.interactions])
            )

    def run_until(self, t):

        if t <= self.t:
            if t == self.t and self.t == 0.0:
                self.compute_effective_field(t)
                self.saver.save()
            return

        ode = self.vode

        self.spin_last[:] = self.spin[:]

        flag = ode.run_until(t)

        if flag < 0:
            raise Exception("Run cython run_until failed!!!")

        self.spin[:] = ode.y[:]

        self.t = t
        self.step += 1

        # update field before saving data
        self.compute_effective_field(t)
        self.saver.save()

    def compute_effective_field(self, t):

        # self.spin[:] = y[:]

        self.field[:] = 0

        for obj in self.interactions:
            self.field += obj.compute_field(t)

    def compute_effective_field_jac(self, t, spin):

        # self.spin[:] = y[:]

        self.field[:] = 0

        for obj in self.interactions:
            if obj.jac is True:
                self.field += obj.compute_field(t, spin=spin)

    def sundials_rhs(self, t, y, ydot):

        self.t = t

        # already synchronized when call this funciton
        # self.spin[:]=y[:]

        self.compute_effective_field(t)

        clib.compute_llg_rhs(
            ydot, self.spin, self.field, self.alpha, self._pins, self.gamma, self.n, self.do_procession, self.default_c
        )

        # ydot[:] = self.dm_dt[:]

        return 0

    def sundials_jtn(self, mp, Jmp, t, m, fy):
        # we can not copy mp to self.spin since m and self.spin is one object.
        # self.spin[:] = mp[:]
        print "NO jac..........."
        self.compute_effective_field_jac(t, mp)
        clib.compute_llg_jtimes(
            Jmp, m, fy, mp, self.field, self.alpha, self._pins, self.gamma, self.n, self.do_procession, self.default_c
        )

        return 0

    def compute_average(self):
        self.spin.shape = (-1, 3)
        average = np.sum(self.spin, axis=0) / self.n_nonzero
        self.spin.shape = (-1,)
        return average

    def compute_energy(self):

        energy = 0

        for obj in self.interactions:
            energy += obj.compute_energy()

        return energy

    def skyrmion_number(self):
        nx = self.mesh.nx
        ny = self.mesh.ny
        nz = self.mesh.nz
        number = clib.compute_skymrion_number(self.spin, self._skx_number, nx, ny, nz, self.mesh.neighbours)
        return number

    def spin_at(self, i, j, k):
        """
        Returns the spin components of the spin at
        i-th position in the z direction, j-th position in the
        y direction and k-th position in x direction
        """

        nxyz = self.mesh.n

        index = 3 * self.mesh.index(i, j, k)

        # print self.spin.shape,nxy,nx,i1,i2,i3
        return np.array([self.spin[index], self.spin[index + 1], self.spin[index + 2]])

    def add_monitor_at(self, i, j, k, name="p"):
        """
        Save site spin with index (i,j,k) to txt file.
        """

        self.saver.entities[name] = {
            "unit": "<>",
            "get": lambda sim: sim.spin_at(i, j, k),
            "header": (name + "_x", name + "_y", name + "_z"),
        }

        self.saver.update_entity_order()

    def save_vtk(self):
        self.vtk.save_vtk(self.spin.reshape(-1, 3), self._mu_s, step=self.step)

    def save_m(self):
        if not os.path.exists("%s_npys" % self.name):
            os.makedirs("%s_npys" % self.name)
        name = "%s_npys/m_%g.npy" % (self.name, self.step)
        np.save(name, self.spin)

    def save_skx(self, vtk=False):
        if not os.path.exists("%s_skx_npys" % self.name):
            os.makedirs("%s_skx_npys" % self.name)
        name = "%s_skx_npys/m_%g.npy" % (self.name, self.step)
        np.save(name, self._skx_number)
        if vtk is True:
            self.vtk.save_vtk_scalar(self._skx_number, self.step)

    def stat(self):
        return self.vode.stat()

    def spin_length(self):
        self.spin.shape = (-1, 3)
        length = np.sqrt(np.sum(self.spin ** 2, axis=1))
        self.spin.shape = (-1,)
        return length

    def compute_spin_error(self):
        length = self.spin_length() - 1.0
        length[self._pins > 0] = 0
        return np.max(abs(length))

    def compute_dmdt(self, dt):
        m0 = self.spin_last
        m1 = self.spin
        dm = (m1 - m0).reshape((-1, 3))
        max_dm = np.max(np.sqrt(np.sum(dm ** 2, axis=1)))
        max_dmdt = max_dm / dt
        return max_dmdt

    def relax(self, dt=1e-11, stopping_dmdt=0.01, max_steps=1000, save_m_steps=100, save_vtk_steps=100):

        for i in range(0, max_steps + 1):

            cvode_dt = self.vode.get_current_step()

            increment_dt = dt

            if cvode_dt > dt:
                increment_dt = cvode_dt

            self.run_until(self.t + increment_dt)

            if save_vtk_steps is not None:
                if i % save_vtk_steps == 0:
                    self.save_vtk()
            if save_m_steps is not None:
                if i % save_m_steps == 0:
                    self.save_m()

            dmdt = self.compute_dmdt(increment_dt)

            print "step=%d, time=%g, max_dmdt=%g ode_step=%g" % (self.step, self.t, dmdt, cvode_dt)

            if dmdt < stopping_dmdt:
                break

        if save_m_steps is not None:
            self.save_m()

        if save_vtk_steps is not None:
            self.save_vtk()
Exemplo n.º 7
0
class MonteCarlo(object):
    def __init__(self, mesh, name='unnamed'):
        self.mesh = mesh
        self.name = name
        self.n = mesh.n
        self.n_nonzero = self.n

        self.ngbs = mesh.neighbours

        self._mu_s = np.zeros(self.n, dtype=np.float)
        self.spin = np.ones(3 * self.n, dtype=np.float)
        self.spin_last = np.ones(3 * self.n, dtype=np.float)

        self.random_spin = np.zeros(3 * self.n, dtype=np.float)
        self._H = np.zeros(3 * self.n, dtype=np.float)

        self._skx_number = np.zeros(self.n, dtype=np.float)
        self.interactions = []

        self.create_tablewriter()
        self.vtk = SaveVTK(self.mesh, name=name)

        self.hexagonal_mesh = False
        if mesh.mesh_type == 'hexagonal':
            self.hexagonal_mesh = True
            #FIX ME !!!!
            self.nngbs = np.copy(mesh.neighbours)
        else:
            self.nngbs = mesh.next_neighbours

        self.step = 0
        self.skx_num = 0
        self.mc = clib.monte_carlo()
        self.set_options()

    def set_options(self,
                    J=50.0 * const.k_B,
                    J1=0,
                    D=0,
                    D1=0,
                    Kc=0,
                    H=None,
                    seed=100,
                    T=10.0,
                    S=1):
        """
        J, D and Kc in units of Joule
        H in units of Tesla.
        S is the spin length
        """
        self.mc.set_seed(seed)
        self.J = J / const.k_B
        self.J1 = J1 / const.k_B
        self.D = D / const.k_B
        self.D1 = D1 / const.k_B
        self.T = T
        self.Kc = Kc / const.k_B
        self.mu_s = 1.0
        if H is not None:
            self._H[:] = helper.init_vector(H, self.mesh)
            self._H[:] = self._H[:] * const.mu_s_1 * S / const.k_B

    def create_tablewriter(self):

        entities = {
            'step': {
                'unit': '<>',
                'get': lambda sim: sim.step,
                'header': 'step'
            },
            'm': {
                'unit': '<>',
                'get': lambda sim: sim.compute_average(),
                'header': ('m_x', 'm_y', 'm_z')
            },
            'skx_num': {
                'unit': '<>',
                'get': lambda sim: sim.skyrmion_number(),
                'header': 'skx_num'
            }
        }

        self.saver = DataSaver(self, self.name + '.txt', entities=entities)

        self.saver.update_entity_order()

    def set_m(self, m0=(1, 0, 0), normalise=True):
        self.spin[:] = helper.init_vector(m0, self.mesh, normalise)

        # TODO: carefully checking and requires to call set_mu first

        self.spin.shape = (-1, 3)
        for i in range(self.spin.shape[0]):
            if self._mu_s[i] == 0:
                self.spin[i, :] = 0
        self.spin.shape = (-1, )

    def get_mu_s(self):
        return self._mu_s

    def set_mu_s(self, value):
        self._mu_s[:] = helper.init_scalar(value, self.mesh)
        nonzero = 0
        for i in range(self.n):
            if self._mu_s[i] > 0.0:
                nonzero += 1

        self.n_nonzero = nonzero

    mu_s = property(get_mu_s, set_mu_s)

    def compute_average(self):
        self.spin.shape = (-1, 3)
        average = np.sum(self.spin, axis=0) / self.n_nonzero
        self.spin.shape = (-1, )
        return average

    def skyrmion_number(self):
        nx = self.mesh.nx
        ny = self.mesh.ny
        nz = self.mesh.nz
        number = clib.compute_skyrmion_number(self.spin, self._skx_number, nx,
                                              ny, nz, self.mesh.neighbours,
                                              self.mesh.n_ngbs)
        self.skx_num = number
        return number

    def save_vtk(self):
        """
        Save a VTK file with the magnetisation vector field and magnetic
        moments as cell data. Magnetic moments are saved in units of
        Bohr magnetons

        NOTE: It is recommended to use a *cell to point data* filter in
        Paraview or Mayavi to plot the vector field
        """
        self.vtk.save_vtk(self.spin.reshape(-1, 3), self._mu_s, step=self.step)

    def save_m(self):
        if not os.path.exists('%s_npys' % self.name):
            os.makedirs('%s_npys' % self.name)
        name = '%s_npys/m_%g.npy' % (self.name, self.step)
        np.save(name, self.spin)

    def run(self,
            steps=1000,
            save_m_steps=100,
            save_vtk_steps=100,
            save_data_steps=1):

        if save_m_steps is not None:
            self.save_m()

        if save_vtk_steps is not None:
            self.save_vtk()

        for step in range(1, steps + 1):
            self.step = step
            self.mc.run_step(self.spin, self.random_spin, self.ngbs,
                             self.nngbs, self.mesh.n_ngbs, self.J, self.J1,
                             self.D, self.D1, self._H, self.Kc, self.n, self.T,
                             self.hexagonal_mesh)
            if save_data_steps is not None:
                if step % save_data_steps == 0:
                    self.saver.save()
                    print("step=%d, skyrmion number=%0.9g" %
                          (self.step, self.skx_num))

            if save_vtk_steps is not None:
                if step % save_vtk_steps == 0:
                    self.save_vtk()
            if save_m_steps is not None:
                if step % save_m_steps == 0:
                    self.save_m()
Exemplo n.º 8
0
    def __init__(self, mesh, name='unnamed', use_jac=False):
        """Simulation object.
        *Arguments*
          name : the Simulation name (used for writing data files, for examples)
        """

        self.t = 0
        self.name = name
        self.mesh = mesh
        self.n = mesh.n
        self.n_nonzero = mesh.n
        self.unit_length = mesh.unit_length
        self._alpha = np.zeros(self.n, dtype=np.float)
        self._mu_s = np.zeros(self.n, dtype=np.float)
        self._mu_s_inv = np.zeros(self.n, dtype=np.float)

        self.spin = np.ones(3 * self.n, dtype=np.float)
        self.spin_last = np.ones(3 * self.n, dtype=np.float)
        self._pins = np.zeros(self.n, dtype=np.int32)
        self.field = np.zeros(3 * self.n, dtype=np.float)
        self.dm_dt = np.zeros(3 * self.n, dtype=np.float)
        self._skx_number = np.zeros(self.n, dtype=np.float)

        self.interactions = []
        self.pin_fun = None

        self.step = 0

        self.saver = DataSaver(self, name + '.txt')

        self.saver.entities['E_total'] = {
            'unit': '<J>',
            'get': lambda sim: sim.compute_energy(),
            'header': 'E_total'
        }

        self.saver.entities['m_error'] = {
            'unit': '<>',
            'get': lambda sim: sim.compute_spin_error(),
            'header': 'm_error'
        }

        self.saver.entities['skx_num'] = {
            'unit': '<>',
            'get': lambda sim: sim.skyrmion_number(),
            'header': 'skx_num'
        }

        self.saver.update_entity_order()

        # This is only for old C files using the xperiodic variable
        self.xperiodic, self.yperiodic = mesh.periodicity[0], mesh.periodicity[
            1]

        self.vtk = SaveVTK(self.mesh, name=name)

        if use_jac is not True:
            self.vode = cvode.CvodeSolver(self.spin, self.sundials_rhs)
        else:
            self.vode = cvode.CvodeSolver(self.spin, self.sundials_rhs,
                                          self.sundials_jtn)

        self.set_default_options()

        self.set_tols()

        # When initialising the integrator in the self.vode call, the CVOde
        # class calls the set_initial_value function (with flag_m=0), which
        # initialises a new integrator and allocates memory in this process.
        # Now, when we set the magnetisation, we will use the same memory
        # setting this flag_m to 1, so instead of calling CVodeInit we call
        # CVodeReInit. If don't, memory is allocated in every call of set_m
        self.flag_m = 1
Exemplo n.º 9
0
class LLG(object):
    def __init__(self, mesh, name='unnamed', use_jac=False):
        """Simulation object.
        *Arguments*
          name : the Simulation name (used for writing data files, for examples)
        """

        self.t = 0
        self.name = name
        self.mesh = mesh
        self.n = mesh.n
        self.n_nonzero = mesh.n
        self.unit_length = mesh.unit_length
        self._alpha = np.zeros(self.n, dtype=np.float)
        self._mu_s = np.zeros(self.n, dtype=np.float)
        self._mu_s_inv = np.zeros(self.n, dtype=np.float)

        self.spin = np.ones(3 * self.n, dtype=np.float)
        self.spin_last = np.ones(3 * self.n, dtype=np.float)
        self._pins = np.zeros(self.n, dtype=np.int32)
        self.field = np.zeros(3 * self.n, dtype=np.float)
        self.dm_dt = np.zeros(3 * self.n, dtype=np.float)
        self._skx_number = np.zeros(self.n, dtype=np.float)

        self.interactions = []
        self.pin_fun = None

        self.step = 0

        self.saver = DataSaver(self, name + '.txt')

        self.saver.entities['E_total'] = {
            'unit': '<J>',
            'get': lambda sim: sim.compute_energy(),
            'header': 'E_total'
        }

        self.saver.entities['m_error'] = {
            'unit': '<>',
            'get': lambda sim: sim.compute_spin_error(),
            'header': 'm_error'
        }

        self.saver.entities['skx_num'] = {
            'unit': '<>',
            'get': lambda sim: sim.skyrmion_number(),
            'header': 'skx_num'
        }

        self.saver.update_entity_order()

        # This is only for old C files using the xperiodic variable
        self.xperiodic, self.yperiodic = mesh.periodicity[0], mesh.periodicity[
            1]

        self.vtk = SaveVTK(self.mesh, name=name)

        if use_jac is not True:
            self.vode = cvode.CvodeSolver(self.spin, self.sundials_rhs)
        else:
            self.vode = cvode.CvodeSolver(self.spin, self.sundials_rhs,
                                          self.sundials_jtn)

        self.set_default_options()

        self.set_tols()

        # When initialising the integrator in the self.vode call, the CVOde
        # class calls the set_initial_value function (with flag_m=0), which
        # initialises a new integrator and allocates memory in this process.
        # Now, when we set the magnetisation, we will use the same memory
        # setting this flag_m to 1, so instead of calling CVodeInit we call
        # CVodeReInit. If don't, memory is allocated in every call of set_m
        self.flag_m = 1

    def set_default_options(self, gamma=1, mu_s=1, alpha=0.1):
        self.default_c = -1
        self._alpha[:] = alpha
        self._mu_s[:] = mu_s
        self.gamma = gamma
        self.do_precession = True

    def reset_integrator(self, t=0):
        self.vode.reset(self.spin, t)
        self.t = t  # also reinitialise the simulation time and step
        self.step = 0

    def set_tols(self, rtol=1e-8, atol=1e-10):
        self.vode.set_options(rtol, atol)

    def set_options(self, rtol=1e-8, atol=1e-10):
        self.set_tols(rtol, atol)

    def set_m(self, m0=(1, 0, 0), normalise=True):

        self.spin[:] = helper.init_vector(m0, self.mesh, normalise)

        # TODO: carefully checking and requires to call set_mu first
        self.spin.shape = (-1, 3)
        for i in range(self.spin.shape[0]):
            if self._mu_s[i] == 0:
                self.spin[i, :] = 0
        self.spin.shape = (-1, )

        self.vode.set_initial_value(self.spin, self.t)

        if not self.flag_m:
            self.flag_m = 1

    def get_pins(self):
        return self._pins

    def set_pins(self, pin):
        self._pins[:] = helper.init_scalar(pin, self.mesh)

        for i in range(len(self._mu_s)):
            if self._mu_s[i] == 0.0:
                self._pins[i] = 1

    pins = property(get_pins, set_pins)

    def get_alpha(self):
        return self._alpha

    def set_alpha(self, value):
        self._alpha[:] = helper.init_scalar(value, self.mesh)

    alpha = property(get_alpha, set_alpha)

    def get_mu_s(self):
        return self._mu_s

    def set_mu_s(self, value):
        self._mu_s[:] = helper.init_scalar(value, self.mesh)
        nonzero = 0
        for i in range(self.n):
            if self._mu_s[i] > 0.0:
                self._mu_s_inv[i] = 1.0 / self._mu_s[i]
                nonzero += 1

        self.n_nonzero = nonzero

        for i in range(len(self._mu_s)):
            if self._mu_s[i] == 0.0:
                self._pins[i] = 1

    mu_s = property(get_mu_s, set_mu_s)

    def add(self, interaction, save_field=False):
        interaction.setup(self.mesh, self.spin, self._mu_s)

        # TODO: FIX
        for i in self.interactions:
            if i.name == interaction.name:
                interaction.name = i.name + '_2'

        self.interactions.append(interaction)

        energy_name = 'E_{0}'.format(interaction.name)
        self.saver.entities[energy_name] = {
            'unit':
            '<J>',
            'get':
            lambda sim: sim.get_interaction(interaction.name).compute_energy(),
            'header':
            energy_name
        }

        if save_field:
            fn = '{0}'.format(interaction.name)
            self.saver.entities[fn] = {
                'unit':
                '<>',
                'get':
                lambda sim: sim.get_interaction(interaction.name).
                average_field(),
                'header': ('%s_x' % fn, '%s_y' % fn, '%s_z' % fn)
            }

        self.saver.update_entity_order()

    def get_interaction(self, name):
        for interaction in self.interactions:
            if interaction.name == name:
                return interaction
        else:
            raise ValueError("Failed to find the interaction with name '{0}', "
                             "available interactions: {1}.".format(
                                 name, [x.name for x in self.interactions]))

    def run_until(self, t):

        if t <= self.t:
            if t == self.t and self.t == 0.0:
                self.compute_effective_field(t)
                self.saver.save()
            return

        ode = self.vode

        self.spin_last[:] = self.spin[:]

        flag = ode.run_until(t)

        if flag < 0:
            raise Exception("Run cython run_until failed!!!")

        self.spin[:] = ode.y[:]

        self.t = t
        self.step += 1

        # update field before saving data
        self.compute_effective_field(t)
        self.saver.save()

    def compute_effective_field(self, t):

        #self.spin[:] = y[:]

        self.field[:] = 0

        for obj in self.interactions:
            self.field += obj.compute_field(t)

    def compute_effective_field_jac(self, t, spin):

        #self.spin[:] = y[:]

        self.field[:] = 0

        for obj in self.interactions:
            if obj.jac is True:
                self.field += obj.compute_field(t, spin=spin)

    def sundials_rhs(self, t, y, ydot):

        self.t = t

        # already synchronized when call this funciton
        # self.spin[:]=y[:]

        self.compute_effective_field(t)

        clib.compute_llg_rhs(ydot, self.spin, self.field, self.alpha,
                             self._pins, self.gamma, self.n,
                             self.do_precession, self.default_c)

        #ydot[:] = self.dm_dt[:]

        return 0

    def sundials_jtn(self, mp, Jmp, t, m, fy):
        # we can not copy mp to self.spin since m and self.spin is one object.
        #self.spin[:] = mp[:]
        print('NO jac...........')
        self.compute_effective_field_jac(t, mp)
        clib.compute_llg_jtimes(Jmp, m, fy, mp, self.field, self.alpha,
                                self._pins, self.gamma, self.n,
                                self.do_precession, self.default_c)

        return 0

    def compute_average(self):
        self.spin.shape = (-1, 3)
        average = np.sum(self.spin, axis=0) / self.n_nonzero
        self.spin.shape = (-1, )
        return average

    def compute_energy(self):

        energy = 0

        for obj in self.interactions:
            energy += obj.compute_energy()

        return energy

    def skyrmion_number(self):
        nx = self.mesh.nx
        ny = self.mesh.ny
        nz = self.mesh.nz
        number = clib.compute_skyrmion_number(self.spin, self._skx_number, nx,
                                              ny, nz, self.mesh.neighbours)
        return number

    def spin_at(self, i, j, k):
        """
        Returns the spin components of the spin at
        i-th position in the z direction, j-th position in the
        y direction and k-th position in x direction
        """

        nxyz = self.mesh.n

        index = 3 * self.mesh.index(i, j, k)

        # print self.spin.shape,nxy,nx,i1,i2,i3
        return np.array(
            [self.spin[index], self.spin[index + 1], self.spin[index + 2]])

    def add_monitor_at(self, i, j, k, name='p'):
        """
        Save site spin with index (i,j,k) to txt file.
        """

        self.saver.entities[name] = {
            'unit': '<>',
            'get': lambda sim: sim.spin_at(i, j, k),
            'header': (name + '_x', name + '_y', name + '_z')
        }

        self.saver.update_entity_order()

    def save_vtk(self):
        """
        Save a VTK file with the magnetisation vector field and magnetic
        moments as cell data. Magnetic moments are saved in units of
        Bohr magnetons

        NOTE: It is recommended to use a *cell to point data* filter in
        Paraview or Mayavi to plot the vector field
        """
        self.vtk.save_vtk(self.spin.reshape(-1, 3),
                          self._mu_s / const.mu_B,
                          step=self.step)

    def save_m(self):
        if not os.path.exists('%s_npys' % self.name):
            os.makedirs('%s_npys' % self.name)
        name = '%s_npys/m_%g.npy' % (self.name, self.step)
        np.save(name, self.spin)

    def save_skx(self, vtk=False):
        if not os.path.exists('%s_skx_npys' % self.name):
            os.makedirs('%s_skx_npys' % self.name)
        name = '%s_skx_npys/m_%g.npy' % (self.name, self.step)
        np.save(name, self._skx_number)
        if vtk is True:
            self.vtk.save_vtk_scalar(self._skx_number, self.step)

    def stat(self):
        return self.vode.stat()

    def spin_length(self):
        self.spin.shape = (-1, 3)
        length = np.sqrt(np.sum(self.spin**2, axis=1))
        self.spin.shape = (-1, )
        return length

    def compute_spin_error(self):
        length = self.spin_length() - 1.0
        length[self._pins > 0] = 0
        return np.max(abs(length))

    def compute_dmdt(self, dt):
        m0 = self.spin_last
        m1 = self.spin
        dm = (m1 - m0).reshape((-1, 3))
        max_dm = np.max(np.sqrt(np.sum(dm**2, axis=1)))
        max_dmdt = max_dm / dt
        return max_dmdt

    def relax(self,
              dt=1e-11,
              stopping_dmdt=0.01,
              max_steps=1000,
              save_m_steps=100,
              save_vtk_steps=100):

        for i in range(0, max_steps + 1):

            cvode_dt = self.vode.get_current_step()

            increment_dt = dt

            if cvode_dt > dt:
                increment_dt = cvode_dt

            self.run_until(self.t + increment_dt)

            if save_vtk_steps is not None:
                if i % save_vtk_steps == 0:
                    self.save_vtk()
            if save_m_steps is not None:
                if i % save_m_steps == 0:
                    self.save_m()

            dmdt = self.compute_dmdt(increment_dt)

            print('step=%d, time=%0.3g, max_dmdt=%0.3g ode_step=%0.3g' %
                  (self.step, self.t, dmdt, cvode_dt))

            if dmdt < stopping_dmdt:
                break

        if save_m_steps is not None:
            self.save_m()

        if save_vtk_steps is not None:
            self.save_vtk()
Exemplo n.º 10
0
class SimBase(object):
    """

    A class with common methods and definitions for both micromagnetic and
    atomistic simulations

    """
    def __init__(self, mesh, name):

        self.name = name
        self.mesh = mesh
        self.n = mesh.n
        self.n_nonzero = mesh.n
        self.unit_length = mesh.unit_length

        self._magnetisation = np.zeros(self.n, dtype=np.float)
        self.spin = np.ones(3 * self.n, dtype=np.float)
        self._pins = np.zeros(self.n, dtype=np.int32)
        self.field = np.zeros(3 * self.n, dtype=np.float)
        self._skx_number = np.zeros(self.n, dtype=np.float)
        self.interactions = []

        # This is for old C files codes using the xperiodic variables
        try:
            self.xperiodic, self.yperiodic, self.zperiodic = mesh.periodicity
        except ValueError:
            self.xperiodic, self.yperiodic = mesh.periodicity

        # To save the simulation data: ----------------------------------------

        self.data_saver = DataSaver(self, name + '.txt')

        self.data_saver.entities['E_total'] = {
            'unit': '<J>',
            'get': lambda sim: self.compute_energy(),
            'header': 'E_total'}

        self.data_saver.entities['m_error'] = {
            'unit': '<>',
            'get': lambda sim: self.compute_spin_error(),
            'header': 'm_error'}

        self.data_saver.update_entity_order()

        # ---------------------------------------------------------------------

    def set_m(self, m0=(1, 0, 0),
              normalise=True):
        """

        Set the magnetisation/spin three dimensional vector field profile.

        ARGUMENTS:

        m0      :: * To set every spin with the same direction,
                   set this value as a 3 elements tuple or list.

                   * For a spatially dependent vector field, you can specify a
                   function that returns a 3 element list depending on the
                   spatial coordinates. For example, a magnetisation field that
                   depends on the x position:

                        def m_profile(r):
                            for r[0] > 2:
                                return (0, 0, 1)
                            else:
                                return (0, 0, -1)

                   * You can also manually specify an array with (3 * n)
                   elements with the spins directions in the following order:

                        [mx_0 my_0 mz_0 mx_1 my_1 ... mx_n, my_n, mz_n]

                   where n is the number of mesh nodes and the order of the
                   magnetisation vectors follow the same order than the mesh
                   coordinates array.

                   * Alternatively, if you previously saved the magnetisation
                   field array to a numpy file, you can load it using
                   numpy.load(my_array)

        """

        self.spin[:] = helper.init_vector(m0, self.mesh, normalise)

        # TODO: carefully checking and requires to call set_mu first

        # Set the magnetisation/spin direction to (0, 0, 0) for sites
        # with no material, i.e. M_s = 0 or mu_s = 0
        # TODO: Check for atomistic and micromagnetic cases
        self.spin.shape = (-1, 3)
        for i in range(self.spin.shape[0]):
            if self._magnetisation[i] == 0:
                self.spin[i, :] = 0
        self.spin.shape = (-1,)

        # Set the initial state for the Sundials integrator using the
        # spins array
        self.driver.integrator.set_initial_value(self.spin, self.driver.t)

    def get_pins(self):
        """
        Returns the array with pinned spins in the sample:
        sites with 0 are unpinned and sites with 1 are pinned. The order
        of the array follows the same order than the mesh coordinates
        """
        return self._pins

    def set_pins(self, pin):
        """

        An scalar field with values 1 or 0 to specify mesh/lattice sites
        with pinned or unpinned magnetic moments, respectively

        ARGUMENTS:

        pin     :: * You can specify a function that returns 1 or 0 depending
                   on the spatial coordinates. For example, to pin the spins
                   in a range in the x direction:

                        def pin_profile(r):
                            for r[0] > 2 and r[0] < 4:
                                return 1
                            else:
                                return 0

                   * You can also manually specify an array with n elements (1
                   or 0) with the pinned/unpinned values in the same order than
                   the mesh coordinates array.

                   * Alternatively, if you previously saved the pin
                   field array to a numpy file, you can load it using
                   numpy.load(my_array)
        """
        self._pins[:] = helper.init_scalar(pin, self.mesh)

        # Sites with no material, i.e. Mu_s or mu_s equal to zero,
        # will be pinned
        for i in range(len(self._magnetisation)):
            if self._magnetisation[i] == 0.0:
                self._pins[i] = 1

    pins = property(get_pins, set_pins)

    def add(self, interaction, save_field=False):
        """

        Add an interaction from one of the Energy subclasses. By default,
        the average energy of the added interaction is saved to the
        data file when relaxing the system

        OPTIONAL ARGUMENTS:

        save_field      :: Set True to save the average values of this
                           interaction field when relaxing the system

        """

        # magnetisation is Ms for the micromagnetic Sim class, and it is
        # mu_s for the atomistic Sim class
        interaction.setup(self.mesh, self.spin,
                          self._magnetisation
                          )

        # TODO: FIX  --> ??
        # When adding an interaction that was previously added, using
        # the same name, append a '_2' to the new interaction name (?)
        for i in self.interactions:
            if i.name == interaction.name:
                interaction.name = i.name + '_2'

        self.interactions.append(interaction)

        # Specify a name for the energy of the interaction, which will
        # appear in a file with saved values
        # When saving the energy values, we call the compute_energy() method
        # from the (micromagnetic/atomistic) Energy class (overhead?)
        energy_name = 'E_{0}'.format(interaction.name)
        self.data_saver.entities[energy_name] = {
            'unit': '<J>',
            'get': lambda sim: sim.get_interaction(interaction.name).compute_energy(),
            'header': energy_name}

        # Save the average values of the interaction vector field components
        if save_field:
            fn = '{0}'.format(interaction.name)
            self.data_saver.entities[fn] = {
                'unit': '<>',
                'get': lambda sim: sim.get_interaction(interaction.name).average_field(),
                'header': ('%s_x' % fn, '%s_y' % fn, '%s_z' % fn)}

        self.data_saver.update_entity_order()

    def get_interaction(self, name):
        """
        Returns an instance of a magnetic interaction previously added
        to the simulation, using the corresponding interaction name as
        a string
        """
        for interaction in self.interactions:
            if interaction.name == name:
                return interaction
        else:
            raise ValueError("Failed to find the interaction with name '{0}', "
                             "available interactions: {1}.".format(
                                 name, [x.name for x in self.interactions]))

    def skyrmion_number(self):
        pass

    def spin_at(self, i, j, k):
        """
        Returns the x,y,z components of a spin in the [i, j, k]
        position of the mesh, where i,j,k are integer indexes. The index
        ordering is specified in the mesh class.
        """

        i1 = 3 * self.mesh.index(i, j, k)

        # print self.spin.shape,nxy,nx,i1,i2,i3
        return np.array([self.spin[i1],
                         self.spin[i1 + 1],
                         self.spin[i1 + 2]])

    def add_monitor_at(self, i, j, k, name='p'):
        """
        Save site spin with index (i,j,k) to txt file.
        """

        self.data_saver.entities[name] = {
            'unit': '<>',
            'get': lambda sim: sim.spin_at(i, j, k),
            'header': (name + '_x', name + '_y', name + '_z')}

        self.data_saver.update_entity_order()

    def spin_length(self):
        """
        Returns an array with the length of every spin in the mesh. The
        order is given by the mesh.coordinates order
        """
        self.spin.shape = (-1, 3)
        length = np.sqrt(np.sum(self.spin ** 2, axis=1))
        self.spin.shape = (-1,)
        return length

    def compute_spin_error(self):
        length = self.spin_length() - 1.0
        length[self._pins > 0] = 0
        return np.max(abs(length))

    def compute_average(self):
        """
        Compute the average values of the 3 components of the magnetisation
        vector field
        """
        self.spin.shape = (-1, 3)
        average = np.sum(self.spin, axis=0) / self.n_nonzero
        self.spin.shape = (3 * self.n)
        return average

    def compute_energy(self):
        """
        Compute the total energy of the magnetic system
        """
        energy = 0

        for obj in self.interactions:
            energy += obj.compute_energy()

        return energy

    def get_field_array(self, interaction):
        """
        Returns the field array corresponding to the interaction given:

        e.g.
            compute_interaction_field('Demag')
        returns a numpy array containing the Demag field.
        """
        field = self.get_interaction(interaction)
        # Copy here to avoid destroying the field accidentally
        # e.g. through reshaping
        f = field.field.copy()
        return f
Exemplo n.º 11
0
class LLG(object):

    def __init__(self, mesh, name='unnamed', integrator='sundials'):
        """Simulation object.

        *Arguments*

          name : the Simulation name (used for writing data files, for examples)

        """

        self.t = 0
        self.name = name
        self.mesh = mesh
        self.n = mesh.n
        self.n_nonzero = mesh.n
        self.unit_length = mesh.unit_length
        self._alpha = np.zeros(self.n, dtype=np.float)
        self._Ms = np.zeros(self.n, dtype=np.float)
        self._Ms_inv = np.zeros(self.n, dtype=np.float)
        self.spin = np.ones(3 * self.n, dtype=np.float)
        self.spin_last = np.ones(3 * self.n, dtype=np.float)
        self._pins = np.zeros(self.n, dtype=np.int32)
        self.field = np.zeros(3 * self.n, dtype=np.float)
        self.dm_dt = np.zeros(3 * self.n, dtype=np.float)
        self._skx_number = np.zeros(self.n, dtype=np.float)
        self.interactions = []
        self.pin_fun = None
        self.integrator_tolerances_set = False
        self.step = 0

        if integrator == "sundials":
            self.integrator = SundialsIntegrator(self.spin, self.sundials_rhs)
        elif integrator == "euler" or integrator == "rk4":
            self.integrator = StepIntegrator(self.spin, self.step_rhs, integrator)
        else:
            raise NotImplemented("integrator must be sundials, euler or rk4")

        self.saver = DataSaver(self, name + '.txt')

        self.saver.entities['E_total'] = {
            'unit': '<J>',
            'get': lambda sim: sim.compute_energy(),
            'header': 'E_total'}

        self.saver.entities['m_error'] = {
            'unit': '<>',
            'get': lambda sim: sim.compute_spin_error(),
            'header': 'm_error'}

        self.saver.entities['skx_num'] = {
            'unit': '<>',
            'get': lambda sim: sim.skyrmion_number(),
            'header': 'skx_num'}

        self.saver.entities['rhs_evals'] = {
            'unit': '<>',
            'get': lambda sim: self.integrator.rhs_evals,
            'header': 'rhs_evals'}

        self.saver.entities['real_time'] = {
            'unit': '<s>',
            'get': lambda _: time.time(),  # seconds since epoch
            'header': 'real_time'}

        self.saver.update_entity_order()

        # This is for old C files codes using the xperiodic variables
        self.xperiodic, self.yperiodic, self.zperiodic = mesh.periodicity

        self.vtk = SaveVTK(self.mesh, name=name)

        self.set_default_options()

    def set_default_options(self, gamma=2.21e5, Ms=8.0e5, alpha=0.1):
        self.default_c = 1e11
        self._alpha[:] = alpha
        self._Ms[:] = Ms
        self.gamma = gamma
        self.do_procession = True

    def reset_integrator(self, t=0):
        self.integrator.reset(self.spin, t)
        self.t = t # also reinitialise the simulation time and step
        self.step = 0

    def set_tols(self, rtol=1e-8, atol=1e-10, max_ord=None, reset=True):
        if max_ord is not None:
            self.integrator.set_tols(rtol=rtol, atol=atol, max_ord=max_ord)
        else:
            # not all integrators have max_ord (only VODE does)
            # and we don't want to encode a default value here either
            self.integrator.set_tols(rtol=rtol, atol=atol)
        if reset:
            self.reset_integrator(self.t)

    def set_m(self, m0=(1, 0, 0), normalise=True):

        self.spin[:] = helper.init_vector(m0, self.mesh, normalise)

        # TODO: carefully checking and requires to call set_mu first
        self.spin.shape = (-1, 3)
        for i in range(self.spin.shape[0]):
            if self._Ms[i] == 0:
                self.spin[i, :] = 0
        self.spin.shape = (-1,)

        self.integrator.set_initial_value(self.spin, self.t)

    def get_pins(self):
        return self._pins

    def set_pins(self, pin):
        self._pins[:] = helper.init_scalar(pin, self.mesh)

        for i in range(len(self._Ms)):
            if self._Ms[i] == 0.0:
                self._pins[i] = 1

    pins = property(get_pins, set_pins)

    def get_alpha(self):
        return self._alpha

    def set_alpha(self, value):
        self._alpha[:] = helper.init_scalar(value, self.mesh)

    alpha = property(get_alpha, set_alpha)

    def get_Ms(self):
        return self._Ms

    def set_Ms(self, value):
        self._Ms[:] = helper.init_scalar(value, self.mesh)
        nonzero = 0
        for i in range(self.n):
            if self._Ms[i] > 0.0:
                self._Ms_inv = 1.0 / self._Ms[i]
                nonzero += 1

        self.n_nonzero = nonzero

        for i in range(len(self._Ms)):
            if self._Ms[i] == 0.0:
                self._pins[i] = 1

        self.Ms_const = np.max(self._Ms)

    Ms = property(get_Ms, set_Ms)

    def add(self, interaction, save_field=False):
        interaction.setup(self.mesh, self.spin, Ms=self._Ms)

        # TODO: FIX
        for i in self.interactions:
            if i.name == interaction.name:
                interaction.name = i.name + '_2'

        self.interactions.append(interaction)

        energy_name = 'E_{0}'.format(interaction.name)
        self.saver.entities[energy_name] = {
            'unit': '<J>',
            'get': lambda sim: sim.get_interaction(interaction.name).compute_energy(),
            'header': energy_name}

        if save_field:
            fn = '{0}'.format(interaction.name)
            self.saver.entities[fn] = {
                'unit': '<>',
                'get': lambda sim: sim.get_interaction(interaction.name).average_field(),
                'header': ('%s_x' % fn, '%s_y' % fn, '%s_z' % fn)}

        self.saver.update_entity_order()

    def get_interaction(self, name):
        for interaction in self.interactions:
            if interaction.name == name:
                return interaction
        else:
            raise ValueError("Failed to find the interaction with name '{0}', "
                             "available interactions: {1}.".format(
                                 name, [x.name for x in self.interactions]))

    def run_until(self, t):
        if t <= self.t:
            if t == self.t and self.t == 0.0:
                self.compute_effective_field(t)
                self.saver.save()
            return

        self.spin_last[:] = self.spin[:]

        flag = self.integrator.run_until(t)
        if flag < 0:
            raise Exception("Run cython run_until failed!!!")

        self.spin[:] = self.integrator.y[:]
        self.t = t
        self.step += 1

        self.compute_effective_field(t) # update fields before saving data
        self.saver.save()

    def compute_effective_field(self, t):

        #self.spin[:] = y[:]

        self.field[:] = 0

        for obj in self.interactions:
            self.field += obj.compute_field(t)

    def sundials_rhs(self, t, y, ydot):

        self.t = t

        # already synchronized when call this funciton
        # self.spin[:]=y[:]

        self.compute_effective_field(t)

        clib.compute_llg_rhs(ydot,
                             self.spin,
                             self.field,
                             self.alpha,
                             self._pins,
                             self.gamma,
                             self.n,
                             self.do_procession,
                             self.default_c)

        #ydot[:] = self.dm_dt[:]

        return 0

    def step_rhs(self, t, y):
        self.t = t
        self.compute_effective_field(t)
        clib.compute_llg_rhs(self.dm_dt,
                             self.spin,
                             self.field,
                             self.alpha,
                             self._pins,
                             self.gamma,
                             self.n,
                             self.do_procession,
                             self.default_c)
        return self.dm_dt

    def compute_average(self):
        self.spin.shape = (-1, 3)
        average = np.sum(self.spin, axis=0) / self.n_nonzero
        self.spin.shape = (3 * self.n)
        return average

    def compute_energy(self):

        energy = 0

        for obj in self.interactions:
            energy += obj.compute_energy()

        return energy

    def skyrmion_number(self):
        nx = self.mesh.nx
        ny = self.mesh.ny
        nz = self.mesh.nz
        number = clib.compute_skymrion_number(
            self.spin, self._skx_number, nx, ny, nz, self.mesh.neighbours)
        return number

    def spin_at(self, i, j, k):

        i1 = 3 * self.mesh.index(i, j, k)

        # print self.spin.shape,nxy,nx,i1,i2,i3
        return np.array([self.spin[i1],
                         self.spin[i1 + 1],
                         self.spin[i1 + 2]])

    def add_monitor_at(self, i, j, k, name='p'):
        """
        Save site spin with index (i,j,k) to txt file.
        """

        self.saver.entities[name] = {
            'unit': '<>',
            'get': lambda sim: sim.spin_at(i, j, k),
            'header': (name + '_x', name + '_y', name + '_z')}

        self.saver.update_entity_order()

    def save_vtk(self):
        self.vtk.save_vtk(self.spin.reshape(-1, 3), self.Ms, step=self.step)

    def save_m(self):
        if not os.path.exists('%s_npys' % self.name):
            os.makedirs('%s_npys' % self.name)
        name = '%s_npys/m_%g.npy' % (self.name, self.step)
        np.save(name, self.spin)

    def save_skx(self):
        if not os.path.exists('%s_skx_npys' % self.name):
            os.makedirs('%s_skx_npys' % self.name)
        name = '%s_skx_npys/m_%g.npy' % (self.name, self.step)
        np.save(name, self._skx_number)

    def stat(self):
        return self.integrator.stat()

    def spin_length(self):
        self.spin.shape = (3, -1)
        length = np.sqrt(np.sum(self.spin**2, axis=0))
        self.spin.shape = (-1,)
        return length

    def compute_spin_error(self):
        length = self.spin_length() - 1.0
        length[self._pins > 0] = 0
        return np.max(abs(length))

    def compute_dmdt(self, dt):
        m0 = self.spin_last
        m1 = self.spin
        dm = (m1 - m0).reshape((3, -1))
        max_dm = np.max(np.sqrt(np.sum(dm**2, axis=0)))
        max_dmdt = max_dm / dt
        return max_dmdt

    def relax(self, dt=1e-11, stopping_dmdt=0.01, max_steps=1000,
              save_m_steps=100, save_vtk_steps=100):

        ONE_DEGREE_PER_NS = 17453292.52

        for i in range(0, max_steps + 1):

            cvode_dt = self.integrator.get_current_step()

            increment_dt = dt

            if cvode_dt > dt:
                increment_dt = cvode_dt

            self.run_until(self.t + increment_dt)

            if save_vtk_steps is not None:
                if i % save_vtk_steps == 0:
                    self.save_vtk()
            if save_m_steps is not None:
                if i % save_m_steps == 0:
                    self.save_m()

            dmdt = self.compute_dmdt(increment_dt)

            print 'step=%d, time=%g, max_dmdt=%g ode_step=%g' % (self.step,
                                                                 self.t,
                                                                 dmdt / ONE_DEGREE_PER_NS,
                                                                 cvode_dt)

            if dmdt < stopping_dmdt * ONE_DEGREE_PER_NS:
                break

        if save_m_steps is not None:
            self.save_m()

        if save_vtk_steps is not None:
            self.save_vtk()
Exemplo n.º 12
0
Arquivo: llg.py Projeto: owlas/fidimag
class LLG(object):

    def __init__(self, mesh, name='unnamed', integrator='sundials'):
        """Simulation object.

        *Arguments*

          name : the Simulation name (used for writing data files, for examples)

        """

        self.t = 0
        self.name = name
        self.mesh = mesh
        self.n = mesh.n
        self.n_nonzero = mesh.n
        self.unit_length = mesh.unit_length
        self._alpha = np.zeros(self.n, dtype=np.float)
        self._Ms = np.zeros(self.n, dtype=np.float)
        self._Ms_inv = np.zeros(self.n, dtype=np.float)
        self.spin = np.ones(3 * self.n, dtype=np.float)
        self.spin_last = np.ones(3 * self.n, dtype=np.float)
        self._pins = np.zeros(self.n, dtype=np.int32)
        self.field = np.zeros(3 * self.n, dtype=np.float)
        self.dm_dt = np.zeros(3 * self.n, dtype=np.float)
        self._skx_number = np.zeros(self.n, dtype=np.float)
        self.interactions = []
        self.pin_fun = None
        self.integrator_tolerances_set = False
        self.step = 0

        if integrator == "sundials":
            self.integrator = SundialsIntegrator(self.spin, self.sundials_rhs)
        elif integrator == "euler" or integrator == "rk4":
            self.integrator = StepIntegrator(self.spin, self.step_rhs, integrator)
        else:
            raise NotImplemented("integrator must be sundials, euler or rk4")

        self.saver = DataSaver(self, name + '.txt')

        self.saver.entities['E_total'] = {
            'unit': '<J>',
            'get': lambda sim: sim.compute_energy(),
            'header': 'E_total'}

        self.saver.entities['m_error'] = {
            'unit': '<>',
            'get': lambda sim: sim.compute_spin_error(),
            'header': 'm_error'}

        self.saver.entities['skx_num'] = {
            'unit': '<>',
            'get': lambda sim: sim.skyrmion_number(),
            'header': 'skx_num'}

        self.saver.entities['rhs_evals'] = {
            'unit': '<>',
            'get': lambda sim: self.integrator.rhs_evals,
            'header': 'rhs_evals'}

        self.saver.entities['real_time'] = {
            'unit': '<s>',
            'get': lambda _: time.time(),  # seconds since epoch
            'header': 'real_time'}

        self.saver.update_entity_order()

        # This is for old C files codes using the xperiodic variables
        self.xperiodic, self.yperiodic, self.zperiodic = mesh.periodicity

        self.vtk = SaveVTK(self.mesh, name=name)

        self.set_default_options()

    def set_default_options(self, gamma=2.21e5, Ms=8.0e5, alpha=0.1):
        self.default_c = 1e11
        self._alpha[:] = alpha
        self._Ms[:] = Ms
        self.gamma = gamma
        self.do_precession = True

    def reset_integrator(self, t=0):
        self.integrator.reset(self.spin, t)
        self.t = t # also reinitialise the simulation time and step
        self.step = 0

    def set_tols(self, rtol=1e-8, atol=1e-10, max_ord=None, reset=True):
        if max_ord is not None:
            self.integrator.set_tols(rtol=rtol, atol=atol, max_ord=max_ord)
        else:
            # not all integrators have max_ord (only VODE does)
            # and we don't want to encode a default value here either
            self.integrator.set_tols(rtol=rtol, atol=atol)
        if reset:
            self.reset_integrator(self.t)

    def set_m(self, m0=(1, 0, 0), normalise=True):

        self.spin[:] = helper.init_vector(m0, self.mesh, normalise)

        # TODO: carefully checking and requires to call set_mu first
        self.spin.shape = (-1, 3)
        for i in range(self.spin.shape[0]):
            if self._Ms[i] == 0:
                self.spin[i, :] = 0
        self.spin.shape = (-1,)

        self.integrator.set_initial_value(self.spin, self.t)

    def get_pins(self):
        return self._pins

    def set_pins(self, pin):
        self._pins[:] = helper.init_scalar(pin, self.mesh)

        for i in range(len(self._Ms)):
            if self._Ms[i] == 0.0:
                self._pins[i] = 1

    pins = property(get_pins, set_pins)

    def get_alpha(self):
        return self._alpha

    def set_alpha(self, value):
        self._alpha[:] = helper.init_scalar(value, self.mesh)

    alpha = property(get_alpha, set_alpha)

    def get_Ms(self):
        return self._Ms

    def set_Ms(self, value):
        self._Ms[:] = helper.init_scalar(value, self.mesh)
        nonzero = 0
        for i in range(self.n):
            if self._Ms[i] > 0.0:
                self._Ms_inv = 1.0 / self._Ms[i]
                nonzero += 1

        self.n_nonzero = nonzero

        for i in range(len(self._Ms)):
            if self._Ms[i] == 0.0:
                self._pins[i] = 1

        self.Ms_const = np.max(self._Ms)

    Ms = property(get_Ms, set_Ms)

    def add(self, interaction, save_field=False):
        interaction.setup(self.mesh, self.spin, Ms=self._Ms)

        # TODO: FIX
        for i in self.interactions:
            if i.name == interaction.name:
                interaction.name = i.name + '_2'

        self.interactions.append(interaction)

        energy_name = 'E_{0}'.format(interaction.name)
        self.saver.entities[energy_name] = {
            'unit': '<J>',
            'get': lambda sim: sim.get_interaction(interaction.name).compute_energy(),
            'header': energy_name}

        if save_field:
            fn = '{0}'.format(interaction.name)
            self.saver.entities[fn] = {
                'unit': '<>',
                'get': lambda sim: sim.get_interaction(interaction.name).average_field(),
                'header': ('%s_x' % fn, '%s_y' % fn, '%s_z' % fn)}

        self.saver.update_entity_order()

    def get_interaction(self, name):
        for interaction in self.interactions:
            if interaction.name == name:
                return interaction
        else:
            raise ValueError("Failed to find the interaction with name '{0}', "
                             "available interactions: {1}.".format(
                                 name, [x.name for x in self.interactions]))

    def run_until(self, t):
        if t <= self.t:
            if t == self.t and self.t == 0.0:
                self.compute_effective_field(t)
                self.saver.save()
            return

        self.spin_last[:] = self.spin[:]

        flag = self.integrator.run_until(t)
        if flag < 0:
            raise Exception("Run cython run_until failed!!!")

        self.spin[:] = self.integrator.y[:]
        self.t = t
        self.step += 1

        self.compute_effective_field(t) # update fields before saving data
        self.saver.save()

    def compute_effective_field(self, t):

        #self.spin[:] = y[:]

        self.field[:] = 0

        for obj in self.interactions:
            self.field += obj.compute_field(t)

    def sundials_rhs(self, t, y, ydot):

        self.t = t

        # already synchronized when call this funciton
        # self.spin[:]=y[:]

        self.compute_effective_field(t)

        clib.compute_llg_rhs(ydot,
                             self.spin,
                             self.field,
                             self.alpha,
                             self._pins,
                             self.gamma,
                             self.n,
                             self.do_precession,
                             self.default_c)

        #ydot[:] = self.dm_dt[:]

        return 0

    def step_rhs(self, t, y):
        self.t = t
        self.compute_effective_field(t)
        clib.compute_llg_rhs(self.dm_dt,
                             self.spin,
                             self.field,
                             self.alpha,
                             self._pins,
                             self.gamma,
                             self.n,
                             self.do_precession,
                             self.default_c)
        return self.dm_dt

    def compute_average(self):
        self.spin.shape = (-1, 3)
        average = np.sum(self.spin, axis=0) / self.n_nonzero
        self.spin.shape = (3 * self.n)
        return average

    def compute_energy(self):

        energy = 0

        for obj in self.interactions:
            energy += obj.compute_energy()

        return energy

    def skyrmion_number(self):
        nx = self.mesh.nx
        ny = self.mesh.ny
        nz = self.mesh.nz
        number = clib.compute_skymrion_number(
            self.spin, self._skx_number, nx, ny, nz, self.mesh.neighbours)
        return number

    def spin_at(self, i, j, k):

        i1 = 3 * self.mesh.index(i, j, k)

        # print self.spin.shape,nxy,nx,i1,i2,i3
        return np.array([self.spin[i1],
                         self.spin[i1 + 1],
                         self.spin[i1 + 2]])

    def add_monitor_at(self, i, j, k, name='p'):
        """
        Save site spin with index (i,j,k) to txt file.
        """

        self.saver.entities[name] = {
            'unit': '<>',
            'get': lambda sim: sim.spin_at(i, j, k),
            'header': (name + '_x', name + '_y', name + '_z')}

        self.saver.update_entity_order()

    def save_vtk(self):
        self.vtk.save_vtk(self.spin.reshape(-1, 3), self.Ms, step=self.step)

    def save_m(self):
        if not os.path.exists('%s_npys' % self.name):
            os.makedirs('%s_npys' % self.name)
        name = '%s_npys/m_%g.npy' % (self.name, self.step)
        np.save(name, self.spin)

    def save_skx(self):
        if not os.path.exists('%s_skx_npys' % self.name):
            os.makedirs('%s_skx_npys' % self.name)
        name = '%s_skx_npys/m_%g.npy' % (self.name, self.step)
        np.save(name, self._skx_number)

    def stat(self):
        return self.integrator.stat()

    def spin_length(self):
        self.spin.shape = (3, -1)
        length = np.sqrt(np.sum(self.spin**2, axis=0))
        self.spin.shape = (-1,)
        return length

    def compute_spin_error(self):
        length = self.spin_length() - 1.0
        length[self._pins > 0] = 0
        return np.max(abs(length))

    def compute_dmdt(self, dt):
        m0 = self.spin_last
        m1 = self.spin
        dm = (m1 - m0).reshape((3, -1))
        max_dm = np.max(np.sqrt(np.sum(dm**2, axis=0)))
        max_dmdt = max_dm / dt
        return max_dmdt

    def relax(self, dt=1e-11, stopping_dmdt=0.01, max_steps=1000,
              save_m_steps=100, save_vtk_steps=100):

        ONE_DEGREE_PER_NS = 17453292.52

        for i in range(0, max_steps + 1):

            cvode_dt = self.integrator.get_current_step()

            increment_dt = dt

            if cvode_dt > dt:
                increment_dt = cvode_dt

            self.run_until(self.t + increment_dt)

            if save_vtk_steps is not None:
                if i % save_vtk_steps == 0:
                    self.save_vtk()
            if save_m_steps is not None:
                if i % save_m_steps == 0:
                    self.save_m()

            dmdt = self.compute_dmdt(increment_dt)

            print 'step=%d, time=%g, max_dmdt=%g ode_step=%g' % (self.step,
                                                                 self.t,
                                                                 dmdt / ONE_DEGREE_PER_NS,
                                                                 cvode_dt)

            if dmdt < stopping_dmdt * ONE_DEGREE_PER_NS:
                break

        if save_m_steps is not None:
            self.save_m()

        if save_vtk_steps is not None:
            self.save_vtk()
Exemplo n.º 13
0
class MonteCarlo(object):

    def __init__(self, mesh, name='unnamed'):
        self.mesh = mesh
        self.name = name
        self.n = mesh.n
        self.n_nonzero = self.n

        self.ngbs = mesh.neighbours

        self._mu_s = np.zeros(self.n, dtype=np.float)
        self.spin = np.ones(3 * self.n, dtype=np.float)
        self.spin_last = np.ones(3 * self.n, dtype=np.float)

        self.random_spin = np.zeros(3 * self.n, dtype=np.float)
        self._H = np.zeros(3 * self.n, dtype=np.float)

        self._skx_number = np.zeros(self.n, dtype=np.float)
        self.interactions = []

        self.create_tablewriter()
        self.vtk = SaveVTK(self.mesh, name=name)

        self.hexagnoal_mesh = False
        if mesh.mesh_type == 'hexagonal':
            self.hexagnoal_mesh =  True
            #FIX ME !!!!
            self.nngbs = np.copy(mesh.neighbours)
        else:
            self.nngbs = mesh.next_neighbours

        self.step = 0
        self.skx_num = 0
        self.mc = clib.monte_carlo()
        self.set_options()

    def set_options(self, J=50.0, J1=0, D=0, D1=0, Kc=0, H=None, seed=100, T=10.0, S=1):
        """
        J, D and Kc in units of k_B
        H in units of Tesla.
        S is the spin length
        """
        self.mc.set_seed(seed)
        self.J = J
        self.J1 = J1
        self.D = D
        self.D1 = D1
        self.T = T
        self.Kc = Kc
        self.mu_s =  1.0
        if H is not None:
            self._H[:] = helper.init_vector(H, self.mesh)
            self._H[:] = self._H[:]*const.mu_s_1*S/const.k_B  #We have set k_B = 1

    def create_tablewriter(self):

        entities = {
            'step': {'unit': '<>',
                     'get': lambda sim: sim.step,
                     'header': 'step'},
            'm': {'unit': '<>',
                  'get': lambda sim: sim.compute_average(),
                  'header': ('m_x', 'm_y', 'm_z')},
            'skx_num':{'unit': '<>', 
                      'get': lambda sim: sim.skyrmion_number(), 
                      'header': 'skx_num'}
        }

        self.saver = DataSaver(self, self.name + '.txt', entities=entities)

        self.saver.update_entity_order()

    def set_m(self, m0=(1, 0, 0), normalise=True):
        self.spin[:] = helper.init_vector(m0, self.mesh, normalise)

        # TODO: carefully checking and requires to call set_mu first

        self.spin.shape = (-1, 3)
        for i in range(self.spin.shape[0]):
            if self._mu_s[i] == 0:
                self.spin[i, :] = 0
        self.spin.shape = (-1,)

    def get_mu_s(self):
        return self._mu_s

    def set_mu_s(self, value):
        self._mu_s[:] = helper.init_scalar(value, self.mesh)
        nonzero = 0
        for i in range(self.n):
            if self._mu_s[i] > 0.0:
                nonzero += 1

        self.n_nonzero = nonzero

    mu_s = property(get_mu_s, set_mu_s)

    def compute_average(self):
        self.spin.shape = (-1, 3)
        average = np.sum(self.spin, axis=0) / self.n_nonzero
        self.spin.shape = (-1,)
        return average

    def skyrmion_number(self):
        nx = self.mesh.nx
        ny = self.mesh.ny
        nz = self.mesh.nz
        number = clib.compute_skyrmion_number(
            self.spin, self._skx_number, nx, ny, nz, self.mesh.neighbours)
        self.skx_num = number
        return number


    def save_vtk(self):
        """
        Save a VTK file with the magnetisation vector field and magnetic
        moments as cell data. Magnetic moments are saved in units of
        Bohr magnetons

        NOTE: It is recommended to use a *cell to point data* filter in
        Paraview or Mayavi to plot the vector field
        """
        self.vtk.save_vtk(self.spin.reshape(-1, 3),
                          self._mu_s,
                          step=self.step)

    def save_m(self):
        if not os.path.exists('%s_npys' % self.name):
            os.makedirs('%s_npys' % self.name)
        name = '%s_npys/m_%g.npy' % (self.name, self.step)
        np.save(name, self.spin)


    def run(self, steps=1000, save_m_steps=100, save_vtk_steps=100, save_data_steps=1):

        if save_m_steps is not None:
            self.save_m()

        if save_vtk_steps is not None:
            self.save_vtk()

        for step in range(1, steps + 1):
            self.step = step
            self.mc.run_step(self.spin, self.random_spin, self.ngbs, self.nngbs, 
                self.J, self.J1, self.D, self.D1, self._H, self.Kc, self.n, self.T, self.hexagnoal_mesh)
            if save_data_steps is not None:
                if step % save_data_steps == 0:
                    self.saver.save()
                    print("step=%d, skyrmion number=%0.9g"%(self.step, self.skx_num))

            if save_vtk_steps is not None:
                if step % save_vtk_steps == 0:
                    self.save_vtk()
            if save_m_steps is not None:
                if step % save_m_steps == 0:
                    self.save_m()
Exemplo n.º 14
0
class LLG(object):
    def __init__(self, mesh, name='unnamed'):
        """Simulation object.

        *Arguments*

          name : the Simulation name (used for writing data files, for examples)

        """

        self.t = 0
        self.name = name
        self.mesh = mesh
        self.n = mesh.n
        self.n_nonzero = mesh.n
        self.unit_length = mesh.unit_length
        self._alpha = np.zeros(self.n, dtype=np.float)
        self._Ms = np.zeros(self.n, dtype=np.float)
        self._Ms_inv = np.zeros(self.n, dtype=np.float)
        self.spin = np.ones(3 * self.n, dtype=np.float)
        self.spin_last = np.ones(3 * self.n, dtype=np.float)
        self._pins = np.zeros(self.n, dtype=np.int32)
        self.field = np.zeros(3 * self.n, dtype=np.float)
        self.dm_dt = np.zeros(3 * self.n, dtype=np.float)
        self._skx_number = np.zeros(self.n, dtype=np.float)
        self.interactions = []
        self.pin_fun = None
        self.integrator_tolerances_set = False

        self.step = 0

        self.saver = DataSaver(self, name + '.txt')

        self.saver.entities['E_total'] = {
            'unit': '<J>',
            'get': lambda sim: sim.compute_energy(),
            'header': 'E_total'
        }

        self.saver.entities['m_error'] = {
            'unit': '<>',
            'get': lambda sim: sim.compute_spin_error(),
            'header': 'm_error'
        }

        self.saver.entities['skx_num'] = {
            'unit': '<>',
            'get': lambda sim: sim.skyrmion_number(),
            'header': 'skx_num'
        }

        self.saver.entities['rhs_evals'] = {
            'unit': '<>',
            'get': lambda sim: self.cvode_stat_output(sim),
            'header': 'rhs_evals'
        }

        self.saver.update_entity_order()

        # This is for old C files codes using the xperiodic variables
        self.xperiodic, self.yperiodic, self.zperiodic = mesh.periodicity

        self.vtk = SaveVTK(self.mesh, name=name)

        self.vode = cvode.CvodeSolver(self.spin, self.sundials_rhs)

        self.set_default_options()

        self.set_tols()

    def set_default_options(self, gamma=2.21e5, Ms=8.0e5, alpha=0.1):
        self.default_c = 1e11
        self._alpha[:] = alpha
        self._Ms[:] = Ms
        self.gamma = gamma
        self.do_procession = True

    def reset_integrator(self, t=0):
        self.vode.reset(self.spin, t)
        # Also reinitialise the simulation time and step
        self.t = t
        self.step = 0

    def set_tols(self, rtol=1e-8, atol=1e-10):
        if self.integrator_tolerances_set is True:
            self.reset_integrator(self.t)
        self.vode.set_options(rtol, atol)
        self.integrator_tolerances_set = True

    def set_m(self, m0=(1, 0, 0), normalise=True):

        self.spin[:] = helper.init_vector(m0, self.mesh, normalise)

        # TODO: carefully checking and requires to call set_mu first
        self.spin.shape = (-1, 3)
        for i in range(self.spin.shape[0]):
            if self._Ms[i] == 0:
                self.spin[i, :] = 0
        self.spin.shape = (-1, )

        self.vode.set_initial_value(self.spin, self.t)

    def get_pins(self):
        return self._pins

    def set_pins(self, pin):
        self._pins[:] = helper.init_scalar(pin, self.mesh)

        for i in range(len(self._Ms)):
            if self._Ms[i] == 0.0:
                self._pins[i] = 1

    pins = property(get_pins, set_pins)

    def get_alpha(self):
        return self._alpha

    def set_alpha(self, value):
        self._alpha[:] = helper.init_scalar(value, self.mesh)

    alpha = property(get_alpha, set_alpha)

    def get_Ms(self):
        return self._Ms

    def set_Ms(self, value):
        self._Ms[:] = helper.init_scalar(value, self.mesh)
        nonzero = 0
        for i in range(self.n):
            if self._Ms[i] > 0.0:
                self._Ms_inv = 1.0 / self._Ms[i]
                nonzero += 1

        self.n_nonzero = nonzero

        for i in range(len(self._Ms)):
            if self._Ms[i] == 0.0:
                self._pins[i] = 1

        self.Ms_const = np.max(self._Ms)

    Ms = property(get_Ms, set_Ms)

    def add(self, interaction, save_field=False):
        interaction.setup(self.mesh, self.spin, Ms=self._Ms)

        # TODO: FIX
        for i in self.interactions:
            if i.name == interaction.name:
                interaction.name = i.name + '_2'

        self.interactions.append(interaction)

        energy_name = 'E_{0}'.format(interaction.name)
        self.saver.entities[energy_name] = {
            'unit':
            '<J>',
            'get':
            lambda sim: sim.get_interaction(interaction.name).compute_energy(),
            'header':
            energy_name
        }

        if save_field:
            fn = '{0}'.format(interaction.name)
            self.saver.entities[fn] = {
                'unit':
                '<>',
                'get':
                lambda sim: sim.get_interaction(interaction.name).
                average_field(),
                'header': ('%s_x' % fn, '%s_y' % fn, '%s_z' % fn)
            }

        self.saver.update_entity_order()

    def get_interaction(self, name):
        for interaction in self.interactions:
            if interaction.name == name:
                return interaction
        else:
            raise ValueError("Failed to find the interaction with name '{0}', "
                             "available interactions: {1}.".format(
                                 name, [x.name for x in self.interactions]))

    def run_until(self, t):

        if t <= self.t:
            if t == self.t and self.t == 0.0:
                self.compute_effective_field(t)
                self.saver.save()
            return

        ode = self.vode

        self.spin_last[:] = self.spin[:]

        flag = ode.run_until(t)

        if flag < 0:
            raise Exception("Run cython run_until failed!!!")

        self.spin[:] = ode.y[:]

        self.t = t
        self.step += 1

        # update field before saving data
        self.compute_effective_field(t)
        self.saver.save()

    def compute_effective_field(self, t):

        #self.spin[:] = y[:]

        self.field[:] = 0

        for obj in self.interactions:
            self.field += obj.compute_field(t)

    def sundials_rhs(self, t, y, ydot):

        self.t = t

        # already synchronized when call this funciton
        # self.spin[:]=y[:]

        self.compute_effective_field(t)

        clib.compute_llg_rhs(ydot, self.spin, self.field, self.alpha,
                             self._pins, self.gamma, self.n,
                             self.do_procession, self.default_c)

        #ydot[:] = self.dm_dt[:]

        return 0

    def compute_average(self):
        self.spin.shape = (-1, 3)
        average = np.sum(self.spin, axis=0) / self.n_nonzero
        self.spin.shape = (3 * self.n)
        return average

    def compute_energy(self):

        energy = 0

        for obj in self.interactions:
            energy += obj.compute_energy()

        return energy

    def skyrmion_number(self):
        nx = self.mesh.nx
        ny = self.mesh.ny
        nz = self.mesh.nz
        number = clib.compute_skymrion_number(self.spin, self._skx_number, nx,
                                              ny, nz, self.mesh.neighbours)
        return number

    def spin_at(self, i, j, k):

        i1 = 3 * self.mesh.index(i, j, k)

        # print self.spin.shape,nxy,nx,i1,i2,i3
        return np.array([self.spin[i1], self.spin[i1 + 1], self.spin[i1 + 2]])

    def add_monitor_at(self, i, j, k, name='p'):
        """
        Save site spin with index (i,j,k) to txt file.
        """

        self.saver.entities[name] = {
            'unit': '<>',
            'get': lambda sim: sim.spin_at(i, j, k),
            'header': (name + '_x', name + '_y', name + '_z')
        }

        self.saver.update_entity_order()

    def save_vtk(self):
        self.vtk.save_vtk(self.spin.reshape(-1, 3), self.Ms, step=self.step)

    def save_m(self):
        if not os.path.exists('%s_npys' % self.name):
            os.makedirs('%s_npys' % self.name)
        name = '%s_npys/m_%g.npy' % (self.name, self.step)
        np.save(name, self.spin)

    def save_skx(self):
        if not os.path.exists('%s_skx_npys' % self.name):
            os.makedirs('%s_skx_npys' % self.name)
        name = '%s_skx_npys/m_%g.npy' % (self.name, self.step)
        np.save(name, self._skx_number)

    def stat(self):
        return self.vode.stat()

    def spin_length(self):
        self.spin.shape = (3, -1)
        length = np.sqrt(np.sum(self.spin**2, axis=0))
        self.spin.shape = (-1, )
        return length

    def compute_spin_error(self):
        length = self.spin_length() - 1.0
        length[self._pins > 0] = 0
        return np.max(abs(length))

    def compute_dmdt(self, dt):
        m0 = self.spin_last
        m1 = self.spin
        dm = (m1 - m0).reshape((3, -1))
        max_dm = np.max(np.sqrt(np.sum(dm**2, axis=0)))
        max_dmdt = max_dm / dt
        return max_dmdt

    def relax(self,
              dt=1e-11,
              stopping_dmdt=0.01,
              max_steps=1000,
              save_m_steps=100,
              save_vtk_steps=100):

        ONE_DEGREE_PER_NS = 17453292.52

        for i in range(0, max_steps + 1):

            cvode_dt = self.vode.get_current_step()

            increment_dt = dt

            if cvode_dt > dt:
                increment_dt = cvode_dt

            self.run_until(self.t + increment_dt)

            if save_vtk_steps is not None:
                if i % save_vtk_steps == 0:
                    self.save_vtk()
            if save_m_steps is not None:
                if i % save_m_steps == 0:
                    self.save_m()

            dmdt = self.compute_dmdt(increment_dt)

            print 'step=%d, time=%g, max_dmdt=%g ode_step=%g' % (
                self.step, self.t, dmdt / ONE_DEGREE_PER_NS, cvode_dt)

            if dmdt < stopping_dmdt * ONE_DEGREE_PER_NS:
                break

        if save_m_steps is not None:
            self.save_m()

        if save_vtk_steps is not None:
            self.save_vtk()

    def cvode_stat_output(self, sim):
        """
        This function tries to get the values from the CVODE statistics. For a
        'sim' simulation object, this is done starting from calling
        sim.vode.stats()

        According to the CVODE version, this call can generate a string:

        CvodeSolver(nsteps = 18,
                    nfevals = 32,
                    njevals = 14.
                    )

        where:

        nsteps  --> number of steps taken by CVODE
        nfevals --> number of calls to the user's f function
                    (I guess this is what we need)
        njevals --> the cumulative number of calls to the Jacobian function

        So, for example,  we can regex search any number preceded by
            "nfevals = "
        to get the number of evaluations of the RHS and convert the
        result to an integer

        OR it can give a tuple with 3 values, which must be in the same
        order than before

        For now, we are only interested in the RHS evaluations, so we
        return a single value

        """
        cvode_stat = sim.vode.stat()

        if isinstance(cvode_stat, str):
            out = int(
                re.search(r'(?<=nfevals\s=\s)[0-9]*', cvode_stat).group(0))
        elif isinstance(cvode_stat, tuple):
            out = cvode_stat[1]
        else:
            raise NotImplementedError('Cannot retrieve the values'
                                      'from CVODE stats')

        return out
Exemplo n.º 15
0
    def __init__(self, mesh, name='unnamed'):
        """Simulation object.

        *Arguments*

          name : the Simulation name (used for writing data files, for examples)

        """

        self.t = 0
        self.name = name
        self.mesh = mesh
        self.n = mesh.n
        self.n_nonzero = mesh.n
        self.unit_length = mesh.unit_length
        self._alpha = np.zeros(self.n, dtype=np.float)
        self._Ms = np.zeros(self.n, dtype=np.float)
        self._Ms_inv = np.zeros(self.n, dtype=np.float)
        self.spin = np.ones(3 * self.n, dtype=np.float)
        self.spin_last = np.ones(3 * self.n, dtype=np.float)
        self._pins = np.zeros(self.n, dtype=np.int32)
        self.field = np.zeros(3 * self.n, dtype=np.float)
        self.dm_dt = np.zeros(3 * self.n, dtype=np.float)
        self._skx_number = np.zeros(self.n, dtype=np.float)
        self.interactions = []
        self.pin_fun = None
        self.integrator_tolerances_set = False

        self.step = 0

        self.saver = DataSaver(self, name + '.txt')

        self.saver.entities['E_total'] = {
            'unit': '<J>',
            'get': lambda sim: sim.compute_energy(),
            'header': 'E_total'}

        self.saver.entities['m_error'] = {
            'unit': '<>',
            'get': lambda sim: sim.compute_spin_error(),
            'header': 'm_error'}

        self.saver.entities['skx_num'] = {
            'unit': '<>',
            'get': lambda sim: sim.skyrmion_number(),
            'header': 'skx_num'}

        self.saver.entities['rhs_evals'] = {
            'unit': '<>',
            'get': lambda sim: self.cvode_stat_output(sim),
            'header': 'rhs_evals'}

        self.saver.update_entity_order()

        # This is for old C files codes using the xperiodic variables
        self.xperiodic, self.yperiodic, self.zperiodic = mesh.periodicity

        self.vtk = SaveVTK(self.mesh, name=name)

        self.vode = cvode.CvodeSolver(self.spin, self.sundials_rhs)

        self.set_default_options()

        self.set_tols()
Exemplo n.º 16
0
class LLG(object):

    def __init__(self, mesh, name='unnamed'):
        """Simulation object.

        *Arguments*

          name : the Simulation name (used for writing data files, for examples)

        """

        self.t = 0
        self.name = name
        self.mesh = mesh
        self.n = mesh.n
        self.n_nonzero = mesh.n
        self.unit_length = mesh.unit_length
        self._alpha = np.zeros(self.n, dtype=np.float)
        self._Ms = np.zeros(self.n, dtype=np.float)
        self._Ms_inv = np.zeros(self.n, dtype=np.float)
        self.spin = np.ones(3 * self.n, dtype=np.float)
        self.spin_last = np.ones(3 * self.n, dtype=np.float)
        self._pins = np.zeros(self.n, dtype=np.int32)
        self.field = np.zeros(3 * self.n, dtype=np.float)
        self.dm_dt = np.zeros(3 * self.n, dtype=np.float)
        self._skx_number = np.zeros(self.n, dtype=np.float)
        self.interactions = []
        self.pin_fun = None
        self.integrator_tolerances_set = False

        self.step = 0

        self.saver = DataSaver(self, name + '.txt')

        self.saver.entities['E_total'] = {
            'unit': '<J>',
            'get': lambda sim: sim.compute_energy(),
            'header': 'E_total'}

        self.saver.entities['m_error'] = {
            'unit': '<>',
            'get': lambda sim: sim.compute_spin_error(),
            'header': 'm_error'}

        self.saver.entities['skx_num'] = {
            'unit': '<>',
            'get': lambda sim: sim.skyrmion_number(),
            'header': 'skx_num'}

        self.saver.entities['rhs_evals'] = {
            'unit': '<>',
            'get': lambda sim: self.cvode_stat_output(sim),
            'header': 'rhs_evals'}

        self.saver.update_entity_order()

        # This is for old C files codes using the xperiodic variables
        self.xperiodic, self.yperiodic, self.zperiodic = mesh.periodicity

        self.vtk = SaveVTK(self.mesh, name=name)

        self.vode = cvode.CvodeSolver(self.spin, self.sundials_rhs)

        self.set_default_options()

        self.set_tols()

    def set_default_options(self, gamma=2.21e5, Ms=8.0e5, alpha=0.1):
        self.default_c = 1e11
        self._alpha[:] = alpha
        self._Ms[:] = Ms
        self.gamma = gamma
        self.do_procession = True

    def reset_integrator(self, t=0):
        self.vode.reset(self.spin, t)
        # Also reinitialise the simulation time and step
        self.t = t
        self.step = 0

    def set_tols(self, rtol=1e-8, atol=1e-10):
        if self.integrator_tolerances_set is True:
            self.reset_integrator(self.t)
        self.vode.set_options(rtol, atol)
        self.integrator_tolerances_set = True

    def set_m(self, m0=(1, 0, 0), normalise=True):

        self.spin[:] = helper.init_vector(m0, self.mesh, normalise)

        # TODO: carefully checking and requires to call set_mu first
        self.spin.shape = (-1, 3)
        for i in range(self.spin.shape[0]):
            if self._Ms[i] == 0:
                self.spin[i, :] = 0
        self.spin.shape = (-1,)

        self.vode.set_initial_value(self.spin, self.t)

    def get_pins(self):
        return self._pins

    def set_pins(self, pin):
        self._pins[:] = helper.init_scalar(pin, self.mesh)

        for i in range(len(self._Ms)):
            if self._Ms[i] == 0.0:
                self._pins[i] = 1

    pins = property(get_pins, set_pins)

    def get_alpha(self):
        return self._alpha

    def set_alpha(self, value):
        self._alpha[:] = helper.init_scalar(value, self.mesh)

    alpha = property(get_alpha, set_alpha)

    def get_Ms(self):
        return self._Ms

    def set_Ms(self, value):
        self._Ms[:] = helper.init_scalar(value, self.mesh)
        nonzero = 0
        for i in range(self.n):
            if self._Ms[i] > 0.0:
                self._Ms_inv = 1.0 / self._Ms[i]
                nonzero += 1

        self.n_nonzero = nonzero

        for i in range(len(self._Ms)):
            if self._Ms[i] == 0.0:
                self._pins[i] = 1

        self.Ms_const = np.max(self._Ms)

    Ms = property(get_Ms, set_Ms)

    def add(self, interaction, save_field=False):
        interaction.setup(self.mesh, self.spin, Ms=self._Ms)

        # TODO: FIX
        for i in self.interactions:
            if i.name == interaction.name:
                interaction.name = i.name + '_2'

        self.interactions.append(interaction)

        energy_name = 'E_{0}'.format(interaction.name)
        self.saver.entities[energy_name] = {
            'unit': '<J>',
            'get': lambda sim: sim.get_interaction(interaction.name).compute_energy(),
            'header': energy_name}

        if save_field:
            fn = '{0}'.format(interaction.name)
            self.saver.entities[fn] = {
                'unit': '<>',
                'get': lambda sim: sim.get_interaction(interaction.name).average_field(),
                'header': ('%s_x' % fn, '%s_y' % fn, '%s_z' % fn)}

        self.saver.update_entity_order()

    def get_interaction(self, name):
        for interaction in self.interactions:
            if interaction.name == name:
                return interaction
        else:
            raise ValueError("Failed to find the interaction with name '{0}', "
                             "available interactions: {1}.".format(
                                 name, [x.name for x in self.interactions]))

    def run_until(self, t):

        if t <= self.t:
            if t == self.t and self.t == 0.0:
                self.compute_effective_field(t)
                self.saver.save()
            return

        ode = self.vode

        self.spin_last[:] = self.spin[:]

        flag = ode.run_until(t)

        if flag < 0:
            raise Exception("Run cython run_until failed!!!")

        self.spin[:] = ode.y[:]

        self.t = t
        self.step += 1

        # update field before saving data
        self.compute_effective_field(t)
        self.saver.save()

    def compute_effective_field(self, t):

        #self.spin[:] = y[:]

        self.field[:] = 0

        for obj in self.interactions:
            self.field += obj.compute_field(t)

    def sundials_rhs(self, t, y, ydot):

        self.t = t

        # already synchronized when call this funciton
        # self.spin[:]=y[:]

        self.compute_effective_field(t)

        clib.compute_llg_rhs(ydot,
                             self.spin,
                             self.field,
                             self.alpha,
                             self._pins,
                             self.gamma,
                             self.n,
                             self.do_procession,
                             self.default_c)

        #ydot[:] = self.dm_dt[:]

        return 0

    def compute_average(self):
        self.spin.shape = (-1, 3)
        average = np.sum(self.spin, axis=0) / self.n_nonzero
        self.spin.shape = (3 * self.n)
        return average

    def compute_energy(self):

        energy = 0

        for obj in self.interactions:
            energy += obj.compute_energy()

        return energy

    def skyrmion_number(self):
        nx = self.mesh.nx
        ny = self.mesh.ny
        nz = self.mesh.nz
        number = clib.compute_skymrion_number(
            self.spin, self._skx_number, nx, ny, nz, self.mesh.neighbours)
        return number

    def spin_at(self, i, j, k):

        i1 = 3 * self.mesh.index(i, j, k)

        # print self.spin.shape,nxy,nx,i1,i2,i3
        return np.array([self.spin[i1],
                         self.spin[i1 + 1],
                         self.spin[i1 + 2]])

    def add_monitor_at(self, i, j, k, name='p'):
        """
        Save site spin with index (i,j,k) to txt file.
        """

        self.saver.entities[name] = {
            'unit': '<>',
            'get': lambda sim: sim.spin_at(i, j, k),
            'header': (name + '_x', name + '_y', name + '_z')}

        self.saver.update_entity_order()

    def save_vtk(self):
        self.vtk.save_vtk(self.spin.reshape(-1, 3), self.Ms, step=self.step)

    def save_m(self):
        if not os.path.exists('%s_npys' % self.name):
            os.makedirs('%s_npys' % self.name)
        name = '%s_npys/m_%g.npy' % (self.name, self.step)
        np.save(name, self.spin)

    def save_skx(self):
        if not os.path.exists('%s_skx_npys' % self.name):
            os.makedirs('%s_skx_npys' % self.name)
        name = '%s_skx_npys/m_%g.npy' % (self.name, self.step)
        np.save(name, self._skx_number)

    def stat(self):
        return self.vode.stat()

    def spin_length(self):
        self.spin.shape = (3, -1)
        length = np.sqrt(np.sum(self.spin**2, axis=0))
        self.spin.shape = (-1,)
        return length

    def compute_spin_error(self):
        length = self.spin_length() - 1.0
        length[self._pins > 0] = 0
        return np.max(abs(length))

    def compute_dmdt(self, dt):
        m0 = self.spin_last
        m1 = self.spin
        dm = (m1 - m0).reshape((3, -1))
        max_dm = np.max(np.sqrt(np.sum(dm**2, axis=0)))
        max_dmdt = max_dm / dt
        return max_dmdt

    def relax(self, dt=1e-11, stopping_dmdt=0.01, max_steps=1000,
              save_m_steps=100, save_vtk_steps=100):

        ONE_DEGREE_PER_NS = 17453292.52

        for i in range(0, max_steps + 1):

            cvode_dt = self.vode.get_current_step()

            increment_dt = dt

            if cvode_dt > dt:
                increment_dt = cvode_dt

            self.run_until(self.t + increment_dt)

            if save_vtk_steps is not None:
                if i % save_vtk_steps == 0:
                    self.save_vtk()
            if save_m_steps is not None:
                if i % save_m_steps == 0:
                    self.save_m()

            dmdt = self.compute_dmdt(increment_dt)

            print 'step=%d, time=%g, max_dmdt=%g ode_step=%g' % (self.step,
                                                                 self.t,
                                                                 dmdt / ONE_DEGREE_PER_NS,
                                                                 cvode_dt)

            if dmdt < stopping_dmdt * ONE_DEGREE_PER_NS:
                break

        if save_m_steps is not None:
            self.save_m()

        if save_vtk_steps is not None:
            self.save_vtk()

    def cvode_stat_output(self, sim):
        """
        This function tries to get the values from the CVODE statistics. For a
        'sim' simulation object, this is done starting from calling
        sim.vode.stats()

        According to the CVODE version, this call can generate a string:

        CvodeSolver(nsteps = 18,
                    nfevals = 32,
                    njevals = 14.
                    )

        where:

        nsteps  --> number of steps taken by CVODE
        nfevals --> number of calls to the user's f function
                    (I guess this is what we need)
        njevals --> the cumulative number of calls to the Jacobian function

        So, for example,  we can regex search any number preceded by
            "nfevals = "
        to get the number of evaluations of the RHS and convert the
        result to an integer

        OR it can give a tuple with 3 values, which must be in the same
        order than before

        For now, we are only interested in the RHS evaluations, so we
        return a single value

        """
        cvode_stat = sim.vode.stat()

        if isinstance(cvode_stat, str):
            out = int(re.search(r'(?<=nfevals\s=\s)[0-9]*',
                                cvode_stat).group(0)
                      )
        elif isinstance(cvode_stat, tuple):
            out = cvode_stat[1]
        else:
            raise NotImplementedError('Cannot retrieve the values'
                                      'from CVODE stats')

        return out
Exemplo n.º 17
0
class SimBase(object):
    """

    A class with common methods and definitions for both micromagnetic and
    atomistic simulations

    """

    def __init__(self, mesh, name):

        self.name = name
        self.mesh = mesh
        self.n = mesh.n
        self.n_nonzero = mesh.n
        self.unit_length = mesh.unit_length

        self._magnetisation = np.zeros(self.n, dtype=np.float)
        # Inverse magnetisation
        self._magnetisation_inv = np.zeros(self.n, dtype=np.float)

        self.spin = np.ones(3 * self.n, dtype=np.float)
        self._pins = np.zeros(self.n, dtype=np.int32)
        self.field = np.zeros(3 * self.n, dtype=np.float)
        self._skx_number = np.zeros(self.n, dtype=np.float)
        self.interactions = []

        # This is for old C files codes using the xperiodic variables
        try:
            self.xperiodic, self.yperiodic, self.zperiodic = mesh.periodicity
        except ValueError:
            self.xperiodic, self.yperiodic = mesh.periodicity

        # To save the simulation data: ----------------------------------------

        self.data_saver = DataSaver(self, name + '.txt')

        self.data_saver.entities['E_total'] = {
            'unit': '<J>',
            'get': lambda sim: self.compute_energy(),
            'header': 'E_total'}

        self.data_saver.entities['m_error'] = {
            'unit': '<>',
            'get': lambda sim: self.compute_spin_error(),
            'header': 'm_error'}

        self.data_saver.update_entity_order()

        # ---------------------------------------------------------------------

    def set_m(self, m0=(1, 0, 0),
              normalise=True):
        """

        Set the magnetisation/spin three dimensional vector field profile.

        ARGUMENTS:

        m0      :: * To set every spin with the same direction,
                   set this value as a 3 elements tuple or list.

                   * For a spatially dependent vector field, you can specify a
                   function that returns a 3 element list depending on the
                   spatial coordinates. For example, a magnetisation field that
                   depends on the x position:

                        def m_profile(r):
                            for r[0] > 2:
                                return (0, 0, 1)
                            else:
                                return (0, 0, -1)

                   * You can also manually specify an array with (3 * n)
                   elements with the spins directions in the following order:

                        [mx_0 my_0 mz_0 mx_1 my_1 ... mx_n, my_n, mz_n]

                   where n is the number of mesh nodes and the order of the
                   magnetisation vectors follow the same order than the mesh
                   coordinates array.

                   * Alternatively, if you previously saved the magnetisation
                   field array to a numpy file, you can load it using
                   numpy.load(my_array)

        """

        self.spin[:] = helper.init_vector(m0, self.mesh, 3, normalise)

        # TODO: carefully checking and requires to call set_mu first

        # Set the magnetisation/spin direction to (0, 0, 0) for sites
        # with no material, i.e. M_s = 0 or mu_s = 0
        # TODO: Check for atomistic and micromagnetic cases
        self.spin.shape = (-1, 3)
        for i in range(self.spin.shape[0]):
            if self._magnetisation[i] == 0:
                self.spin[i, :] = 0
        self.spin.shape = (-1,)

        # Set the initial state for the Sundials integrator using the
        # spins array
        # Minimiser methods do not have integrator
        try:
            self.driver.integrator.set_initial_value(self.spin, self.driver.t)
        except AttributeError:
            pass

    def get_pins(self):
        """
        Returns the array with pinned spins in the sample:
        sites with 0 are unpinned and sites with 1 are pinned. The order
        of the array follows the same order than the mesh coordinates
        """
        return self._pins

    def set_pins(self, pin):
        """

        An scalar field with values 1 or 0 to specify mesh/lattice sites
        with pinned or unpinned magnetic moments, respectively

        ARGUMENTS:

        pin     :: * You can specify a function that returns 1 or 0 depending
                   on the spatial coordinates. For example, to pin the spins
                   in a range in the x direction:

                        def pin_profile(r):
                            for r[0] > 2 and r[0] < 4:
                                return 1
                            else:
                                return 0

                   * You can also manually specify an array with n elements (1
                   or 0) with the pinned/unpinned values in the same order than
                   the mesh coordinates array.

                   * Alternatively, if you previously saved the pin
                   field array to a numpy file, you can load it using
                   numpy.load(my_array)
        """
        self._pins[:] = helper.init_scalar(pin, self.mesh)

        # Sites with no material, i.e. Mu_s or mu_s equal to zero,
        # will be pinned
        for i in range(len(self._magnetisation)):
            if self._magnetisation[i] == 0.0:
                self._pins[i] = 1

    pins = property(get_pins, set_pins)

    def add(self, interaction, save_field=False):
        """

        Add an interaction from one of the Energy subclasses. By default,
        the average energy of the added interaction is saved to the
        data file when relaxing the system

        OPTIONAL ARGUMENTS:

        save_field      :: Set True to save the average values of this
                           interaction field when relaxing the system

        """

        # magnetisation is Ms for the micromagnetic Sim class, and it is
        # mu_s for the atomistic Sim class
        interaction.setup(self.mesh, self.spin,
                          self._magnetisation,
                          self._magnetisation_inv
                          )

        # TODO: FIX  --> ??
        # When adding an interaction that was previously added, using
        # the same name, append a '_2' to the new interaction name (?)
        for i in self.interactions:
            if i.name == interaction.name:
                interaction.name = i.name + '_2'

        self.interactions.append(interaction)

        # Specify a name for the energy of the interaction, which will
        # appear in a file with saved values
        # When saving the energy values, we call the compute_energy() method
        # from the (micromagnetic/atomistic) Energy class (overhead?)
        energy_name = 'E_{0}'.format(interaction.name)
        self.data_saver.entities[energy_name] = {
            'unit': '<J>',
            'get': lambda sim: sim.get_interaction(interaction.name).compute_energy(),
            'header': energy_name}

        # Save the average values of the interaction vector field components
        if save_field:
            fn = '{0}'.format(interaction.name)
            self.data_saver.entities[fn] = {
                'unit': '<>',
                'get': lambda sim: sim.get_interaction(interaction.name).average_field(),
                'header': ('%s_x' % fn, '%s_y' % fn, '%s_z' % fn)}

        self.data_saver.update_entity_order()

    def get_interaction(self, name):
        """
        Returns an instance of a magnetic interaction previously added
        to the simulation, using the corresponding interaction name as
        a string
        """
        for interaction in self.interactions:
            if interaction.name == name:
                return interaction
        else:
            raise ValueError("Failed to find the interaction with name '{0}', "
                             "available interactions: {1}.".format(
                                 name, [x.name for x in self.interactions]))

    def remove(self, name):
        """
        Removes an interaction from a simulation.

        This is useful because it reduces the run-time
        if the interaction calculation time is substantial.
        """

        interaction = None
        # First we remove it from the list of interactions
        print(self.interactions)
        for i, intn in enumerate(self.interactions):
            print(intn.name)
            if intn.name == name:
                interaction = self.interactions.pop(i)
                break

        if not interaction:
            raise ValueError("Could not find the "
                             "interaction with name {}".format(name))

        # Next, we need to change the data saver entities.
        # We don't want to remove the relevant interaction
        # completely because if this is done, the table
        # would be incorrect. What we need to do is therefore
        # replace the lambda functions with dummy ones which
        # just return zeros; for example.if no Zeeman intn then
        # the Zeeman energy and field are zero anyway.
        self.data_saver.entities['E_{}'.format(name)]['get'] = lambda sim: 0
        # We don't save field by default, so need to check if
        # save field is selected. Easiest just to use a try/except
        # block here; not a performance critical function.
        try:
            self.data_saver.entities[name]['get'] = \
                lambda sim: np.array([0.0, 0.0, 0.0])
        except:
            pass

    def skyrmion_number(self):
        pass

    def spin_at(self, i, j, k):
        """
        Returns the x,y,z components of a spin in the [i, j, k]
        position of the mesh, where i,j,k are integer indexes. The index
        ordering is specified in the mesh class.
        """

        i1 = 3 * self.mesh.index(i, j, k)

        # print self.spin.shape,nxy,nx,i1,i2,i3
        return np.array([self.spin[i1],
                         self.spin[i1 + 1],
                         self.spin[i1 + 2]])

    def add_monitor_at(self, i, j, k, name='p'):
        """
        Save site spin with index (i,j,k) to txt file.
        """

        self.data_saver.entities[name] = {
            'unit': '<>',
            'get': lambda sim: sim.spin_at(i, j, k),
            'header': (name + '_x', name + '_y', name + '_z')}

        self.data_saver.update_entity_order()

    def spin_length(self):
        """
        Returns an array with the length of every spin in the mesh. The
        order is given by the mesh.coordinates order
        """
        self.spin.shape = (-1, 3)
        length = np.sqrt(np.sum(self.spin ** 2, axis=1))
        self.spin.shape = (-1,)
        return length

    def compute_spin_error(self):
        length = self.spin_length() - 1.0
        length[self._pins > 0] = 0
        return np.max(abs(length))

    def compute_average(self):
        """
        Compute the average values of the 3 components of the magnetisation
        vector field
        """
        self.spin.shape = (-1, 3)
        average = np.sum(self.spin, axis=0) / self.n_nonzero
        self.spin.shape = (3 * self.n)
        return average

    def compute_energy(self):
        """
        Compute the total energy of the magnetic system
        """
        energy = 0

        for obj in self.interactions:
            energy += obj.compute_energy()

        return energy

    def get_field_array(self, interaction):
        """
        Returns the field array corresponding to the interaction given:

        e.g.
            compute_interaction_field('Demag')
        returns a numpy array containing the Demag field.
        """
        field = self.get_interaction(interaction)
        # Copy here to avoid destroying the field accidentally
        # e.g. through reshaping
        f = field.field.copy()
        return f
Exemplo n.º 18
0
class LLG(object):

    def __init__(self, mesh, name='unnamed', use_jac=False):
        """Simulation object.
        *Arguments*
          name : the Simulation name (used for writing data files, for examples)
        """

        self.t = 0
        self.name = name
        self.mesh = mesh
        self.n = mesh.n
        self.n_nonzero = mesh.n
        self.unit_length = mesh.unit_length
        self._alpha = np.zeros(self.n, dtype=np.float)
        self._mu_s = np.zeros(self.n, dtype=np.float)
        self._mu_s_inv = np.zeros(self.n, dtype=np.float)

        self.spin = np.ones(3 * self.n, dtype=np.float)
        self.spin_last = np.ones(3 * self.n, dtype=np.float)
        self._pins = np.zeros(self.n, dtype=np.int32)
        self.field = np.zeros(3 * self.n, dtype=np.float)
        self.dm_dt = np.zeros(3 * self.n, dtype=np.float)
        self._skx_number = np.zeros(self.n, dtype=np.float)

        self.interactions = []
        self.pin_fun = None

        self.step = 0

        self.saver = DataSaver(self, name + '.txt')

        self.saver.entities['E_total'] = {
            'unit': '<J>',
            'get': lambda sim: sim.compute_energy(),
            'header': 'E_total'}

        self.saver.entities['m_error'] = {
            'unit': '<>',
            'get': lambda sim: sim.compute_spin_error(),
            'header': 'm_error'}

        self.saver.entities['skx_num'] = {
            'unit': '<>',
            'get': lambda sim: sim.skyrmion_number(),
            'header': 'skx_num'}

        self.saver.update_entity_order()

        # This is only for old C files using the xperiodic variable
        self.xperiodic, self.yperiodic = mesh.periodicity[0], mesh.periodicity[1]

        self.vtk = SaveVTK(self.mesh, name=name)

        if use_jac is not True:
            self.vode = cvode.CvodeSolver(self.spin, self.sundials_rhs)
        else:
            self.vode = cvode.CvodeSolver(
                self.spin, self.sundials_rhs, self.sundials_jtn)

        self.set_default_options()

        self.set_tols()

        # When initialising the integrator in the self.vode call, the CVOde
        # class calls the set_initial_value function (with flag_m=0), which
        # initialises a new integrator and allocates memory in this process.
        # Now, when we set the magnetisation, we will use the same memory
        # setting this flag_m to 1, so instead of calling CVodeInit we call
        # CVodeReInit. If don't, memory is allocated in every call of set_m
        self.flag_m = 1

    def set_default_options(self, gamma=1, mu_s=1, alpha=0.1):
        self.default_c = -1
        self._alpha[:] = alpha
        self._mu_s[:] = mu_s
        self.gamma = gamma
        self.do_precession = True

    def reset_integrator(self, t=0):
        self.vode.reset(self.spin, t)
        self.t = t  # also reinitialise the simulation time and step
        self.step = 0

    def set_tols(self, rtol=1e-8, atol=1e-10):
        self.vode.set_options(rtol, atol)

    def set_options(self, rtol=1e-8, atol=1e-10):
        self.set_tols(rtol, atol)

    def set_m(self, m0=(1, 0, 0), normalise=True):

        self.spin[:] = helper.init_vector(m0, self.mesh, normalise)

        # TODO: carefully checking and requires to call set_mu first
        self.spin.shape = (-1, 3)
        for i in range(self.spin.shape[0]):
            if self._mu_s[i] == 0:
                self.spin[i, :] = 0
        self.spin.shape = (-1,)

        self.vode.set_initial_value(self.spin, self.t)

        if not self.flag_m:
            self.flag_m = 1

    def get_pins(self):
        return self._pins

    def set_pins(self, pin):
        self._pins[:] = helper.init_scalar(pin, self.mesh)

        for i in range(len(self._mu_s)):
            if self._mu_s[i] == 0.0:
                self._pins[i] = 1

    pins = property(get_pins, set_pins)

    def get_alpha(self):
        return self._alpha

    def set_alpha(self, value):
        self._alpha[:] = helper.init_scalar(value, self.mesh)

    alpha = property(get_alpha, set_alpha)

    def get_mu_s(self):
        return self._mu_s

    def set_mu_s(self, value):
        self._mu_s[:] = helper.init_scalar(value, self.mesh)
        nonzero = 0
        for i in range(self.n):
            if self._mu_s[i] > 0.0:
                self._mu_s_inv[i] = 1.0 / self._mu_s[i]
                nonzero += 1

        self.n_nonzero = nonzero

        for i in range(len(self._mu_s)):
            if self._mu_s[i] == 0.0:
                self._pins[i] = 1

    mu_s = property(get_mu_s, set_mu_s)

    def add(self, interaction, save_field=False):
        interaction.setup(self.mesh, self.spin, self._mu_s)

        # TODO: FIX
        for i in self.interactions:
            if i.name == interaction.name:
                interaction.name = i.name + '_2'

        self.interactions.append(interaction)

        energy_name = 'E_{0}'.format(interaction.name)
        self.saver.entities[energy_name] = {
            'unit': '<J>',
            'get': lambda sim: sim.get_interaction(interaction.name).compute_energy(),
            'header': energy_name}

        if save_field:
            fn = '{0}'.format(interaction.name)
            self.saver.entities[fn] = {
                'unit': '<>',
                'get': lambda sim: sim.get_interaction(interaction.name).average_field(),
                'header': ('%s_x' % fn, '%s_y' % fn, '%s_z' % fn)}

        self.saver.update_entity_order()

    def get_interaction(self, name):
        for interaction in self.interactions:
            if interaction.name == name:
                return interaction
        else:
            raise ValueError("Failed to find the interaction with name '{0}', "
                             "available interactions: {1}.".format(
                                 name, [x.name for x in self.interactions]))

    def run_until(self, t):

        if t <= self.t:
            if t == self.t and self.t == 0.0:
                self.compute_effective_field(t)
                self.saver.save()
            return

        ode = self.vode

        self.spin_last[:] = self.spin[:]

        flag = ode.run_until(t)

        if flag < 0:
            raise Exception("Run cython run_until failed!!!")

        self.spin[:] = ode.y[:]

        self.t = t
        self.step += 1

        # update field before saving data
        self.compute_effective_field(t)
        self.saver.save()

    def compute_effective_field(self, t):

        #self.spin[:] = y[:]

        self.field[:] = 0

        for obj in self.interactions:
            self.field += obj.compute_field(t)

    def compute_effective_field_jac(self, t, spin):

        #self.spin[:] = y[:]

        self.field[:] = 0

        for obj in self.interactions:
            if obj.jac is True:
                self.field += obj.compute_field(t, spin=spin)

    def sundials_rhs(self, t, y, ydot):

        self.t = t

        # already synchronized when call this funciton
        # self.spin[:]=y[:]

        self.compute_effective_field(t)

        clib.compute_llg_rhs(ydot,
                             self.spin,
                             self.field,
                             self.alpha,
                             self._pins,
                             self.gamma,
                             self.n,
                             self.do_precession,
                             self.default_c)

        #ydot[:] = self.dm_dt[:]

        return 0

    def sundials_jtn(self, mp, Jmp, t, m, fy):
        # we can not copy mp to self.spin since m and self.spin is one object.
        #self.spin[:] = mp[:]
        print('NO jac...........')
        self.compute_effective_field_jac(t, mp)
        clib.compute_llg_jtimes(Jmp,
                                m, fy,
                                mp, self.field,
                                self.alpha,
                                self._pins,
                                self.gamma,
                                self.n,
                                self.do_precession,
                                self.default_c)

        return 0

    def compute_average(self):
        self.spin.shape = (-1, 3)
        average = np.sum(self.spin, axis=0) / self.n_nonzero
        self.spin.shape = (-1,)
        return average

    def compute_energy(self):

        energy = 0

        for obj in self.interactions:
            energy += obj.compute_energy()

        return energy

    def skyrmion_number(self):
        nx = self.mesh.nx
        ny = self.mesh.ny
        nz = self.mesh.nz
        number = clib.compute_skyrmion_number(
            self.spin, self._skx_number, nx, ny, nz, self.mesh.neighbours)
        return number

    def spin_at(self, i, j, k):
        """
        Returns the spin components of the spin at
        i-th position in the z direction, j-th position in the
        y direction and k-th position in x direction
        """

        nxyz = self.mesh.n

        index = 3 * self.mesh.index(i, j, k)

        # print self.spin.shape,nxy,nx,i1,i2,i3
        return np.array([self.spin[index],
                         self.spin[index + 1],
                         self.spin[index + 2]])

    def add_monitor_at(self, i, j, k, name='p'):
        """
        Save site spin with index (i,j,k) to txt file.
        """

        self.saver.entities[name] = {
            'unit': '<>',
            'get': lambda sim: sim.spin_at(i, j, k),
            'header': (name + '_x', name + '_y', name + '_z')}

        self.saver.update_entity_order()

    def save_vtk(self):
        """
        Save a VTK file with the magnetisation vector field and magnetic
        moments as cell data. Magnetic moments are saved in units of
        Bohr magnetons

        NOTE: It is recommended to use a *cell to point data* filter in
        Paraview or Mayavi to plot the vector field
        """
        self.vtk.save_vtk(self.spin.reshape(-1, 3),
                          self._mu_s / const.mu_B,
                          step=self.step
                          )

    def save_m(self):
        if not os.path.exists('%s_npys' % self.name):
            os.makedirs('%s_npys' % self.name)
        name = '%s_npys/m_%g.npy' % (self.name, self.step)
        np.save(name, self.spin)

    def save_skx(self, vtk=False):
        if not os.path.exists('%s_skx_npys' % self.name):
            os.makedirs('%s_skx_npys' % self.name)
        name = '%s_skx_npys/m_%g.npy' % (self.name, self.step)
        np.save(name, self._skx_number)
        if vtk is True:
            self.vtk.save_vtk_scalar(self._skx_number, self.step)

    def stat(self):
        return self.vode.stat()

    def spin_length(self):
        self.spin.shape = (-1, 3)
        length = np.sqrt(np.sum(self.spin**2, axis=1))
        self.spin.shape = (-1,)
        return length

    def compute_spin_error(self):
        length = self.spin_length() - 1.0
        length[self._pins > 0] = 0
        return np.max(abs(length))

    def compute_dmdt(self, dt):
        m0 = self.spin_last
        m1 = self.spin
        dm = (m1 - m0).reshape((-1, 3))
        max_dm = np.max(np.sqrt(np.sum(dm**2, axis=1)))
        max_dmdt = max_dm / dt
        return max_dmdt

    def relax(self, dt=1e-11, stopping_dmdt=0.01,
              max_steps=1000, save_m_steps=100, save_vtk_steps=100):

        for i in range(0, max_steps + 1):

            cvode_dt = self.vode.get_current_step()

            increment_dt = dt

            if cvode_dt > dt:
                increment_dt = cvode_dt

            self.run_until(self.t + increment_dt)

            if save_vtk_steps is not None:
                if i % save_vtk_steps == 0:
                    self.save_vtk()
            if save_m_steps is not None:
                if i % save_m_steps == 0:
                    self.save_m()

            dmdt = self.compute_dmdt(increment_dt)

            print('step=%d, time=%0.3g, max_dmdt=%0.3g ode_step=%0.3g' % (self.step,self.t, dmdt,cvode_dt))

            if dmdt < stopping_dmdt:
                break

        if save_m_steps is not None:
            self.save_m()

        if save_vtk_steps is not None:
            self.save_vtk()
Exemplo n.º 19
0
Arquivo: llg.py Projeto: owlas/fidimag
    def __init__(self, mesh, name='unnamed', integrator='sundials'):
        """Simulation object.

        *Arguments*

          name : the Simulation name (used for writing data files, for examples)

        """

        self.t = 0
        self.name = name
        self.mesh = mesh
        self.n = mesh.n
        self.n_nonzero = mesh.n
        self.unit_length = mesh.unit_length
        self._alpha = np.zeros(self.n, dtype=np.float)
        self._Ms = np.zeros(self.n, dtype=np.float)
        self._Ms_inv = np.zeros(self.n, dtype=np.float)
        self.spin = np.ones(3 * self.n, dtype=np.float)
        self.spin_last = np.ones(3 * self.n, dtype=np.float)
        self._pins = np.zeros(self.n, dtype=np.int32)
        self.field = np.zeros(3 * self.n, dtype=np.float)
        self.dm_dt = np.zeros(3 * self.n, dtype=np.float)
        self._skx_number = np.zeros(self.n, dtype=np.float)
        self.interactions = []
        self.pin_fun = None
        self.integrator_tolerances_set = False
        self.step = 0

        if integrator == "sundials":
            self.integrator = SundialsIntegrator(self.spin, self.sundials_rhs)
        elif integrator == "euler" or integrator == "rk4":
            self.integrator = StepIntegrator(self.spin, self.step_rhs, integrator)
        else:
            raise NotImplemented("integrator must be sundials, euler or rk4")

        self.saver = DataSaver(self, name + '.txt')

        self.saver.entities['E_total'] = {
            'unit': '<J>',
            'get': lambda sim: sim.compute_energy(),
            'header': 'E_total'}

        self.saver.entities['m_error'] = {
            'unit': '<>',
            'get': lambda sim: sim.compute_spin_error(),
            'header': 'm_error'}

        self.saver.entities['skx_num'] = {
            'unit': '<>',
            'get': lambda sim: sim.skyrmion_number(),
            'header': 'skx_num'}

        self.saver.entities['rhs_evals'] = {
            'unit': '<>',
            'get': lambda sim: self.integrator.rhs_evals,
            'header': 'rhs_evals'}

        self.saver.entities['real_time'] = {
            'unit': '<s>',
            'get': lambda _: time.time(),  # seconds since epoch
            'header': 'real_time'}

        self.saver.update_entity_order()

        # This is for old C files codes using the xperiodic variables
        self.xperiodic, self.yperiodic, self.zperiodic = mesh.periodicity

        self.vtk = SaveVTK(self.mesh, name=name)

        self.set_default_options()
Exemplo n.º 20
0
    def __init__(self, mesh, name='unnamed', use_jac=False):
        """Simulation object.
        *Arguments*
          name : the Simulation name (used for writing data files, for examples)
        """

        self.t = 0
        self.name = name
        self.mesh = mesh
        self.n = mesh.n
        self.n_nonzero = mesh.n
        self.unit_length = mesh.unit_length
        self._alpha = np.zeros(self.n, dtype=np.float)
        self._mu_s = np.zeros(self.n, dtype=np.float)
        self._mu_s_inv = np.zeros(self.n, dtype=np.float)

        self.spin = np.ones(3 * self.n, dtype=np.float)
        self.spin_last = np.ones(3 * self.n, dtype=np.float)
        self._pins = np.zeros(self.n, dtype=np.int32)
        self.field = np.zeros(3 * self.n, dtype=np.float)
        self.dm_dt = np.zeros(3 * self.n, dtype=np.float)
        self._skx_number = np.zeros(self.n, dtype=np.float)

        self.interactions = []
        self.pin_fun = None

        self.step = 0

        self.saver = DataSaver(self, name + '.txt')

        self.saver.entities['E_total'] = {
            'unit': '<J>',
            'get': lambda sim: sim.compute_energy(),
            'header': 'E_total'}

        self.saver.entities['m_error'] = {
            'unit': '<>',
            'get': lambda sim: sim.compute_spin_error(),
            'header': 'm_error'}

        self.saver.entities['skx_num'] = {
            'unit': '<>',
            'get': lambda sim: sim.skyrmion_number(),
            'header': 'skx_num'}

        self.saver.update_entity_order()

        # This is only for old C files using the xperiodic variable
        self.xperiodic, self.yperiodic = mesh.periodicity[0], mesh.periodicity[1]

        self.vtk = SaveVTK(self.mesh, name=name)

        if use_jac is not True:
            self.vode = cvode.CvodeSolver(self.spin, self.sundials_rhs)
        else:
            self.vode = cvode.CvodeSolver(
                self.spin, self.sundials_rhs, self.sundials_jtn)

        self.set_default_options()

        self.set_tols()

        # When initialising the integrator in the self.vode call, the CVOde
        # class calls the set_initial_value function (with flag_m=0), which
        # initialises a new integrator and allocates memory in this process.
        # Now, when we set the magnetisation, we will use the same memory
        # setting this flag_m to 1, so instead of calling CVodeInit we call
        # CVodeReInit. If don't, memory is allocated in every call of set_m
        self.flag_m = 1
Exemplo n.º 21
0
    def __init__(self, mesh, name='unnamed', integrator='sundials'):
        """Simulation object.

        *Arguments*

          name : the Simulation name (used for writing data files, for examples)

        """

        self.t = 0
        self.name = name
        self.mesh = mesh
        self.n = mesh.n
        self.n_nonzero = mesh.n
        self.unit_length = mesh.unit_length
        self._alpha = np.zeros(self.n, dtype=np.float)
        self._Ms = np.zeros(self.n, dtype=np.float)
        self._Ms_inv = np.zeros(self.n, dtype=np.float)
        self.spin = np.ones(3 * self.n, dtype=np.float)
        self.spin_last = np.ones(3 * self.n, dtype=np.float)
        self._pins = np.zeros(self.n, dtype=np.int32)
        self.field = np.zeros(3 * self.n, dtype=np.float)
        self.dm_dt = np.zeros(3 * self.n, dtype=np.float)
        self._skx_number = np.zeros(self.n, dtype=np.float)
        self.interactions = []
        self.pin_fun = None
        self.integrator_tolerances_set = False
        self.step = 0

        if integrator == "sundials":
            self.integrator = SundialsIntegrator(self.spin, self.sundials_rhs)
        elif integrator == "euler" or integrator == "rk4":
            self.integrator = StepIntegrator(self.spin, self.step_rhs, integrator)
        else:
            raise NotImplemented("integrator must be sundials, euler or rk4")

        self.saver = DataSaver(self, name + '.txt')

        self.saver.entities['E_total'] = {
            'unit': '<J>',
            'get': lambda sim: sim.compute_energy(),
            'header': 'E_total'}

        self.saver.entities['m_error'] = {
            'unit': '<>',
            'get': lambda sim: sim.compute_spin_error(),
            'header': 'm_error'}

        self.saver.entities['skx_num'] = {
            'unit': '<>',
            'get': lambda sim: sim.skyrmion_number(),
            'header': 'skx_num'}

        self.saver.entities['rhs_evals'] = {
            'unit': '<>',
            'get': lambda sim: self.integrator.rhs_evals,
            'header': 'rhs_evals'}

        self.saver.entities['real_time'] = {
            'unit': '<s>',
            'get': lambda _: time.time(),  # seconds since epoch
            'header': 'real_time'}

        self.saver.update_entity_order()

        # This is for old C files codes using the xperiodic variables
        self.xperiodic, self.yperiodic, self.zperiodic = mesh.periodicity

        self.vtk = SaveVTK(self.mesh, name=name)

        self.set_default_options()
Exemplo n.º 22
0
    def __init__(self, mesh, name='unnamed', use_jac=False):
        """Simulation object.
        *Arguments*
          name : the Simulation name (used for writing data files, for examples)
        """

        self.t = 0
        self.name = name
        self.mesh = mesh
        self.n = mesh.n
        self.n_nonzero = mesh.n
        self.unit_length = mesh.unit_length
        self._alpha = np.zeros(self.n, dtype=np.float)
        self._mu_s = np.zeros(self.n, dtype=np.float)
        self._mu_s_inv = np.zeros(self.n, dtype=np.float)

        self.spin = np.ones(3 * self.n, dtype=np.float)
        self.spin_last = np.ones(3 * self.n, dtype=np.float)
        self._pins = np.zeros(self.n, dtype=np.int32)
        self.field = np.zeros(3 * self.n, dtype=np.float)
        self.dm_dt = np.zeros(3 * self.n, dtype=np.float)
        self._skx_number = np.zeros(self.n, dtype=np.float)

        self.interactions = []
        self.pin_fun = None

        self.step = 0

        self.saver = DataSaver(self, name + '.txt')

        self.saver.entities['E_total'] = {
            'unit': '<J>',
            'get': lambda sim: sim.compute_energy(),
            'header': 'E_total'
        }

        self.saver.entities['m_error'] = {
            'unit': '<>',
            'get': lambda sim: sim.compute_spin_error(),
            'header': 'm_error'
        }

        self.saver.entities['skx_num'] = {
            'unit': '<>',
            'get': lambda sim: sim.skyrmion_number(),
            'header': 'skx_num'
        }

        self.saver.update_entity_order()

        # This is only for old C files using the xperiodic variable
        self.xperiodic, self.yperiodic = mesh.periodicity[0], mesh.periodicity[
            1]

        self.vtk = SaveVTK(self.mesh, name=name)

        if use_jac is not True:
            self.vode = cvode.CvodeSolver(self.spin, self.sundials_rhs)
        else:
            self.vode = cvode.CvodeSolver(self.spin, self.sundials_rhs,
                                          self.sundials_jtn)

        self.set_default_options()

        self.set_tols()