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 Mandmode M)a few other parameters are hard-coded (details below)
annealing in the initial phase is not supported
only
exchange,momfile,posfileandrestartfileare 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:
we create a
Simulationobjectwe call a method of that object to run a simulation (either
runortemperature_sweep, we will in the following only talk aboutrunas 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:
exchangescontaining the exchange coupling constantsposfilecontaining the position of the atoms in the unit cellmomfilecontaining 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:
a string containing the content for
inpsd.data 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
run1.Ms
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
sweep.Ms
sweep.Cv
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.