UppASD Python interface#

This notebook explains the Python interface to UppASD provided in mammos_spindynamics.uppasd in detail.

import os

import mammos_dft
from mammos_spindynamics import uppasd

os.environ.setdefault("OMP_NUM_THREADS", "1");  # Use only 1 thread (needed for Binder)

Running simulations#

Warning

In order to keep the runtime of this notebook short we use unreasonably small values for ncell, ip_mcnstep and mcnstep.

For meaningfull simulations you will have to use higher values, suitable starting points could be ncell 32 32 32, ip_mcnstep 25000, mcnstep 50000

All simulations in this notebook will be performed for Co2Fe2H4 for which the required static inputs are available in the database of mammos_dft.

Co2Fe2H4_data = mammos_dft.db.get_uppasd_properties(chemical_formula="Co2Fe2H4")

Single simulation without having inpsd.dat#

The main input file for UppASD is called inpsd.dat, details about this file can be found in the UppASD documentation.

The Python interface in this package can create these input files. Currently, it has a number of assumptions hard-coded:

  • we always run Monte Carlo simulations for the initial and the measurement phase (ip_mode M and mode M)

  • a few other parameters are hard-coded (details below)

  • annealing in the initial phase is not supported

  • only exchange, momfile, posfile and restartfile are supported as input files

The main object to control simulations is uppasd.Simulation. We can first get a list of required parameters for the input file:

uppasd.Simulation().required_parameters
{'alat', 'cell', 'initmag', 'ip_temp', 'maptype', 'posfiletype', 'temp'}

These parameters depend heavily on the simulation requirements and the the format of the additional input files, and therefore have to be provided by the user.

A few more parameters can be controlled by the user but have sensible defaults. To get a list of all available parameters run:

uppasd.Simulation().allowed_parameters
{'alat',
 'cell',
 'initmag',
 'ip_mcnstep',
 'ip_temp',
 'maptype',
 'mcnstep',
 'ncell',
 'posfiletype',
 'temp'}

We run simulations in two steps:

  1. we create a Simulation object

  2. we call a method of that object to run a simulation (either run or temperature_sweep, we will in the following only talk about run as they behave nearly identical)

We have to provide all required arguments by the time we call run. We can either pass them when creating the object or when calling the run method (we can freely choose for each parameter). Parameters passed to the run method are treated special and can simplify accessing the outputs later.

Therefore, as a rule of thumb:

  • pass all parameters that do not vary between simulation runs when creating the object (e.g. cell, alat, …)

  • pass all parameters that you vary when calling run (e.g. ip_temp/temp, …)

In addition to these parameters we also need to pass paths to three files:

  • exchanges containing the exchange coupling constants

  • posfile containing the position of the atoms in the unit cell

  • momfile containing the magnetic moments of the atoms in the unit cell

These files will be copied into the run directory (more on that directory below) and in the resulting input file relative paths will be used. Note: the files will be renamed to exchange, posfile, and momfile, the original names are lost.

We now create a Simulation object. In this notebook we are only interested in varying temperature, so we pass all other parameters now:

simulation = uppasd.Simulation(
    cell=Co2Fe2H4_data.cell,
    alat=2.65e-10,
    ncell=(12, 12, 12),  # to keep the simulations short in this notebook
    posfile=Co2Fe2H4_data.posfile,
    posfiletype=Co2Fe2H4_data.posfiletype,
    momfile=Co2Fe2H4_data.momfile,
    exchange=Co2Fe2H4_data.exchange,
    maptype=Co2Fe2H4_data.maptype,
    initmag=3,
    ip_mcnstep=100,  # to keep the simulations short in this notebook
    mcnstep=100,  # to keep the simulations short in this notebook
)
simulation
Simulation(
    ncell=(12, 12, 12),
    ip_mcnstep=100,
    mcnstep=100,
    cell=array([[ 2.645345  ,  0.        ,  0.        ],
       [-1.32267223,  2.29090033,  0.        ],
       [ 0.        ,  0.        ,  8.539476  ]]),
    alat=2.65e-10,
    posfile=PosixPath('/home/petrocch/mammos/mammos-spindynamics/.pixi/envs/default/lib/python3.11/site-packages/mammos_dft/data/0001/posfile'),
    posfiletype='D',
    momfile=PosixPath('/home/petrocch/mammos/mammos-spindynamics/.pixi/envs/default/lib/python3.11/site-packages/mammos_dft/data/0001/momfile'),
    exchange=PosixPath('/home/petrocch/mammos/mammos-spindynamics/.pixi/envs/default/lib/python3.11/site-packages/mammos_dft/data/0001/exchange'),
    maptype=2,
    initmag=3,
)

