asimtools package

asimtools.calculators module

Tools for loading and returning ASE calculator objects for use in simulations

asimtools.calculators.load_aqcat(calc_params)[source]

Load AQCat Calculator :param calc_params: args to pass to loader, including checkpoint_path :type calc_params: Dict :return: AQCat calculator :rtype: fairchem.core.common.relaxation.ase_utils.patched_calc

asimtools.calculators.load_ase_calc(calc_params)[source]

Load any builtin ASE calculator

asimtools.calculators.load_ase_dftd3(calc_params)[source]

Load any calculator with DFTD3 correction as implemented in ASE

Parameters:

calc_params (Dict) – Dictionary with 2 keys. First is d3_args which is passed to ase.calculators.dftd3.DFTD3 except for the dft argument. The second is dft_calc_id or dft_calc_params which loads the calculator to be wrapped.

Returns:

ASE calculator

Return type:

ase.calculators.calculators.Calculator

asimtools.calculators.load_calc(calculator: Dict | None = None, calc_id: str | None = None, calc_input: Dict | None = None, calc_params: Dict | None = None)[source]

Loads a calculator using a calculator dict or legacy calc_id/calc_params arguments.

Parameters:
  • calculator (Optional[Dict], optional) – Dictionary with either a calc_id key (to look up from the global or supplied calc_input) or a calc_params key (to use directly). Takes precedence over the legacy arguments if provided, defaults to None

  • calc_id (str, optional) – Deprecated — use calculator={'calc_id': ...} instead. ID/key to use to load calculator from the supplied or global calc_input file, defaults to None

  • calc_input – calc_input dictionary, same form as calc_input yaml :type calc_input: Optional[Dict], optional

  • calc_params (Optional[Dict], optional) – Deprecated — use calculator={'calc_params': ...} instead. calc_params dictionary for a single calculator, defaults to None

Returns:

ASE calculator instance

Return type:

ase.calculators.calculators.Calculator

asimtools.calculators.load_chgnet(calc_params)[source]

Load CHGNet Calculator

https://chgnet.lbl.gov/#tutorials-and-docs

Parameters:

calc_params (Dict) – args to pass to loader

Returns:

CHGNet calculator

Return type:

chgnet.model.dynamics.CHGNetCalculator

asimtools.calculators.load_dp(calc_params)[source]

Load Deep Potential Calculator

https://docs.deepmodeling.com/projects/deepmd/en/master/

Parameters:

calc_params (Dict) – args to pass to loader

Returns:

DP calculator

Return type:

deepmd.calculator.DP

asimtools.calculators.load_espresso_profile(calc_params)[source]

Load Qunatum Espresso Calculator for ASE>3.22.1. If using older versions such as the ones available on PyPI or conda-forge, just load it as an ASE calculator. Until the new ASE version becomes an official release, we will have to have both for compatibility. The interface however remains that of ASE <=3.22.1 within ASIMTools for consistency using the command keyword

https://wiki.fysik.dtu.dk/ase/releasenotes.html

Parameters:

calc_params (Dict) – args to pass to loader

Returns:

Espresso calculator

Return type:

ase.calculators.espresso.Espresso

asimtools.calculators.load_fairchem_v1(calc_params)[source]

Load any fairchemV1 calculator

Parameters:

calc_params (Dict) – parameters to be passed to fairchem.core.OCPCalculator. Must include a key “model” that points to the model files used to instantiate the potential

Returns:

fairchem calculator

Return type:

fairchem.core.OCPCalculator

asimtools.calculators.load_fairchem_v2(calc_params)[source]

Load any fairchemV1 calculator

Parameters:

calc_params (Dict) – parameters to be passed to fairchem.core.FAIRChemCalculator. Must include a key “model” that points to the model files used to instantiate the potential

Returns:

fairchem calculator

Return type:

fairchem.core.FAIRChemCalculator

Examples

>>> from asimtools.calculators import load_calc
>>> calc_params = {
...     'name': 'fairchem',
...     'args': {
...         'model_name': 'uma-s-1',
...         'device': 'cuda',
...         'task_name': 'oc20' # Also 'omol','omat','oc20','odac' or 'omc'
...     }
... }
>>> calc = load_calc(calc_params=calc_params)
asimtools.calculators.load_m3gnet(calc_params)[source]

Load any M3GNet or MatGL calculator

Parameters:

calc_params (Dict) – parameters to be passed to matgl.ext.ase.M3GNetCalculator. Must include a key “model” that points to the model used to instantiate the potential

Returns:

M3GNet calculator

Return type:

matgl.ext.ase.M3GNetCalculator

asimtools.calculators.load_mace(calc_params)[source]

Load MACE Calculator

https://github.com/ACEsuit/mace?tab=readme-ov-file

Parameters:

calc_params (Dict) – args to pass to loader

Returns:

MACE calculator

Return type:

mace.calculators.mace_mp

asimtools.calculators.load_mace_mp(calc_params)[source]

Load MACE Calculator

https://github.com/ACEsuit/mace?tab=readme-ov-file

Parameters:

calc_params (Dict) – args to pass to loader

Returns:

MACE calculator

Return type:

mace.calculators.mace_mp

asimtools.calculators.load_mace_off(calc_params)[source]

Load MACE Calculator

https://github.com/ACEsuit/mace?tab=readme-ov-file

Parameters:

calc_params (Dict) – args to pass to loader

Returns:

MACE-OFF calculator

Return type:

mace.calculators.mace_mp

asimtools.calculators.load_matgl(calc_params)[source]

Load any MatGL calculator

Parameters:

calc_params (Dict) – parameters to be passed to matgl.ext.ase.PESCalculator. Must include a key “model” that points to the model used to instantiate the potential

Returns:

MatGL calculator

Return type:

matgl.ext.ase.PESCalculator

asimtools.calculators.load_nequip(calc_params)[source]

