TetraX.2: Cleaner, more convenient and reliable#

We’re thrilled to introduce TetraX.2, a leap forward for our micromagnetic modeling package! This major update brings several improvements, from a complete architectural overhaul of samples to enhanced tools for running experiments and analyzing results. With a focus on simplicity, flexibility, and performance, TetraX.2 represents a significant step forward in enabling researchers to model and study spin waves in numerical micromagnetism with ease.

Among many structural changes, we have worked hard on a revamped material handling, better organization of numerical experiments, modularized magnetic interactions to ensure smoother, more efficient workflows. In addition, we implement a great deal of new features, such as integrated plotting of numerical results, a more reliable relaxation method using torque minimization, and a completely new geometry type to model axially-symmetric waveguides!

Read on to discover the big and small changes that come with TetraX.2!

_images/LogoV2.png

Sample, mesh and material#

The architecture around samples has been completely redesigned to be safer and simpler. In TetraX versions 1.x, the AbstractSample sample object had too many responsibilities which are now subdivided into class components such as handling the mesh and the material separately. Different traits such as the type of geometry (waveguide, layer, etc.) or the magnetic order (FM, AFM) were configured through multiple-inheritance and created by the create_sample() factory. This is much simpler now.

A simpler Sample class#

In version 2, there is a single Sample class whose properties are configured through composition. Responsbilites such as mananging the geometry or the material of the sample have been outsourced to the new SampleMesh and SampleMaterial which are accessible as the mesh and material attributes of a sample. Instead of calling the old create_sample() function, now simply create a sample in the following way.

from tetrax import Sample, geometries

sample = Sample(geometries.waveguide.tube(20, 30, 2))

# is the same as

sample = Sample(geometries.waveguide.tube(inner_radius=20, outer_radius=30, cell_size=2),
                geometry_type="waveguide",
                magnetic_order="FM"
                )

sample.show() # works just like before

Note that, it is not possible anymore to create a Sample without specifying a mesh (which are now organized more neatly in the tetrax.geometries module). Previously, the unnecessary possibility to have a sample without mesh always lead to problems. Similarly, by default, the magnetization is always set to the \(z\) direction.

Note

When choosing a geometry from our template library, the GeometryType is automatically inferred from the mesh and does not need to be supplied explicitely. Moreover, the MagneticOrder always defaults to ferromagnetic.

New SampleMesh class#

As before, upon creation of a sample, all necessary matrices and quantities assosiated with the geometry of the mesh are precalculated. These are now managed by the new SampleMesh which is accesible as the mesh attribute of the respective sample. For example, the number of nodes in a sample is now accessed with

sample.mesh.nx
# returns the number of nodes in the mesh of the sample

New SampleMaterial and MaterialParameter classes#

Material properties of samples are now not accesible anymore directly as sample attributes, but instead managed by the SampleMaterial that is accessed through the material attribute of a sample. This unloads responsibility from the Sample class and removes the possibility to set non-existent material parameters. In versions 1.x, material parameters could be set as

sample.Msat = 1e3 # DEPRECATED!
sample.Ms = 1e3  # did not raise an error

which is now done via

sample.material["Msat"] = 1e3
sample.material["Ms"] = = 1e3  # raises an error

Other than that, material paramaters can be set as before. Each parameter like "Msat" is stored as a MaterialParameter object that provides metadata and some convencience methods. For example

sample.material["Msat"]
# will print a html summary of the material parameter,
# including unit, current value, average, etc.

sample.material["Msat"].value
# will return the current value of the parameter

sample.material["Msat"].average
# will return the sample average of the parameter

For more details, see SampleMaterial and MaterialParameter within the API reference.

ExperimentSetup and results#

We were not happy with the way that numerical experiments were organized. Somehow, the necessity to create an ExperimentalSetup to manage numerical experiments felt off and was also not very useful when wanting to organize several experiments on the same sample. This is now completely overhauled for more modularity and automatic documentation. First of all, we get rid of the experimental setup and included its attributes (microwave antenna and external field) into the properties of the sample. Furthermore, most experiments are now implemented directly as top-level functions that take a particular sample as a parameter and generate a Result as an output. For example the eigenmodes() experiment acts as

eigen_result = tx.experiments.eigenmodes(sample)

and returns an EigenResult. Depending on the particular experiment, each Result provides methods to plot the calculated data and perform post-processing on it. For example, the EigenResult allows calculating the power absorption of the a spin-wave spectrum with respect to a specified microwave antenna using the EigenResult.absorption() method (which returns an AbsorptionResult).

By default, each Result is automatically saved to the disk. This includes the calculated data itself, as well as some metadata and the parameters used to perform the calculations. For example, running the eigenmodes() experiment as written above produces the following output on the disk.

my_sample/
    eigen/
        external_field.vtk
        eigenvalue_dataframe.csv
        mode_profiles/
            ...
        equilibrium.vtk
        report.json

with a report.json file that contains material parameters, experiment configuration, metadata (such as the version of TetraX that has been used) and can be used, for example, for data archiving. By default, running the same experiment twice will overwrite the old results. In order to circumvent this, results can be equipped with a label when running the experiment. For example,