We can now call the run method to actually perform a simulation. We need to pass a directory name where the output will be written to and the missing parameters. For temperature we can use a convenience: we can pass T and the same value will be used for ip_temp and temp.

We can pass an additional human-readable description, which will be stored as additional metadata.

sim_result = simulation.run(out="Co2Fe2H4", T=20, description="Without custom inpsd.dat")
Running UppASD in Co2Fe2H4/0-run ... simulation finished, took 0:00:04

The run method displays some status information, telling us where the output has been written to and how long the simulation took.

The run method returns an object that provides access to some of the simulation output. We will discuss data analysis in detail in a later section, here we only show the attribute restartfile: the restart file of the simulation; we will pass it to a simulation further down.

sim_result.restartfile
PosixPath('Co2Fe2H4/0-run/restart._UppASD_.out')

We can also get the input file that would be run by calling the method create_input_files. This method returns two objects:

  1. a string containing the content for inpsd.dat

  2. a dictionary of auxilary files that will be copied to the run directory; keys will be the file names in the run directory, values are the original paths

We need to pass parameters similar to the run method.

inp_content, aux_files = simulation.create_input_files(T=20)
print(inp_content)
cell  2.645345 0.0 0.0
      -1.3226722338393606 2.290900329657839 0.0
      0.0 0.0 8.539476
alat  2.65e-10
ncell 12 12 12
bc    P P P
sym   0

posfile     ./posfile
posfiletype D
momfile     ./momfile
exchange    ./exchange
maptype     2

initmag 3


ip_mode    M
ip_temp    20
ip_mcnstep 100

mode    M
temp    20
mcnstep 100

plotenergy   1
do_proj_avrg Y
do_cumu      Y
aux_files
{'exchange': PosixPath('/home/petrocch/mammos/mammos-spindynamics/.pixi/envs/default/lib/python3.11/site-packages/mammos_dft/data/0001/exchange'),
 'posfile': PosixPath('/home/petrocch/mammos/mammos-spindynamics/.pixi/envs/default/lib/python3.11/site-packages/mammos_dft/data/0001/posfile'),
 'momfile': PosixPath('/home/petrocch/mammos/mammos-spindynamics/.pixi/envs/default/lib/python3.11/site-packages/mammos_dft/data/0001/momfile')}

We can overwrite parameters defined when creating the object at the time where we call the run method (or equally create_input_files). We demonstrate this here for ncell and mcnstep. We also pass two different temperatures:

inp_content, _ = simulation.create_input_files(
    ncell=(36, 36, 36),
    mcnstep=50_000,
    ip_temp=50,
    temp=55,
)
print(inp_content)
cell  2.645345 0.0 0.0
      -1.3226722338393606 2.290900329657839 0.0
      0.0 0.0 8.539476
alat  2.65e-10
ncell 36 36 36
bc    P P P
sym   0

posfile     ./posfile
posfiletype D
momfile     ./momfile
exchange    ./exchange
maptype     2

initmag 3


ip_mode    M
ip_temp    50
ip_mcnstep 100

mode    M
temp    55
mcnstep 50000

plotenergy   1
do_proj_avrg Y
do_cumu      Y