Load NequIP or Allegro calculator

https://github.com/mir-group/nequip/tree/main

Parameters:

calc_params (Dict) – args to pass to loader

Returns:

NequIP ase calculator

Return type:

nequip.ase.nequip_calculator.NequIPCalculator

asimtools.job module

Set of tools for handling jobs in slurm, interactively or in the teminal for a specific calculator.

Author: mkphuthi@github.com

class asimtools.job.ChainedJob(sim_input: dict, env_input: dict | None = None, calc_input: dict | None = None)[source]

Bases: Job

Jobs made of smaller sequential unitjobs. Note that this only works well with unit jobs. If one of the UnitJobs calls asimmodules, internally, slurm will fail to build the correct dependencies but the files will be generated appropriately, you can submit them manually

get_last_output() dict[source]

Returns the output of the last job in the chain

submit(dependency: list | None = None, debug: bool = False) list[source]

Submit a job using slurm, interactively or in the terminal

class asimtools.job.DistributedJob(sim_input: dict, env_input: dict | None = None, calc_input: dict | None = None)[source]

Bases: Job

Array job object with ability to submit simultaneous jobs

gen_input_files(**kwargs) None[source]

Write input files to working directory

submit(**kwargs) list[int] | None[source]

Submit a job using slurm, interactively or in the current console

submit_jobs(**kwargs) list[int] | None[source]

Submits the jobs. If submitting lots of batch jobs, we recommend using DistributedJob.submit_slurm_array

submit_sh_array(**_kwargs) list[int] | None[source]

Submits jobs using a sh script. Proceeds even if some jobs fail

submit_slurm_array(array_max: int | None = None, dependency: list[str] | None = None, group_size: int = 1, debug: bool = False, **_kwargs) list[int] | None[source]

Submits a job array if all the jobs have the same env and use slurm

class asimtools.job.Job(sim_input: dict, env_input: dict | None = None, calc_input: dict | None = None, asimrun_mode: bool = False)[source]

Bases: object

Abstract class for the job object

add_output_files(file_dict: dict) None[source]

Adds a file to the output file list

complete() None[source]

Updates the output to signal that the job completed

discard() None[source]

Updates status to discarded

fail() None[source]

Updates status to failed

get_calc_input() dict[source]

Get calculator input

get_env_input() dict[source]

Get environment input

get_logger(logfilename: str = 'stdout.txt', level: str = 'info') Logger[source]

Get the logger

get_output() dict[source]

Get values in output.yaml

get_output_yaml() Path[source]

Get current output file

get_sim_input() dict[source]

Get simulation input

get_status(descend: bool = False, display: bool = False) tuple[bool, str][source]

Check job status

get_workdir() Path[source]

Get working directory

go_to_workdir() None[source]

Go to workdir

leave_workdir() None[source]

goes to directory from which job was launched

mkworkdir() None[source]

Creates the work directory if it doesn’t exist

set_status(status: str) None[source]

Updates job status to specificied value

set_workdir(workdir: str | Path) None[source]

Set working directory both in sim_input and instance

start() None[source]

Updates the output to signal that the job was started

update_calc_input(new_params: dict) None[source]

Update calculator parameters

update_env_input(new_params: dict) None[source]

Update calculator parameters

update_output(output_update: dict) None[source]

Update output.yaml if it exists or write a new one

update_sim_input(new_params: dict) None[source]

Update simulation parameters

class asimtools.job.UnitJob(sim_input: dict, env_input: dict | None = None, calc_input: dict | None = None)[source]

Bases: Job

Unit job object with ability to submit a slurm/interactive job. Unit jobs run in a specific environment and if required, run a specific calculator. More complex workflows are built up of unit jobs

gen_input_files(write_calc_input: bool = True, write_env_input: bool = True, write_image: bool = True) None[source]

Write input files to working directory

gen_run_command() str[source]

Generates the command to run the job, independent of slurm

submit(dependency: list | str | None = None, write_image: bool = True) list[str] | None[source]

Submit a job using slurm, interactively or in the terminal

asimtools.job.check_job_tree_complete(job_tree: dict, skip_failed: bool = False) tuple[bool, str][source]

Recursively check if all jobs in a job tree are complete

asimtools.job.create_unitjob(sim_input: dict, env_input: dict, workdir: str | Path, calc_input: dict | None = None) UnitJob[source]

Helper for making a generic UnitJob object, mostly for testing

asimtools.job.get_subjobs(workdir: Path) list[Path][source]

Get all the directories with jobs in them

Parameters:

workdir (Path) – directory to search for subjobs

Returns:

sorted list of subdirectory paths containing sim_input.yaml

Return type:

list[Path]

asimtools.job.load_job_from_directory(workdir: str | Path, asimrun_mode: bool = False) Job[source]

Loads a job from a given directory

asimtools.job.load_job_tree(workdir: str | Path = './') dict[source]

Loads all the jobs in a directory in a tree format using recursion

Parameters:

workdir (str, optional) – root directory from which to recursively find jobs, defaults to ‘./’

Returns:

dictionary mimicking the job tree

Return type:

dict

asimtools.utils module

Utilities and helper functions for reading and writing data using set standards

asimtools.utils.change_dict_value(dct: Dict, new_value, key_sequence: Sequence, return_copy: bool | None = True, placeholder: str | None = None) Dict[source]

Changes a value in the specified dictionary given by following the key sequence

Parameters:
  • dct (Dict) – dictionary to be changed

  • new_value (_type_) – The new value that will replace the old one

  • key_sequence (Sequence) – List of keys in the order in which they access the dictionary key

  • return_copy (bool, optional) – Whether to return a copy only or to modify the dictionary in-place as well, defaults to True

Returns:

The changed dictionary

Return type:

Dict

asimtools.utils.change_dict_values(dct: Dict, new_values: Sequence, key_sequences: Sequence, return_copy: bool = True) Dict[source]

