#!/usr/bin/env python # coding: utf-8 # # Tutorial 01: Deriving an energy term # # > Interactive online tutorial: # > [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/ubermag/micromagneticmodel/master?filepath=docs%2Fipynb%2Findex.ipynb) # # All energy terms in `micromagneticmodel` are in `micromagneticmodel/hamiltonian` directory. They are all derived from `micromagneticmodel.hamiltonian.EnergyTerm` base class. # # For instance, let us say we want to implement an energy term with following specifications: # # | property | value | # | --- | --- | # | name | `SpecialEnergy` | # | expression | $U\mathbf{m}\cdot\mathbf{m}$ | # | parameter | $U$ | # | parameter properties | $U \ge 0$, can be spatially varying | # # The energy term class would be: # In[1]: import micromagneticmodel as mm class SpecialEnergy(mm.EnergyTerm): def __init__(self, U, name='specialenergy'): self.U = U self.name = name # Now, we can try to instantiate it # In[2]: try: se = SpecialEnergy(U=3) except TypeError: print('Exception raised.') # An exception was raised because `_repr` and `_latex` properties must be implemented. Therefore, an extended implementation of the class is: # In[3]: class SpecialEnergy(mm.EnergyTerm): _latex = r'$U\mathbf{m}\cdot\mathbf{m}$' def __init__(self, U, name='specialenergy'): self.U = U self.name = name @property def _repr(self): return f'SpecialEnergy(U={self.U}, name=\'{self.name}\')' # We can try to instantiate the class again: # In[4]: se = SpecialEnergy(U=3) se # The energy object is created. The last thing we have to impose on the energy class is the typesystem. More precisely, we have to make sure no negative $U$ values are allowed and that `name` attribute accepts only valid Python variable names. This is done by using `ubermagutil`. Full documentation can be found [here](https://ubermagutil.readthedocs.io/en/latest/). # In[5]: import ubermagutil.typesystem as ts import discretisedfield as df @ts.typesystem(U=ts.Parameter(descriptor=ts.Scalar(unsigned=True), otherwise=df.Field), name=ts.Name()) class SpecialEnergy(mm.EnergyTerm): _latex = r'$U\mathbf{m}\cdot\mathbf{m}$' def __init__(self, U, name='specialenergy'): self.U = U self.name = name @property def _repr(self): return f'SpecialEnergy(U={self.U}, name=\'{self.name}\')' # If we now attempt to pass invalid input arguments, exceptions are raised. # In[6]: try: se = SpecialEnergy(U=-3, name='valid_name') # negative U except ValueError: print('Exception raised.') # In[7]: try: se = SpecialEnergy(U=3, name='invalid name') # name contains space except ValueError: print('Exception raised.') # Some of the properties and methods of the implemented energy term are: # In[8]: se = SpecialEnergy(U=5e-5) # In[9]: se.U # In[10]: se.name # In[11]: se # In[12]: repr(se) # Finally, the class we have just implemented is going to be inherited by a specific micromagnetic calculator, where `_script` method is going to be implemented. # In[13]: try: se._script except NotImplementedError: print('Exception raised.') # ## Other # # Full description of all existing descriptors can be found in the [API Reference](https://micromagneticmodel.readthedocs.io/en/latest/?badge=latest).