UppASD can use a restart file from a previous simulation as starting point for the new simulation. To do that, we need to set initmag 4 and we need to pass an additional argument restartfile. We can use the object returned from the run above to restart from that run. Again we show just the modified input file and the auxilary files, which show where the restartfile is coming from:

inp_content, aux_files = simulation.create_input_files(
    T=20,
    initmag=4,
    restartfile=sim_result.restartfile,
)
print(inp_content)
aux_files
cell  2.645345 0.0 0.0
      -1.3226722338393606 2.290900329657839 0.0
      0.0 0.0 8.539476
alat  2.65e-10
ncell 12 12 12
bc    P P P
sym   0

posfile     ./posfile
posfiletype D
momfile     ./momfile
exchange    ./exchange
maptype     2

initmag 4
restartfile ./restartfile

ip_mode    M
ip_temp    20
ip_mcnstep 100

mode    M
temp    20
mcnstep 100

plotenergy   1
do_proj_avrg Y
do_cumu      Y
{'exchange': PosixPath('/home/petrocch/mammos/mammos-spindynamics/.pixi/envs/default/lib/python3.11/site-packages/mammos_dft/data/0001/exchange'),
 'posfile': PosixPath('/home/petrocch/mammos/mammos-spindynamics/.pixi/envs/default/lib/python3.11/site-packages/mammos_dft/data/0001/posfile'),
 'momfile': PosixPath('/home/petrocch/mammos/mammos-spindynamics/.pixi/envs/default/lib/python3.11/site-packages/mammos_dft/data/0001/momfile'),
 'restartfile': PosixPath('Co2Fe2H4/0-run/restart._UppASD_.out')}

Single simulation starting from an existing inpsd.dat file#

We can also run a simulation based on an existing inpsd.dat file. In this notebook we create one using the writefile magic:

%%writefile inpsd.dat
simid Co2Fe2H4

cell  1.000000000000000  -0.500000000182990   0.000000000000000
      0.000000000000000   0.866011964524435   0.000000000000000
      0.000000000000000   0.000000000000000   3.228114436804486

ncell 12  12  12
bc    P P P
sym 0
posfile ./posfile
posfiletype D

initmag 3
momfile ./momfile
maptype 2
exchange ./jfile
ip_mode M
ip_temp 20 
ip_mcnstep 500

mode M
temp 20
mcnstep 500
plotenergy 1
do_proj_avrg Y
do_cumu Y

alat 2.6489381562e-10
Writing inpsd.dat

We can now pass the path to this file as additional parameter to the simulation class. The file references ./posfile, ./momfile and ./jfile which do not exist in our working directory. Therefore, we need to pass paths to these files:

simulation = uppasd.Simulation(
    inpsd="inpsd.dat",
    posfile=Co2Fe2H4_data.posfile,
    momfile=Co2Fe2H4_data.momfile,
    exchange=Co2Fe2H4_data.exchange,
)
simulation
Simulation(
    inpsd=inpsd.dat,
    posfile=PosixPath('/home/petrocch/mammos/mammos-spindynamics/.pixi/envs/default/lib/python3.11/site-packages/mammos_dft/data/0001/posfile'),
    momfile=PosixPath('/home/petrocch/mammos/mammos-spindynamics/.pixi/envs/default/lib/python3.11/site-packages/mammos_dft/data/0001/momfile'),
    exchange=PosixPath('/home/petrocch/mammos/mammos-spindynamics/.pixi/envs/default/lib/python3.11/site-packages/mammos_dft/data/0001/exchange'),
)

Passing these paths is not required when the files exist in the location specified in the input file (relative to the current working directory).

Note: the files will always be copied to the run directory and their names will be changed to posfile, momfile and exchange to ensure that the run-directory is fully self-contained.

We can now run a simulation as before. The inpsd.dat file is complete, so we don’t have to pass any simulation parameters:

simulation.run("Co2Fe2H4", description="Custom inpsd.dat file");
Running UppASD in Co2Fe2H4/1-run ... simulation finished, took 0:00:18