Changes values in the specified dictionary given by following the key sequences. Key-value pairs are set in the given order

Parameters:
  • dct (Dict) – dictionary to be changed

  • new_values (Sequence) – The new values that will replace the old one

  • key_sequence (Sequence) – List of list of keys in the order in which they access the dictionary key

  • return_copy (bool, optional) – Whether to return a copy only or to modify the dictionary in-place as well, defaults to True

Returns:

The changed dictionary

Return type:

Dict

asimtools.utils.check_if_slurm_job_is_running(slurm_job_id: str | int)[source]

Checks if the slurm job with specifying job_id is running

Parameters:

slurm_job_id (Union[str,int]) – Job ID as str or int

Returns:

Whether job is running or not

Return type:

bool

asimtools.utils.expand_wildcards(dct: Dict, root_path: PathLike | None = None) Dict[source]

Expands paths in a dictionary

Parameters:
  • dct (Dict[str, Any]) – Dictionary to expand paths in

  • root_path (os.PathLike) – Root path to expand paths from

Returns:

Dictionary with expanded paths

Return type:

Dict[str, Any]

asimtools.utils.find_nth(haystack: str, needle: str, n: int) int[source]

Return index of nth occurence of substring in string

asimtools.utils.get_atoms(image_file: str | None = None, interface: str = 'ase', builder: str | None = 'bulk', atoms: Atoms | None = None, repeat: Tuple[int, int, int] | None = None, repeat_to_n_args: Dict | None = None, rattle_stdev: float | None = None, mp_id: str | None = None, user_api_key: str | None = None, return_type: str = 'ase', constraints: Sequence[dict] | None = None, **kwargs) Atoms | Structure[source]

Return an ASE Atoms or pymatgen Structure object based on specified config. This is the recommended way to load atoms objects for asimmodules.

Parameters:
  • image_file (str, optional) – Path to an ASE-readable image file, defaults to None

  • interface (str, optional) – Whether to use “ase” or “pymatgen” to create the atoms object

  • builder (str, optional) – Builder to use from ase.build, defaults to ‘bulk’. Any extra keyword arguments specified are passed to the build

  • atoms (Atoms, optional) – ase.Atoms object, defaults to None

  • repeat (Tuple[int, int, int], optional) – Number of times to repeat input to create supercell, defaults to None

  • rattle_stdev (float, optional) – stdev to be provided to ase.Atoms.rattle(), or as distance to pymatgen.core.structure.Structure.perturb() defaults to None

  • mp_id (str, optional) – Material Project ID to fetch structure from, defaults to None

  • user_api_key (str, optional) – Material Project API key, must be provided to get structures from Materials Project, defaults to None

  • constraints (Sequence[dict], optional) – List of constraints to apply to the atoms object, currently only supports FixAtoms constraints, defaults to None

  • return_type – When set to ase returns a ase.Atoms object, when set to pymatgen returns a pymatgen.core.structure.Structure object, defaults to ‘ase’

Returns:

One ase.Atoms or pymatgen.core.structure.Structure instance

Return type:

Union[Atoms, Structure]

There are three options one could use to specify and image or an atoms objects:

  1. image_file + **kwargs

  2. builder + **kwargs.

  3. atoms

Examples

Some examples using builders from ASE. All **kwargs are passed to ase.build():

>>> get_atoms(builder='molecule', name='H2O')
Atoms(symbols='OH2', pbc=False)
>>> get_atoms(builder='bulk', name='Cu')  
Atoms(symbols='Cu', pbc=True, ...)
>>> get_atoms(builder='bulk', name='Ar', crystalstructure='fcc', a=3.4, cubic=True)
Atoms(symbols='Ar4', pbc=True, cell=[3.4, 3.4, 3.4])
>>> get_atoms(builder='fcc100', symbol='Fe', vacuum=8, size=[4,4,5])  
Atoms(symbols='Cu80', pbc=[True, True, False], ...)