eigen_result_A = tx.experiments.eigenmodes(sample, label="A", **some_settings)

...

eigen_result_B = tx.experiments.eigenmodes(sample, label="B", **different_settings)

produces

my_sample/
    eigen_A/
        ...
    eigen_B/
        ...

For more information on how to run experiments, see the User Guide.

Interactions#

While different magnetic interactions have been more or less fixed throughout previous version of TetraX, they are now modularized as Interaction s in the tetrax.interactions module. The different interactions of a sample are now listed in its interactions attritube. Each interaction has an InteractionName that allows to address it. For example

sample.interactions
# returns a list of the different interactions in the sample,

sample.get_interaction("exchange")
# returns reference to the ExchangeInteration object of the sample,

sample.get_field("dipole")
# returns the current dipolar get_field and

sample.get_energy("cubic_anisotropy")
# returns the current cubic-anisotropy energy.

In this way, the different interactions that participate in a particular numerical experiment can now be chosen freely. For example

zeeman = sample.get_interaction("zeeman")
exchange = sample.get_interaction("exchange")

relax(sample, interactions=[zeeman, exchange])
eigen_result = eigenmodes(sample, interactions=[zeeman, exchange])

minimizes the energy of the sample and calculates its eigenmodes only considering Zeeman and exchange interaction. This allows, for example, to subsequently perturb an eigenspectrum using additional interactions.

dipole = sample.get_interaction("dipole")
dmi_bulk = sample.get_interaction("dmi_bulk")

eigen_result.perturb_with([dipole, dmi_bulk])

which calculates the zeroth-order perturbation of an eigenspectrum with respect to dipolar and bulk Dzyaloshinskii-Moriya interaction. More details are found in the User Guide or in the example Thin film dispersion exact vs perturbation (Kalinikos & Slavin).

Torque minimization with the new relax_dynamic() experiment#

In need of a more reliable equilibration method than energy minization using the relax() experiment, we now additionally implemented relaxation based on the overdamped Landau-Lifhsitz-Gilbert equation in the new relax_dynamic() experiment. This is generally slower than energy minization but gets the job done in almost all cases where energy minimization fails. This can be come especially important when Dzyaloshinskii-Moriya interactions are included, as seen in the newly added example Channeling mixed Néel-Bloch domain walls with iDMI.

New geometry type: Axially-symmetric waveguide#

Thus far, through the WAVEGUIDE and LAYER geometr types, we have provided the oppurtunity to exploit translational symmetries in magnetic samples and directly calculate the spin-wave spectrum as a function of the wave number \(k\) along the translation axis. We are curretly working hard to expand this notion to magnetic systems with cylindrical/axial symmetries. For this, we are introducing the first of two new geometry types, namely WAVEGUIDE_AXIAL. This geometry type combines axial with translational symmetries and allow to efficiently model round wires, tubes, or multitubes by only modeling the magnetic system within a one-dimensional mesh along the radial direction. The spin-wave modes are directly calculated as function of wave vector \(k\) and azimuthal mode index \(m\) in the angular direction.

A demonstration is found in the updated Dispersion of a nanotube in vortex state example or in the newly added Dispersion of a solid wire in axial magnetization state example. We hope you are having fun with this new geometry type. Meanwhile, we are working on finalizing the CONFINED_AXIAL geometry type, allowing to model confined samples with axial symmetry (such as ring, disks, or toroids).

Other new features and changes#

  • A couple of template materials have been added, which are available in the new tetrax.materials module. They can be listed by accessing tetrax.materials.available.

  • Similarly, the library of template geometries tetrax.geometries has been restructured and subdivided into more meaningful submodules such as tetrax.geometries.waveguide or tetrax.geometries.layer. All template geometries have been cleaned up (e.g. keywords have more meaningful names) and can be listed using tetrax.geometries.available.

  • All template vectorfields have been cleaned up and can be accessed with tetrax.vectorfields.available.

  • Both vectorfields and geometries can now be read from a file using tetrax.geometries.from_file() or tetrax.vectorfields.from_file().

  • A new microwave antenna (MultiStriplineAntenna) has been added, which allows to model an arbitrary series of stripline antennae (e.g. to model a meandering antenna).

  • All examples have been revised to comply with version 2. Furthermore, new examples have been added.

  • The number of CPU cores num_cpus when calculating an eigen spectrum is now -1 by default (all cores are used).

  • Frequencies are now stored in Hz, wave vectors in rad/m.

  • The symmetry breaking direction material["e_d"] of the InterfacialDMI can now be chosen freely.

  • Both InterfacialDMI and BulkDMI now satisfy proper boundary conditions at the sample boundary. Open boundary conditions can be recovered by setting the open_boundary attribute of the respective interaction to True, which requires to call the update_matrices() function of the interaction. Setting open boundary conditions should be done with care (that’s why this feature is kind of hidden).

  • The plot method of the sample has been removed and its functionality absorbed into the show method.