We can use the same overwriting logic shown in the first section to also overwrite values in the inpsd.dat file. We can pass additional parameters either when creating the simulation object or when calling the run/create_input_files methods. Here, we show a modified input file:

inp_content, _ = simulation.create_input_files(ncell=[20, 20, 20], initmag=1, T=1000)
print(inp_content)
simid Co2Fe2H4

cell  1.000000000000000  -0.500000000182990   0.000000000000000
      0.000000000000000   0.866011964524435   0.000000000000000
      0.000000000000000   0.000000000000000   3.228114436804486

ncell 20 20 20
bc    P P P
sym 0
posfile ./posfile
posfiletype D

initmag 1
momfile ./momfile
maptype 2
exchange ./exchange
ip_mode M
ip_temp 1000
ip_mcnstep 500

mode M
temp 1000
mcnstep 500
plotenergy 1
do_proj_avrg Y
do_cumu Y

alat 2.6489381562e-10

Temperature sweep#

A common use case is running simulations for several different temperatures to extract temperature-dependent intrinsic properties. To simplify this task a dedicated method temperature_sweep is provided. First, we re-create the simulation object from cell 5.

simulation = uppasd.Simulation(
    cell=Co2Fe2H4_data.cell,
    alat=2.65e-10,
    ncell=(12, 12, 12),  # to keep the simulations short in this notebook
    posfile=Co2Fe2H4_data.posfile,
    posfiletype=Co2Fe2H4_data.posfiletype,
    momfile=Co2Fe2H4_data.momfile,
    exchange=Co2Fe2H4_data.exchange,
    maptype=Co2Fe2H4_data.maptype,
    initmag=3,
    ip_mcnstep=100,  # to keep the simulations short in this notebook
    mcnstep=100,  # to keep the simulations short in this notebook
)
simulation
Simulation(
    ncell=(12, 12, 12),
    ip_mcnstep=100,
    mcnstep=100,
    cell=array([[ 2.645345  ,  0.        ,  0.        ],
       [-1.32267223,  2.29090033,  0.        ],
       [ 0.        ,  0.        ,  8.539476  ]]),
    alat=2.65e-10,
    posfile=PosixPath('/home/petrocch/mammos/mammos-spindynamics/.pixi/envs/default/lib/python3.11/site-packages/mammos_dft/data/0001/posfile'),
    posfiletype='D',
    momfile=PosixPath('/home/petrocch/mammos/mammos-spindynamics/.pixi/envs/default/lib/python3.11/site-packages/mammos_dft/data/0001/momfile'),
    exchange=PosixPath('/home/petrocch/mammos/mammos-spindynamics/.pixi/envs/default/lib/python3.11/site-packages/mammos_dft/data/0001/exchange'),
    maptype=2,
    initmag=3,
)

The method temperature_sweep behaves very similar to run, but always requires the argument T, which must contain all temperatures for which simulations should be performed. For each temperature, it will run a simulation, using the same temperature for both ip_temp and temp.

By default, each simulation will use the restartfile from the previous simulation as starting point. Therefore, you should ensure that your temperatures are ordered.

simulation.temperature_sweep(T=[50, 100, 150], out="Co2Fe2H4")
Running simulations for 3 different temperatures:
    [50, 100, 150]
T=50: Running UppASD in Co2Fe2H4/2-temperature_sweep/0-run ... simulation finished, took 0:00:04
T=100: Running UppASD in Co2Fe2H4/2-temperature_sweep/1-run ... simulation finished, took 0:00:04
T=150: Running UppASD in Co2Fe2H4/2-temperature_sweep/2-run ... simulation finished, took 0:00:04
TemperatureSweepData('Co2Fe2H4/2-temperature_sweep')

Analyzing simulation output#

In the last part of this notebook we discuss data analysis in more detail.

To load all data, we can use uppasd.read and give it the name of the base directory where we have stored simulation outputs:

data = uppasd.read("Co2Fe2H4")
data
MammosUppasdData('Co2Fe2H4')