You can also specify constraints to fix atoms in place, for example >>> get_atoms( # doctest: +ELLIPSIS … builder=’fcc100’, symbol=’Fe’, vacuum=8, size=[4,4,5], … constraints=[{‘constraint’: ‘FixAtoms’, ‘indices’: [0,1]}]) Atoms(symbols=’Cu80’, pbc=[True, True, False], …)

Some examples for reading an image from a file using ase.io.read() are given below. All **kwargs are passed to ase.io.read()

>>> h2o = get_atoms(builder='molecule', name='H2O')
>>> h2o.write('h2o.cif')
>>> get_atoms(image_file='h2o.cif')
Atoms(symbols='OH2', pbc=False)
>>> get_atoms(image_file='h2o.cif', format='cif')
Atoms(symbols='OH2', pbc=False)
>>> from ase.io import write
>>> molecules = [get_atoms(builder='molecule', name='H2O'),
...              get_atoms(builder='molecule', name='H2')]
>>> write('molecules.xyz', molecules, format='extxyz')
>>> get_atoms(image_file='molecules.xyz', index=0) # Pick out one structure using indexing
Atoms(symbols='OH2', pbc=False)

You can also make supercells and rattle the atoms or repeat to a target

>>> li_bulk = get_atoms(name='Li')
>>> li_bulk.write('POSCAR', format='vasp')
>>> get_atoms(image_file='POSCAR', repeat=[3,3,3])  
Atoms(symbols='Li27', pbc=True, ...)
>>> get_atoms(  
...     builder='bulk', name='Li', repeat=[2,2,2], rattle_stdev=0.01)
Atoms(symbols='Li8', pbc=True, ...)
>>> get_atoms(  
...     builder='bulk', name='Li', repeat_to_n_args={'n': 16, 'max_dim': 10.0})
Atoms(symbols='Li16', pbc=True, ...)

Mostly for internal use and use in asimmodules, one can specify atoms directly

>>> li_bulk = get_atoms(name='Li')
>>> get_atoms(atoms=li_bulk)  
Atoms(symbols='Li', pbc=True, ...)

In an asimmodule, the image argument is always given as a dictionary, you therefore have to expand it before passing it to get_atoms

>>> image = {'name': 'Pt'}
>>> get_atoms(**image)
Atoms(symbols='Pt', pbc=True, cell=[[0.0, 1.96, 1.96], [1.96, 0.0, 1.96], [1.96, 1.96, 0.0]])

To get a pymatgen structure object, set the return_type to “pymatgen”

>>> image = {'name': 'Pt', 'return_type': 'pymatgen'}
>>> get_atoms(**image)
Structure Summary
Lattice
...

To download a structure from the Materials Project, you need to provide an API key, the MP ID of the structure and set the interface to ‘pymatgen’ You can also specify whether you want the primitive(default) or conventional unit cell as a keyword argument

>>> image = {
...     'mp_id': 'mp-14', 'interface': 'pymatgen',
...     'user_api_key': "USER_API_KEY", 'conventional_unit_cell': True}
>>> get_atoms(**image)
Structure Summary
Lattice
abc : 2.7718585822512662 2.7718585822512662 2.7718585822512662
...

You can also specify a builder from pymatgen.core.structure or pymatgen.core.molecule, for example pymatgen.core.surface.Structure. The lattice paramters are passed as an ArrayLike with shape [3,3] to pymatgen.core.lattice.Lattice or dictionary to the pymatgen.core.lattice.Lattice.from_parameters() function. >>> image = { # doctest: +ELLIPSIS … ‘builder’: ‘pymatgen.core.surface.Structure’, … ‘lattice’: {‘a’: 2.7, ‘b’: 2.7, ‘c’: 2.7, ‘alpha’: 90, ‘beta’: 90, ‘gamma’: 90}} >>> get_atoms(**image) Structure Summary Lattice abc : 2.7 2.7 2.7 …

asimtools.utils.get_axis_lims(x: Sequence, y: Sequence, padding: float = 0.1)[source]

Get an estimate of good limits for a plot axis

asimtools.utils.get_calc_input()[source]

Gets the global calc_input from the environment variable, then tries then .asimtools dotfile

Returns:

calc_input dictionary or empty dictionary if not found

Return type:

Dict

asimtools.utils.get_env_input() Dict[source]

Gets the global env_input from the environment variable, then tries then .asimtools dotfile. If an env_input.yaml is present in the work direcory, it takes precedence.

Returns:

env_input dictionary or empty dictionary if not found

Return type:

Dict

asimtools.utils.get_images(image_file: str | None = None, pattern: str | None = None, patterns: List[str] | None = None, images: Iterable[Atoms] | None = None, index: str | int = ':', skip_failed: bool = False, **kwargs) List[Atoms][source]
Return a list of atoms objects based on the input arguments. Options

to specify are: #. image_file #. pattern #. patterns #. images

Parameters:
  • image_file (str, optional) – Path to ASE-readable file with one or more images, defaults to None

  • pattern (str, optional) – String pattern of paths from which to search for images, defaults to None. This only gets the last image from each file as in ase.io.read() if an index is not specified.

  • patterns – Sequence of string patterns/paths from which to search for images, defaults to None. This only gets one image from each file as in ase.io.read() without specifying an index

  • images (Iterable[Atoms], optional) – A list of atoms objects, defaults to None

  • index (Union[str, int], optional) – Index to specify when using ase.io.read(), defaults to ‘:’

  • skip_failed (bool, optional) – Whether to raise an IO error if it fails to read any of the specified images or ignore errors, defaults to False

Raises:

IOError – Failed to read one of the specified images

Returns:

List of ase.Atoms for all images found

Return type:

List[Atoms]

There are three options one could use to specify and image or an atoms objects:

  1. image_file + **kwargs for specifying one image file

  2. pattern + **kwargs for specifying multiple image files with a wildcard character

  3. patterns + **kwargs for specifying a list of patterns to match, captures the above two cases

  4. images

Examples

Some examples for reading images selectively from an image_file. All **kwargs are passed to ase.io.read():

>>> from asimtools.utils import get_atoms
>>> molecules = []
>>> molecules.append(get_atoms(builder='molecule', name='H2O'))
>>> molecules.append(get_atoms(builder='molecule', name='H2'))
>>> molecules.append(get_atoms(builder='molecule', name='N2'))
>>> write('molecules.xyz', molecules, format='extxyz')
>>> get_images(image_file='molecules.xyz')  
[Atoms(symbols='OH2', pbc=False), ...]
>>> get_images(image_file='molecules.xyz', index=':2')
[Atoms(symbols='OH2', pbc=False), Atoms(symbols='H2', pbc=False)]

You can also use a wildcard (*) by specifying the pattern argument. Notice that the files don’t have to be the same format if ASE can guess all the file formats, otherwise you can specify the format argument which should apply to all the images.

>>> cu = get_atoms(name='Cu')
>>> cu.write('bulk_cu.cfg')
>>> fe = get_atoms(name='Fe')
>>> fe.write('bulk_fe.cif')
>>> pt = get_atoms(name='Pt')
>>> pt.write('bulk_pt.cfg')
>>> get_images(pattern='bulk*')  
[Atoms(symbols='Cu', ...), ...]
>>> get_images(pattern='bulk*.cfg', format='cfg')  
[Atoms(symbols='Cu', ...), ...]

You can also specify multiple patterns

>>> get_images(patterns=['bulk*.cfg', 'bulk*.cif'])  
[Atoms(symbols='Cu', ...), ...]

Or you can directly pass a list of Atoms, mostly for internal use

>>> get_images(images=molecules)  
[Atoms(symbols='OH2', pbc=False), ...]

In an asimmodule, the images argument is always given as a dictionary, you therefore have to expand it before passing it to get_images

>>> images = {'pattern': 'bulk*'}
>>> get_images(**images)  
[Atoms(symbols='Cu', ...), ...]
asimtools.utils.get_logger(logfile='job.log', fstr='%(asctime)s |%(module)s|%(funcName)s| %(levelname)s: %(message)s', level='debug')[source]

Get the logger

asimtools.utils.get_nth_label(string: PathLike, n: int = 1)[source]

Return nth label in a string potentially containing multiple labels, indexing starts from 0

asimtools.utils.get_str_btn(string: str | PathLike, s1: str, s2: str, occurence: int | None = 0, start_index: int | None = 0)[source]

Returns the substring between strings s1 and s2 from string

Parameters:
  • string (str) – string/path from which to extract substring

  • s1 (str) – substring before the desired substring, None starts from the beginning of s

  • s2 (str) – substring after the desired substring, None ends at the end of the string

  • occurence (int, optional) – which occurence to return e.g. occurence=1 returns the second time a substring is encountered, defaults to 0

  • start_index – first index from which to search for substring, defaults to 0

Returns:

substring

Return type:

_type_

asimtools.utils.improve_plot(ax=None, fontsize=14)[source]

Apply standard formatting improvements to a matplotlib axis

asimtools.utils.join_names(substrs: Sequence[str]) str[source]

Join multiple strings using a hyphen neatly

Parameters:

substrs (Sequence[str]) – Sequence of strings to merge

Returns:

Joined strings

Return type:

str

asimtools.utils.new_db(dbname: str)[source]

Creates a new ASE database overwriting an old one

Parameters:

dbname (str) – Name for the new database

Returns:

Connection to database

Return type:

ase.db connection

asimtools.utils.parse_slice(value: str, bash: bool = False) slice[source]

Parses a slice() from string, like start:stop:step.

Parameters:

value (str) – Slice string

Returns:

slice object

Return type:

slice

asimtools.utils.read_yaml(yaml_path: str) Dict[source]

Read a yaml file

Parameters:

yaml_path (str) – Path to yaml file

Returns:

Dictionary

Return type:

Dict

asimtools.utils.repeat_to_n(atoms: Atoms, n: int, max_dim: float = 50.0) Atoms[source]

Scale a structure to have approximately n atoms in the unit cell. The function repeats the shortest axis of the unit cell until the number of atoms >= n or the longest axis of the unit cell is > max_dim.

Parameters:
  • atoms (Atoms) – Input atoms object

  • n (int) – Target number of atoms

  • max_dim (float, optional) – Maximum length of the longest axis of the unit cell, defaults to 50.0

Raises:

ValueError – If it fails to scale the structure to n atoms

Returns:

Scaled atoms object

Return type:

Atoms

asimtools.utils.strip_symbols(substr: str) str[source]

Helper function to format filenames using standard

Parameters:

substr (str) – substring

Returns:

stripped string

Return type:

str

asimtools.utils.write_atoms(image_file: str, atoms: Atoms | list[Atoms], fmt: str = 'extxyz', write_info: bool = True, columns: Sequence | None = None, **kwargs)[source]

Writes image/images to a file. The default format is extxyz and the default columns are symbols and positions. All the images should have the same metadata

Parameters:
  • image_file (str) – Path to file to write to

  • atoms (Atoms or list[Atoms]) – Atoms object or list of atoms objects to write

  • fmt (str) – Format to write, defaults to ‘extxyz’

  • write_info (bool) – Whether to write info, defaults to True

  • columns (Sequence, optional) – Columns to write, mostly for debugging, if used, specify all columns including positions, symbols etc. defaults to None,

  • kwargs (Any) – Extra keyword arguments passed to ase.io.write()

Raises:

ValueError – If the format is not supported

Returns:

None

Return type:

None

asimtools.utils.write_csv_from_dict(fname: str, data: Dict, columns: Sequence | None = None, header: str = '', **kwargs) DataFrame[source]

Write a tabulated csv to a file from a dictionary. The keys will become the columns and the values will be the columns, All columns used must have the same length. kwargs are passed to pandas.Dataframe.to_csv()

Parameters:
  • fname (str) – File name to be return to

  • data (Dict) – Dictionary to write

  • columns (Iterable, optional) – Keys to use as columns of csv, defaults to None

Returns:

Pandas dataframe of results

Return type:

pd.DataFrame

asimtools.utils.write_yaml(yaml_path: str, yaml_dict: Dict) None[source]

Write a dictionary to a yaml file

Parameters:
  • yaml_path (str) – Path to write yaml to

  • yaml_dict (Dict) – Dictionary to write

Module contents

asimtools: lightweight atomic simulation workflow manager.

class asimtools.ChainedJob(sim_input: dict, env_input: dict | None = None, calc_input: dict | None = None)[source]

Bases: Job

Jobs made of smaller sequential unitjobs. Note that this only works well with unit jobs. If one of the UnitJobs calls asimmodules, internally, slurm will fail to build the correct dependencies but the files will be generated appropriately, you can submit them manually

get_last_output() dict[source]

Returns the output of the last job in the chain

submit(dependency: list | None = None, debug: bool = False) list[source]

Submit a job using slurm, interactively or in the terminal

class asimtools.DistributedJob(sim_input: dict, env_input: dict | None = None, calc_input: dict | None = None)[source]

Bases: Job

Array job object with ability to submit simultaneous jobs

gen_input_files(**kwargs) None[source]

Write input files to working directory

submit(**kwargs) list[int] | None[source]

Submit a job using slurm, interactively or in the current console

submit_jobs(**kwargs) list[int] | None[source]

Submits the jobs. If submitting lots of batch jobs, we recommend using DistributedJob.submit_slurm_array

submit_sh_array(**_kwargs) list[int] | None[source]

Submits jobs using a sh script. Proceeds even if some jobs fail

submit_slurm_array(array_max: int | None = None, dependency: list[str] | None = None, group_size: int = 1, debug: bool = False, **_kwargs) list[int] | None[source]

Submits a job array if all the jobs have the same env and use slurm

class asimtools.Job(sim_input: dict, env_input: dict | None = None, calc_input: dict | None = None, asimrun_mode: bool = False)[source]

Bases: object

Abstract class for the job object

add_output_files(file_dict: dict) None[source]

Adds a file to the output file list

complete() None[source]

Updates the output to signal that the job completed

discard() None[source]

Updates status to discarded

fail() None[source]

Updates status to failed

get_calc_input() dict[source]

Get calculator input

get_env_input() dict[source]

Get environment input

get_logger(logfilename: str = 'stdout.txt', level: str = 'info') Logger[source]

Get the logger

get_output() dict[source]

Get values in output.yaml

get_output_yaml() Path[source]

Get current output file

get_sim_input() dict[source]

Get simulation input

get_status(descend: bool = False, display: bool = False) tuple[bool, str][source]

Check job status

get_workdir() Path[source]

Get working directory

go_to_workdir() None[source]

Go to workdir

leave_workdir() None[source]

goes to directory from which job was launched

mkworkdir() None[source]

Creates the work directory if it doesn’t exist

set_status(status: str) None[source]

Updates job status to specificied value

set_workdir(workdir: str | Path) None[source]

Set working directory both in sim_input and instance

start() None[source]

Updates the output to signal that the job was started

update_calc_input(new_params: dict) None[source]

Update calculator parameters

update_env_input(new_params: dict) None[source]

Update calculator parameters

update_output(output_update: dict) None[source]

Update output.yaml if it exists or write a new one

update_sim_input(new_params: dict) None[source]

Update simulation parameters

class asimtools.UnitJob(sim_input: dict, env_input: dict | None = None, calc_input: dict | None = None)[source]

Bases: Job

Unit job object with ability to submit a slurm/interactive job. Unit jobs run in a specific environment and if required, run a specific calculator. More complex workflows are built up of unit jobs

gen_input_files(write_calc_input: bool = True, write_env_input: bool = True, write_image: bool = True) None[source]

Write input files to working directory

gen_run_command() str[source]

Generates the command to run the job, independent of slurm

submit(dependency: list | str | None = None, write_image: bool = True) list[str] | None[source]

Submit a job using slurm, interactively or in the terminal

asimtools.change_dict_value(dct: Dict, new_value, key_sequence: Sequence, return_copy: bool | None = True, placeholder: str | None = None) Dict[source]

Changes a value in the specified dictionary given by following the key sequence

Parameters:
  • dct (Dict) – dictionary to be changed

  • new_value (_type_) – The new value that will replace the old one

  • key_sequence (Sequence) – List of keys in the order in which they access the dictionary key

  • return_copy (bool, optional) – Whether to return a copy only or to modify the dictionary in-place as well, defaults to True

Returns:

The changed dictionary

Return type:

Dict

asimtools.change_dict_values(dct: Dict, new_values: Sequence, key_sequences: Sequence, return_copy: bool = True) Dict[source]

Changes values in the specified dictionary given by following the key sequences. Key-value pairs are set in the given order

Parameters:
  • dct (Dict) – dictionary to be changed

  • new_values (Sequence) – The new values that will replace the old one

  • key_sequence (Sequence) – List of list of keys in the order in which they access the dictionary key

  • return_copy (bool, optional) – Whether to return a copy only or to modify the dictionary in-place as well, defaults to True

Returns:

The changed dictionary

Return type:

Dict

asimtools.expand_wildcards(dct: Dict, root_path: PathLike | None = None) Dict[source]

Expands paths in a dictionary

Parameters:
  • dct (Dict[str, Any]) – Dictionary to expand paths in

  • root_path (os.PathLike) – Root path to expand paths from

Returns:

Dictionary with expanded paths

Return type:

Dict[str, Any]

asimtools.get_atoms(image_file: str | None = None, interface: str = 'ase', builder: str | None = 'bulk', atoms: Atoms | None = None, repeat: Tuple[int, int, int] | None = None, repeat_to_n_args: Dict | None = None, rattle_stdev: float | None = None, mp_id: str | None = None, user_api_key: str | None = None, return_type: str = 'ase', constraints: Sequence[dict] | None = None, **kwargs) Atoms | Structure[source]

Return an ASE Atoms or pymatgen Structure object based on specified config. This is the recommended way to load atoms objects for asimmodules.

Parameters:
  • image_file (str, optional) – Path to an ASE-readable image file, defaults to None

  • interface (str, optional) – Whether to use “ase” or “pymatgen” to create the atoms object

  • builder (str, optional) – Builder to use from ase.build, defaults to ‘bulk’. Any extra keyword arguments specified are passed to the build

  • atoms (Atoms, optional) – ase.Atoms object, defaults to None

  • repeat (Tuple[int, int, int], optional) – Number of times to repeat input to create supercell, defaults to None

  • rattle_stdev (float, optional) – stdev to be provided to ase.Atoms.rattle(), or as distance to pymatgen.core.structure.Structure.perturb() defaults to None

  • mp_id (str, optional) – Material Project ID to fetch structure from, defaults to None

  • user_api_key (str, optional) – Material Project API key, must be provided to get structures from Materials Project, defaults to None

  • constraints (Sequence[dict], optional) – List of constraints to apply to the atoms object, currently only supports FixAtoms constraints, defaults to None

  • return_type – When set to ase returns a ase.Atoms object, when set to pymatgen returns a pymatgen.core.structure.Structure object, defaults to ‘ase’

Returns:

One ase.Atoms or pymatgen.core.structure.Structure instance

Return type:

Union[Atoms, Structure]

There are three options one could use to specify and image or an atoms objects:

  1. image_file + **kwargs

  2. builder + **kwargs.

  3. atoms

Examples

Some examples using builders from ASE. All **kwargs are passed to ase.build():

>>> get_atoms(builder='molecule', name='H2O')
Atoms(symbols='OH2', pbc=False)
>>> get_atoms(builder='bulk', name='Cu')  
Atoms(symbols='Cu', pbc=True, ...)
>>> get_atoms(builder='bulk', name='Ar', crystalstructure='fcc', a=3.4, cubic=True)
Atoms(symbols='Ar4', pbc=True, cell=[3.4, 3.4, 3.4])
>>> get_atoms(builder='fcc100', symbol='Fe', vacuum=8, size=[4,4,5])  
Atoms(symbols='Cu80', pbc=[True, True, False], ...)

You can also specify constraints to fix atoms in place, for example >>> get_atoms( # doctest: +ELLIPSIS … builder=’fcc100’, symbol=’Fe’, vacuum=8, size=[4,4,5], … constraints=[{‘constraint’: ‘FixAtoms’, ‘indices’: [0,1]}]) Atoms(symbols=’Cu80’, pbc=[True, True, False], …)

Some examples for reading an image from a file using ase.io.read() are given below. All **kwargs are passed to ase.io.read()

>>> h2o = get_atoms(builder='molecule', name='H2O')
>>> h2o.write('h2o.cif')
>>> get_atoms(image_file='h2o.cif')
Atoms(symbols='OH2', pbc=False)
>>> get_atoms(image_file='h2o.cif', format='cif')
Atoms(symbols='OH2', pbc=False)
>>> from ase.io import write
>>> molecules = [get_atoms(builder='molecule', name='H2O'),
...              get_atoms(builder='molecule', name='H2')]
>>> write('molecules.xyz', molecules, format='extxyz')
>>> get_atoms(image_file='molecules.xyz', index=0) # Pick out one structure using indexing
Atoms(symbols='OH2', pbc=False)

You can also make supercells and rattle the atoms or repeat to a target

>>> li_bulk = get_atoms(name='Li')
>>> li_bulk.write('POSCAR', format='vasp')
>>> get_atoms(image_file='POSCAR', repeat=[3,3,3])  
Atoms(symbols='Li27', pbc=True, ...)
>>> get_atoms(  
...     builder='bulk', name='Li', repeat=[2,2,2], rattle_stdev=0.01)
Atoms(symbols='Li8', pbc=True, ...)
>>> get_atoms(  
...     builder='bulk', name='Li', repeat_to_n_args={'n': 16, 'max_dim': 10.0})
Atoms(symbols='Li16', pbc=True, ...)

Mostly for internal use and use in asimmodules, one can specify atoms directly

>>> li_bulk = get_atoms(name='Li')
>>> get_atoms(atoms=li_bulk)  
Atoms(symbols='Li', pbc=True, ...)

In an asimmodule, the image argument is always given as a dictionary, you therefore have to expand it before passing it to get_atoms

>>> image = {'name': 'Pt'}
>>> get_atoms(**image)
Atoms(symbols='Pt', pbc=True, cell=[[0.0, 1.96, 1.96], [1.96, 0.0, 1.96], [1.96, 1.96, 0.0]])

To get a pymatgen structure object, set the return_type to “pymatgen”

>>> image = {'name': 'Pt', 'return_type': 'pymatgen'}
>>> get_atoms(**image)
Structure Summary
Lattice
...

To download a structure from the Materials Project, you need to provide an API key, the MP ID of the structure and set the interface to ‘pymatgen’ You can also specify whether you want the primitive(default) or conventional unit cell as a keyword argument

>>> image = {
...     'mp_id': 'mp-14', 'interface': 'pymatgen',
...     'user_api_key': "USER_API_KEY", 'conventional_unit_cell': True}
>>> get_atoms(**image)
Structure Summary
Lattice
abc : 2.7718585822512662 2.7718585822512662 2.7718585822512662
...

You can also specify a builder from pymatgen.core.structure or pymatgen.core.molecule, for example pymatgen.core.surface.Structure. The lattice paramters are passed as an ArrayLike with shape [3,3] to pymatgen.core.lattice.Lattice or dictionary to the pymatgen.core.lattice.Lattice.from_parameters() function. >>> image = { # doctest: +ELLIPSIS … ‘builder’: ‘pymatgen.core.surface.Structure’, … ‘lattice’: {‘a’: 2.7, ‘b’: 2.7, ‘c’: 2.7, ‘alpha’: 90, ‘beta’: 90, ‘gamma’: 90}} >>> get_atoms(**image) Structure Summary Lattice abc : 2.7 2.7 2.7 …

asimtools.get_images(image_file: str | None = None, pattern: str | None = None, patterns: List[str] | None = None, images: Iterable[Atoms] | None = None, index: str | int = ':', skip_failed: bool = False, **kwargs) List[Atoms][source]
Return a list of atoms objects based on the input arguments. Options

to specify are: #. image_file #. pattern #. patterns #. images

Parameters:
  • image_file (str, optional) – Path to ASE-readable file with one or more images, defaults to None

  • pattern (str, optional) – String pattern of paths from which to search for images, defaults to None. This only gets the last image from each file as in ase.io.read() if an index is not specified.

  • patterns – Sequence of string patterns/paths from which to search for images, defaults to None. This only gets one image from each file as in ase.io.read() without specifying an index

  • images (Iterable[Atoms], optional) – A list of atoms objects, defaults to None

  • index (Union[str, int], optional) – Index to specify when using ase.io.read(), defaults to ‘:’

  • skip_failed (bool, optional) – Whether to raise an IO error if it fails to read any of the specified images or ignore errors, defaults to False

Raises:

IOError – Failed to read one of the specified images

Returns:

List of ase.Atoms for all images found

Return type:

List[Atoms]

There are three options one could use to specify and image or an atoms objects:

  1. image_file + **kwargs for specifying one image file

  2. pattern + **kwargs for specifying multiple image files with a wildcard character

  3. patterns + **kwargs for specifying a list of patterns to match, captures the above two cases

  4. images

Examples

Some examples for reading images selectively from an image_file. All **kwargs are passed to ase.io.read():

>>> from asimtools.utils import get_atoms
>>> molecules = []
>>> molecules.append(get_atoms(builder='molecule', name='H2O'))
>>> molecules.append(get_atoms(builder='molecule', name='H2'))
>>> molecules.append(get_atoms(builder='molecule', name='N2'))
>>> write('molecules.xyz', molecules, format='extxyz')
>>> get_images(image_file='molecules.xyz')  
[Atoms(symbols='OH2', pbc=False), ...]
>>> get_images(image_file='molecules.xyz', index=':2')
[Atoms(symbols='OH2', pbc=False), Atoms(symbols='H2', pbc=False)]

You can also use a wildcard (*) by specifying the pattern argument. Notice that the files don’t have to be the same format if ASE can guess all the file formats, otherwise you can specify the format argument which should apply to all the images.

>>> cu = get_atoms(name='Cu')
>>> cu.write('bulk_cu.cfg')
>>> fe = get_atoms(name='Fe')
>>> fe.write('bulk_fe.cif')
>>> pt = get_atoms(name='Pt')
>>> pt.write('bulk_pt.cfg')
>>> get_images(pattern='bulk*')  
[Atoms(symbols='Cu', ...), ...]
>>> get_images(pattern='bulk*.cfg', format='cfg')  
[Atoms(symbols='Cu', ...), ...]

You can also specify multiple patterns

>>> get_images(patterns=['bulk*.cfg', 'bulk*.cif'])  
[Atoms(symbols='Cu', ...), ...]

Or you can directly pass a list of Atoms, mostly for internal use

>>> get_images(images=molecules)  
[Atoms(symbols='OH2', pbc=False), ...]

In an asimmodule, the images argument is always given as a dictionary, you therefore have to expand it before passing it to get_images

>>> images = {'pattern': 'bulk*'}
>>> get_images(**images)  
[Atoms(symbols='Cu', ...), ...]
asimtools.get_str_btn(string: str | PathLike, s1: str, s2: str, occurence: int | None = 0, start_index: int | None = 0)[source]

Returns the substring between strings s1 and s2 from string

Parameters:
  • string (str) – string/path from which to extract substring

  • s1 (str) – substring before the desired substring, None starts from the beginning of s

  • s2 (str) – substring after the desired substring, None ends at the end of the string

  • occurence (int, optional) – which occurence to return e.g. occurence=1 returns the second time a substring is encountered, defaults to 0

  • start_index – first index from which to search for substring, defaults to 0

Returns:

substring

Return type:

_type_

asimtools.join_names(substrs: Sequence[str]) str[source]

Join multiple strings using a hyphen neatly

Parameters:

substrs (Sequence[str]) – Sequence of strings to merge

Returns:

Joined strings

Return type:

str

asimtools.load_calc(calculator: Dict | None = None, calc_id: str | None = None, calc_input: Dict | None = None, calc_params: Dict | None = None)[source]

Loads a calculator using a calculator dict or legacy calc_id/calc_params arguments.

Parameters:
  • calculator (Optional[Dict], optional) – Dictionary with either a calc_id key (to look up from the global or supplied calc_input) or a calc_params key (to use directly). Takes precedence over the legacy arguments if provided, defaults to None

  • calc_id (str, optional) – Deprecated — use calculator={'calc_id': ...} instead. ID/key to use to load calculator from the supplied or global calc_input file, defaults to None

  • calc_input – calc_input dictionary, same form as calc_input yaml :type calc_input: Optional[Dict], optional

  • calc_params (Optional[Dict], optional) – Deprecated — use calculator={'calc_params': ...} instead. calc_params dictionary for a single calculator, defaults to None

Returns:

ASE calculator instance

Return type:

ase.calculators.calculators.Calculator

asimtools.read_yaml(yaml_path: str) Dict[source]

Read a yaml file

Parameters:

yaml_path (str) – Path to yaml file

Returns:

Dictionary

Return type:

Dict

asimtools.repeat_to_n(atoms: Atoms, n: int, max_dim: float = 50.0) Atoms[source]

Scale a structure to have approximately n atoms in the unit cell. The function repeats the shortest axis of the unit cell until the number of atoms >= n or the longest axis of the unit cell is > max_dim.

Parameters:
  • atoms (Atoms) – Input atoms object

  • n (int) – Target number of atoms

  • max_dim (float, optional) – Maximum length of the longest axis of the unit cell, defaults to 50.0

Raises:

ValueError – If it fails to scale the structure to n atoms

Returns:

Scaled atoms object

Return type:

Atoms

asimtools.write_atoms(image_file: str, atoms: Atoms | list[Atoms], fmt: str = 'extxyz', write_info: bool = True, columns: Sequence | None = None, **kwargs)[source]

Writes image/images to a file. The default format is extxyz and the default columns are symbols and positions. All the images should have the same metadata

Parameters:
  • image_file (str) – Path to file to write to

  • atoms (Atoms or list[Atoms]) – Atoms object or list of atoms objects to write

  • fmt (str) – Format to write, defaults to ‘extxyz’

  • write_info (bool) – Whether to write info, defaults to True

  • columns (Sequence, optional) – Columns to write, mostly for debugging, if used, specify all columns including positions, symbols etc. defaults to None,

  • kwargs (Any) – Extra keyword arguments passed to ase.io.write()

Raises:

ValueError – If the format is not supported

Returns:

None

Return type:

None

asimtools.write_yaml(yaml_path: str, yaml_dict: Dict) None[source]

Write a dictionary to a yaml file

Parameters:
  • yaml_path (str) – Path to write yaml to

  • yaml_dict (Dict) – Dictionary to write