We can use the info method to get some metadata for all runs:

data.info()
name description time_elapsed T
0 0-run Without custom inpsd.dat 0:00:04 20
1 1-run Custom inpsd.dat file 0:00:18 NaN
2 2-temperature_sweep None [50, 100, 150]

We have a total of three outputs, first the two single runs and last the temperature sweep.

In the info output we can also see the advantage of passing parameters to run/temperature_sweep instead of when creating the object: the temperature information T is only present for the runs where we have passed the temperature to run/temperature_sweep.

We can access individual runs/sweeps either by index:

data[0]
RunData('Co2Fe2H4/0-run')
data[-1]
TemperatureSweepData('Co2Fe2H4/2-temperature_sweep')

or using the get method (to which we can pass any unique identifier in the info table):

data.get(name="1-run")
RunData('Co2Fe2H4/1-run')
data.get(T=20)
RunData('Co2Fe2H4/0-run')

Single run#

For individual runs, we get an object that provides access to the simulation output:

run1 = data[1]
run1.info()
name description time_elapsed
0 1-run Custom inpsd.dat file 0:00:18
run1.T
ThermodynamicTemperature(value=20.0, unit=K)
run1.Ms
SpontaneousMagnetization(value=1156473.0901440205, unit=A / m)
run1.restartfile
PosixPath('Co2Fe2H4/1-run/restart.Co2Fe2H4.out')
run1.exchange
PosixPath('Co2Fe2H4/1-run/exchange')

For a list of all available attributes refer to the API.

Temperature sweep#

A temperature sweep again consists of multiple runs, which are identical to single runs.

sweep = data.get(name="2-temperature_sweep")
sweep.info()
name description time_elapsed T initmag restartfile
0 0-run 0:00:04 50 NaN NaN
1 1-run 0:00:04 100 4.0 Co2Fe2H4/2-temperature_sweep/0-run/restart._Up...
2 2-run 0:00:04 150 4.0 Co2Fe2H4/2-temperature_sweep/1-run/restart._Up...

In addition, we have access to combined simulation results for the whole sweep:

sweep.T
ThermodynamicTemperature(value=[ 50. 100. 150.], unit=K)
sweep.Ms
SpontaneousMagnetization(value=[61805.79117509 60670.30445491 59511.3329604 ], unit=A / m)
sweep.Cv
IsochoricHeatCapacity(value=[4.00437719 4.09909896 4.19382074], unit=J / K)

Directory structure#

Sometimes it can be useful to browse the directory structure outside Python. Therefore, we will explain it in the following.

Each run/sweep has its own directory. They are consecutively numbered. In addition there is a metadata file mammos_spindynamics.yaml.

!tree -L 1 Co2Fe2H4/
Co2Fe2H4/
├── 0-run
├── 1-run
├── 2-temperature_sweep
└── mammos_spindynamics.yaml

4 directories, 1 file

Each run has its own directory following the naming scheme <index>-run. In that directory all inputs and outputs are stored. In addition to “normal” uppasd inputs/outputs there are uppasd_stdout.txt and uppasd_stderr.txt, which contain the captured output and error streams from running UppASD and mammos_spindynamics.yaml with additional metadata.

!tree Co2Fe2H4/0-run
Co2Fe2H4/0-run
├── averages._UppASD_.out
├── cumulants._UppASD_.json
├── cumulants._UppASD_.out
├── exchange
├── inpsd.dat
├── inp._UppASD_.json
├── mammos_spindynamics.yaml
├── mcinitial._UppASD_.out
├── momfile
├── posfile
├── projavgs._UppASD_.out
├── restart._UppASD_.out
├── totenergy._UppASD_.out
├── uppasd_stderr.txt
├── uppasd_stdout.txt
└── uppasd._UppASD_.yaml

1 directory, 16 files
!cat Co2Fe2H4/0-run/mammos_spindynamics.yaml
metadata:
  description: Without custom inpsd.dat
  index: 0
  mammos_spindynamics_version: 0.3.0
  mode: run
  time_elapsed: 0:00:04
  time_end: '2025-12-18T10:37:29'
  time_start: '2025-12-18T10:37:25'
  uppasd_git_revision: <unknown>
parameters:
  T: 20

For a sweep a nested structure is created. The outer directory is called <index>-temperature_sweep. It contains a number of runs, a metadata file mammos_spindynamics.yaml and two files with aggregated data M(T) and output.csv.

!tree -L 1 Co2Fe2H4/2-temperature_sweep
Co2Fe2H4/2-temperature_sweep
├── 0-run
├── 1-run
├── 2-run
├── mammos_spindynamics.yaml
├── M(T)
└── output.csv

4 directories, 3 files
!cat "Co2Fe2H4/2-temperature_sweep/M(T)"
    T    #Iter             <M>           <M^2>           <M^4>      U_{Binder}            \chi        C_v(tot)             <E>       <E_{exc}>       <E_{lsf}>
   50        1  1.60457113E+00  2.57464852E+00  6.62881500E+00  6.66666667E-01  0.00000000E+00  0.00000000E+00 -1.20121349E+01 -1.20121349E+01  0.00000000E+00
  100        1  1.57509219E+00  2.48091540E+00  6.15494123E+00  6.66666667E-01  0.00000000E+00  0.00000000E+00 -1.16951072E+01 -1.16951072E+01  0.00000000E+00
  150        1  1.54500355E+00  2.38703598E+00  5.69794079E+00  6.66666667E-01  0.00000000E+00  0.00000000E+00 -1.13630812E+01 -1.13630812E+01  0.00000000E+00

The file output.csv is particularly well suited for data exchange as it contains ontology information for the simulation results.

!cat Co2Fe2H4/2-temperature_sweep/output.csv
#mammos csv v2
#----------------------------------------
# Magnetization and heat capacity from UppASD
#----------------------------------------
#ThermodynamicTemperature,SpontaneousMagnetization,,IsochoricHeatCapacity,Energy
#https://w3id.org/emmo#EMMO_affe07e4_e9bc_4852_86c6_69e26182a17f,https://w3id.org/emmo/domain/magnetic_material#EMMO_032731f8-874d-5efb-9c9d-6dafaa17ef25,,https://w3id.org/emmo#EMMO_8fc576e1_3984_402b_a548_be921b4e1bf4,https://w3id.org/emmo#EMMO_31ec09ba_1713_42cb_83c7_b38bf6f9ced2
#K,A / m,,J / K,J
T,Ms,U_binder,Cv,E
50.0,61805.791175092585,0.666666667,4.00437719324309,-1.0473968346543109e-19
100.0,60670.30445491017,0.666666667,4.099098964333479,-1.0197536378169415e-19
150.0,59511.33296040089,0.666666667,4.193820735423869,-9.908026657942302e-20

If you need to read it manually you should use mammos_entity.io.entities_from_file (further details):

import mammos_entity as me

me.io.entities_from_file("Co2Fe2H4/2-temperature_sweep/output.csv")
EntityCollection(
    T=Entity(ontology_label='ThermodynamicTemperature', value=array([ 50., 100., 150.]), unit='K'),
    Ms=Entity(ontology_label='SpontaneousMagnetization', value=array([61805.79117509, 60670.30445491, 59511.3329604 ]), unit='A / m'),
    U_binder=array([0.66666667, 0.66666667, 0.66666667]),
    Cv=Entity(ontology_label='IsochoricHeatCapacity', value=array([4.00437719, 4.09909896, 4.19382074]), unit='J / K'),
    E=Entity(ontology_label='Energy', value=array([-1.04739683e-19, -1.01975364e-19, -9.90802666e-20]), unit='J'),
)

When working just with mammos_spindynamics.uppasd it is generally easier to access the data via uppasd.read as explained above because you don’t need to remember details of the directory structure.

If you would like to access additional information that is not currently exposed, please let us know.