diff --git a/.gitignore b/.gitignore index 558c4af..99a81e1 100644 --- a/.gitignore +++ b/.gitignore @@ -157,3 +157,6 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ + +# ASIMTools results +examples/*/*results*/ diff --git a/Building_docs.md b/Building_docs.md new file mode 100644 index 0000000..36878ea --- /dev/null +++ b/Building_docs.md @@ -0,0 +1,18 @@ +To build the documentation you will need the following packages: + +- sphinx +- sphinx-apidoc +- autodoc + +### Steps +1. First, generate the autdoc sources for the API reference from the root using + + sphinx-apidoc -f -o docs asimtools + +2. Then go into the docs directory and run + + make html + +This will generate the docs in the `_build` directory + + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cd24d2d..f39ccba 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,10 +5,10 @@ When contributing to this repository, please first discuss the change you wish t Please note we have a code of conduct, please follow it in all your interactions with the project. There are two main ways to contribute. One is to make changes to the existing -codes and scripts. In this case, please make sure your change passes tests or -submit a test accordinly. The second way to contribute is to add a new script -to the core set of scripts. Reviewers will work with you to make sure your new -script follows best practice including using utils, syntax highlighting, docs +codes and asimmodules. In this case, please make sure your change passes tests or +submit a test accordinly. The second way to contribute is to add a new asimmodule +to the core set of asimmodules. Reviewers will work with you to make sure your new +asimmodule follows best practice including using utils, syntax highlighting, docs and tests. ## Pull Request Process diff --git a/README.md b/README.md index f6f8006..261c627 100644 --- a/README.md +++ b/README.md @@ -1,166 +1,118 @@ + + + + + +[Documentation](https://battmodels.github.io/asimtools/) | [GitHub](https://github.com/BattModels/asimtools) + # Atomic SIMulation Tools This package is a lightweight workflow and simulation manager for reproducible atomistic simulations that can be transferred across environments, calculators -and structures. By using in-built or user-defined scripts and utilities, users -can run/build their own simulation recipes and automagically scale them on -slurm based clusters. The core idea is to separate the dependence of the -atomistic potential/calculator and the simulations steps thereby allowing the -same simulation to be run with multiple calculators and the same calculator to -be used for multiple simulations. Input and output files must follow a strict -format so that consistent analysis pipelines can be used across users - -** ASIMTools is going through some breaking changes for version 0.1.0, please use with caution ** - -## Documentation -Documentation is under construction [here](https://battmodels.github.io/asimtools/) +and structures. By using in-built or user-defined asimmodules and utilities, +users can run/build their own simulation recipes and automagically scale them +locally or on slurm based clusters. The core idea is to separate the dependence +of the atomistic potential/calculator and the simulations steps thereby +allowing the same simulation to be run with multiple calculators and the same +calculator to be used for multiple simulations without altering simulation +code. Input and output files must follow a strict format so that consistent +analysis pipelines can be used across users ## Developer philosophy -The goal of asimtools is to push all the complexity of workflow -management, best-practices, file management etc. into the backend such that the -everyday user only has to handle input files for existing workflows and script -files for workflows they want to implement. The following are the guiding -principles for how asimtools should work: - -- Scripts should resemble boilerplate ASE code as much as possible. -- Scripts should not explicitly depend on a calculator -- Scripts should not explicitly depend on the context in which they run -- It should be easy to debug individual scripts/parts in large workflows. In - addition, it should always be possible to debug/resubmit jobs without using - asimtools. -- input file structure and format should be standard across all scripts. In +The goal of asimtools is to push all the complexity of workflow management, +best-practices, file management etc. into the backend such that the everyday +user only has to handle input files for existing workflows and asimmodule files +for workflows they want to implement. The following are the guiding principles +for how asimtools should work: + +- Asimmodules should resemble boilerplate ASE code as much as possible. +- Asimmodules should not explicitly depend on a calculator +- Asimmodules should not explicitly depend on the context/environment in which + they run +- It should be easy to debug individual asimmodules/parts in large workflows. + In addition, it should always be possible to debug/resubmit jobs without + using asimtools. +- Input file structure and format should be standard across all asimmodules. In addition all input parameters should match how they would like without - asimtools i.e. do not provide an API! This means extensive use of importlib. -- output file structure should be predictable across all scripts + asimtools i.e. do not provide an API! - Job progress tracking must be incorporated - Best practices should be built-in e.g. if multiple jobs of the same slurm - context are submitted simulataneously, it must be a job array + context are submitted simulataneously, it must be a job array. ## Philosophy on User Experience -The philosophy is to build "simulations" using building blocks of scripts. -These scripts can be as complicated/efficient as you make them using any -external packages you want and can be optimized with time but can still be run -within the framework. This allows a test friendly way to transition from say a -tutorial on the ASE/pymatgen website to an asimtools script. So while -complicated wrappers are discouraged, they would still work as long as the -script works. The benefit out of the box is that you can make your script -independent of calculator or input structures and submit them easily. +The philosophy is to build "simulations" using building blocks of asimmodules. +Asimmodules are nothing but Python functions that return a dictionary, anything +can be done in the function code. These asimmodules can be as +complicated/efficient as you make them using any external packages you want and +can be optimized with time but can still be run within the framework. This +allows a test friendly way to transition from say a tutorial on the +ASE/pymatgen website to an asimtools asimmodule. So while complicated wrappers +are discouraged, they would still work as long as the asimmodule works. The +benefit out of the box is that you can make your asimmodule independent of +calculator or input structures and submit them easily. ## Getting Started -These instructions will give you a copy of the project up and running on -your local machine for development and testing purposes. +These instructions will give you a copy of the project up and running. ### Installing You can install asimtools in a new conda environment using: ``` -conda create -n asimtools python=3.9 +conda create -n asimtools python=3.7 conda activate asimtools conda install ase -c conda-forge git clone https://github.com/BattModels/asimtools.git cd asimtools -pip install -e . +pip install . ``` -This installs the base package in developer mode so that you do not have to -reinstall if you make changes to the core package. - Individual calculators may need external packages for loading those -calculators. Similarly some scripts e.g. `lammps.py` might also need external packages to be used. It is up to the user to make sure those are installed. +calculators. For example if you want to use Quantum Espresso or CASTEP, you +will have to install them. Similarly some asimmodules e.g. `lammps.py` might +also need external packages to be used. It is up to the user to make sure those +are installed. You will also need to setup some environment variables, these variables point -to a `env_input.yaml` and `calc_input.yaml` with your favorite configs since -these are commonly shared among simulations. You can also directly specify them -when running `asim-execute` (See `asim-execute -h`). -Examples of these files can be found in the examples. +to global `env_input.yaml` and `calc_input.yaml` files with your favorite +configurations since these are commonly shared among simulations. You can also +directly specify them when running `asim-execute` (See `asim-execute -h`). You +can also provide a directory for ASIMTools to search for your custom +asimmodules. Examples of these files can be found in the examples. Add the following to your `.bashrc` ``` export ASIMTOOLS_ENV_INPUT=/path/to/my/global/env_input.yaml export ASIMTOOLS_CALC_INPUT=/path/to/my/global/calc_input.yaml -export ASIMTOOLS_SCRIPT_DIR=/path/to/my/script/dir +export ASIMTOOLS_ASIMMODULE_DIR=/path/to/my/asimmodule/dir ``` - - - - - ## Running the tests -To run all tests from the tests directory, call: +To run tests for the workflow tools, from the tests directory, call: pytest To run the test suite on a component `component.py` , call: pytest test_component.py - - + source run_all.sh + +Or you can run a test for each individual example in its directory using: + + source run.sh +If no errors are reported, the tests have passed. These tests simply test the +functionality of the code, not the validity of the simulations! + +To run tests for the provided asimmodules using slurm, you can similarly run +the bash scripts ending with `_slurm.sh`. + ## Contributing Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code diff --git a/asimtools/scripts/__init__.py b/asimtools/asimmodules/__init__.py similarity index 100% rename from asimtools/scripts/__init__.py rename to asimtools/asimmodules/__init__.py diff --git a/asimtools/scripts/eos/__init__.py b/asimtools/asimmodules/benchmarking/__init__.py similarity index 100% rename from asimtools/scripts/eos/__init__.py rename to asimtools/asimmodules/benchmarking/__init__.py diff --git a/asimtools/scripts/parity.py b/asimtools/asimmodules/benchmarking/parity.py similarity index 99% rename from asimtools/scripts/parity.py rename to asimtools/asimmodules/benchmarking/parity.py index 6b2c26b..9ded857 100644 --- a/asimtools/scripts/parity.py +++ b/asimtools/asimmodules/benchmarking/parity.py @@ -2,7 +2,7 @@ ''' Generates a parity plot and collects evaluation statistics comparing energy and/or forces and/or stress to existing values in the provided dataset. This -script can work in parallel based on the number of cores specified. +asimmodule can work in parallel based on the number of cores specified. Author: mkphuthi@github.com diff --git a/asimtools/scripts/do_nothing.py b/asimtools/asimmodules/do_nothing.py similarity index 84% rename from asimtools/scripts/do_nothing.py rename to asimtools/asimmodules/do_nothing.py index 41a0567..843b8be 100755 --- a/asimtools/scripts/do_nothing.py +++ b/asimtools/asimmodules/do_nothing.py @@ -1,6 +1,6 @@ #!/usr/bin/env python ''' -Script for unit tests and debugging, does nothing for specified duration +asimmodule for unit tests and debugging, does nothing for specified duration ''' from time import sleep from typing import Dict, Optional diff --git a/examples/ase_eos/env_input.yaml b/asimtools/asimmodules/elastic_constants/__init__.py similarity index 100% rename from examples/ase_eos/env_input.yaml rename to asimtools/asimmodules/elastic_constants/__init__.py diff --git a/asimtools/scripts/elastic_constants/cubic_energy_expansion.py b/asimtools/asimmodules/elastic_constants/cubic_energy_expansion.py similarity index 85% rename from asimtools/scripts/elastic_constants/cubic_energy_expansion.py rename to asimtools/asimmodules/elastic_constants/cubic_energy_expansion.py index 350cf5a..a7ea189 100755 --- a/asimtools/scripts/elastic_constants/cubic_energy_expansion.py +++ b/asimtools/asimmodules/elastic_constants/cubic_energy_expansion.py @@ -1,9 +1,9 @@ #!/usr/bin/env python ''' -Describe the script briefly here. If this is a script that runs multiple steps, +Describe the asimmodule briefly here. If this is a asimmodule that runs multiple steps, describe it here using reStructuredText to generate autodocs -Cite the papers where the method/script was first introduced here as well +Cite the papers where the method/asimmodule was first introduced here as well Author: mkphuthi@github.com ''' @@ -17,10 +17,9 @@ from asimtools.utils import ( get_atoms, ) -from asimtools.scripts.ase_cubic_eos_optimization import ( +from asimtools.asimmodules.geometry_optimization.ase_cubic_eos_optimization import ( ase_cubic_eos_optimization as eos ) -from asimtools.scripts.cell_relax import cell_relax def get_strained_atoms(atoms, strain: str, delta: float): """Returns a unit cell with atoms strained according to some useful types @@ -84,7 +83,7 @@ def cubic_energy_expansion( calc_id: str, image: Dict, deltas: Sequence[float] = (-0.01,-0.0075,-0.005,0.00,0.005,0.0075,0.01), - ase_eos_args: Optional[Dict] = None, + ase_cubic_eos_args: Optional[Dict] = None, ) -> Dict: ''' Calculates B, C11, C12 and C44 elastic constants of a structure with cubic @@ -98,8 +97,8 @@ def cubic_energy_expansion( # Start by getting the Bulk modulus and optimized cell from the EOS logging.info('Calculating EOS') eos_kwargs = {'image': image, 'calc_id': calc_id} - if ase_eos_args is not None: - eos_kwargs.update(ase_eos_args) + if ase_cubic_eos_args is not None: + eos_kwargs.update(ase_cubic_eos_args) eos_results = eos(**eos_kwargs) B = eos_results['B'] atoms = get_atoms(image_file='eos_image_output.xyz') @@ -135,7 +134,7 @@ def cubic_energy_expansion( c11min12_atoms.write(f's{delta:.4f}_c11min12.xyz') def f(x, a, b, c): - ''' Fitting function for Free energy expansion to second order''' + ''' Fitting function for free energy expansion to second order''' return a*x**2 + b*x + c logging.info('Fitting for C44') @@ -152,13 +151,15 @@ def f(x, a, b, c): anis = 2 * C44 / (C11 - C12) results = { - 'B': float(B / GPa), - 'C11minC12': float(C11minC12 / GPa), - 'C11': float(C11 / GPa), - 'C12': float(C12 / GPa), - 'C44': float(C44 / GPa), - 'Anisotropy': float(anis), - 'vol': float(atoms.get_volume()), + 'constants': { + 'B': float(B / GPa), + 'C11minC12': float(C11minC12 / GPa), + 'C11': float(C11 / GPa), + 'C12': float(C12 / GPa), + 'C44': float(C44 / GPa), + 'Anisotropy': float(anis), + 'vol': float(atoms.get_volume()), + }, } return results # Always return a dictionary! Use {} if necessary diff --git a/examples/eos/results/step-0/id-0_x0.95/stdout.txt b/asimtools/asimmodules/eos/__init__.py similarity index 100% rename from examples/eos/results/step-0/id-0_x0.95/stdout.txt rename to asimtools/asimmodules/eos/__init__.py diff --git a/asimtools/scripts/eos/postprocess.py b/asimtools/asimmodules/eos/postprocess.py similarity index 100% rename from asimtools/scripts/eos/postprocess.py rename to asimtools/asimmodules/eos/postprocess.py diff --git a/examples/eos/results/step-0/id-1_x0.97/stdout.txt b/asimtools/asimmodules/geometry_optimization/__init__.py similarity index 100% rename from examples/eos/results/step-0/id-1_x0.97/stdout.txt rename to asimtools/asimmodules/geometry_optimization/__init__.py diff --git a/asimtools/scripts/ase_cubic_eos_optimization.py b/asimtools/asimmodules/geometry_optimization/ase_cubic_eos_optimization.py similarity index 64% rename from asimtools/scripts/ase_cubic_eos_optimization.py rename to asimtools/asimmodules/geometry_optimization/ase_cubic_eos_optimization.py index ec3eaa4..5bf5d07 100644 --- a/asimtools/scripts/ase_cubic_eos_optimization.py +++ b/asimtools/asimmodules/geometry_optimization/ase_cubic_eos_optimization.py @@ -23,16 +23,21 @@ def ase_cubic_eos_optimization( eps: Optional[float] = 0.04, plot: Optional[bool] = True, ) -> Dict: - """Calculate the equation of state, fit it and extract the equilibrium - volume, energy and bulk modulus + """Generate the energy-volume equation of state (energy calculations not parallelized) - :param image: image to use + :param image: Image input :type image: Dict - :param calc_id: calculator id + :param calc_id: calc_id :type calc_id: str - :param db_file: ASE database in which to store results - :type db_file: bulk.db - :return: results including equilibrium volume, energy and bulk modulus + :param npoints: Number of energy points to calculate, must be >5, defaults to 5 + :type npoints: Optional[int], optional + :param eos_string: eos_string as specified for :func:`ase.eos.calculate_eos`, defaults to 'sj' + :type eos_string: Optional[str], optional + :param eps: eps as sepecified for, defaults to 0.04 + :type eps: Optional[float], optional + :param plot: Whether to plot eos or not, defaults to True + :type plot: Optional[bool], optional + :return: Equilibrium energy, volume, bulk modulus and factor by which to scale lattice parameter to get equilibrium structure :rtype: Dict """ calc = load_calc(calc_id) @@ -44,15 +49,17 @@ def ase_cubic_eos_optimization( eos.eos_string = eos_string v0, e0, B = eos.fit() # find minimum logging.info('Successfully fit EOS') - # Do one more calculation at the minimum and write to database: + # Do one more calculation at the minimum and write to disk new_cell = atoms.cell.copy() - new_cell *= (v0 / atoms.get_volume())**(1 / 3) + x = (v0 / atoms.get_volume())**(1 / 3) # scale cell by this much + new_cell *= x atoms.set_cell(new_cell, scale_atoms=True) atoms.get_potential_energy() atoms.info['B'] = B atoms.info['v0'] = v0 + atoms.info['scale'] = x atoms.write('eos_image_output.xyz') - + if plot: eos.plot() plt.savefig('eos.png') @@ -67,7 +74,8 @@ def ase_cubic_eos_optimization( for struct in traj: eos_dict['energies'].append(struct.get_potential_energy()) eos_dict['volumes'].append(struct.get_volume()) - eos_dict['volume_scale_factors'].append(struct.get_volume() / v_init) + v_factor = struct.get_volume() / v_init + eos_dict['volume_scale_factors'].append(v_factor) eos_df = pd.DataFrame(eos_dict) eos_df.to_csv('eos.csv') diff --git a/asimtools/scripts/atom_relax.py b/asimtools/asimmodules/geometry_optimization/atom_relax.py similarity index 99% rename from asimtools/scripts/atom_relax.py rename to asimtools/asimmodules/geometry_optimization/atom_relax.py index 17573b0..7620964 100755 --- a/asimtools/scripts/atom_relax.py +++ b/asimtools/asimmodules/geometry_optimization/atom_relax.py @@ -49,7 +49,7 @@ def atom_relax( dyn = getattr(ase.optimize, optimizer)(atoms) traj = Trajectory( traj_file, - 'w', + 'a', atoms, properties=properties ) diff --git a/asimtools/scripts/cell_relax.py b/asimtools/asimmodules/geometry_optimization/cell_relax.py similarity index 93% rename from asimtools/scripts/cell_relax.py rename to asimtools/asimmodules/geometry_optimization/cell_relax.py index 93978c7..892f40a 100755 --- a/asimtools/scripts/cell_relax.py +++ b/asimtools/asimmodules/geometry_optimization/cell_relax.py @@ -50,12 +50,12 @@ def cell_relax( else: prefix = '' - traj_file = join_names([prefix, 'cell_relax.traj']) + traj_file = join_names([prefix, 'cell_relax.traj'])[:-2] # Don't include __ sf = StrainFilter(atoms, mask=mask) dyn = getattr(ase.optimize, optimizer)(sf) traj = Trajectory( traj_file, - 'w', + 'a', atoms, properties=['energy', 'forces', 'stress'], ) @@ -66,7 +66,7 @@ def cell_relax( print('Failed to optimize atoms') raise - image_file = join_names([prefix, 'image_output.xyz']) + image_file = join_names([prefix, 'image_output.xyz'])[:-2] atoms.write(image_file, format='extxyz') energy = float(atoms.get_potential_energy()) diff --git a/asimtools/scripts/optimize_geometry.py b/asimtools/asimmodules/geometry_optimization/optimize.py similarity index 94% rename from asimtools/scripts/optimize_geometry.py rename to asimtools/asimmodules/geometry_optimization/optimize.py index eb0f4fd..e1a4492 100755 --- a/asimtools/scripts/optimize_geometry.py +++ b/asimtools/asimmodules/geometry_optimization/optimize.py @@ -12,7 +12,7 @@ from asimtools.calculators import load_calc from asimtools.utils import get_atoms -def optimize_geometry( +def optimize( calc_id: str, image: Dict, optimizer: str = 'BFGS', @@ -47,10 +47,10 @@ def optimize_geometry( ecf = ExpCellFilter(atoms, **expcellfilter_args) dyn = getattr(ase.optimize, optimizer)(ecf) - traj_file = 'cell_relax.traj' + traj_file = 'optimize.traj' traj = Trajectory( traj_file, - 'w', + 'a', # If there are multiple relaxations, we keep all trajectories atoms, properties=['energy', 'forces', 'stress'], ) diff --git a/asimtools/scripts/symmetric_cell_relax.py b/asimtools/asimmodules/geometry_optimization/symmetric_cell_relax.py similarity index 99% rename from asimtools/scripts/symmetric_cell_relax.py rename to asimtools/asimmodules/geometry_optimization/symmetric_cell_relax.py index c7e0caf..d647927 100755 --- a/asimtools/scripts/symmetric_cell_relax.py +++ b/asimtools/asimmodules/geometry_optimization/symmetric_cell_relax.py @@ -56,7 +56,7 @@ def symmetric_cell_relax( traj_file = 'cell_relax.traj' traj = Trajectory( traj_file, - 'w', + 'a', atoms, properties=['energy', 'forces', 'stress'], ) diff --git a/examples/eos/results/step-0/id-2_x1.00/stdout.txt b/asimtools/asimmodules/lammps/__init__.py similarity index 100% rename from examples/eos/results/step-0/id-2_x1.00/stdout.txt rename to asimtools/asimmodules/lammps/__init__.py diff --git a/asimtools/scripts/lammps.py b/asimtools/asimmodules/lammps/lammps.py similarity index 95% rename from asimtools/scripts/lammps.py rename to asimtools/asimmodules/lammps/lammps.py index 7246a07..b9446b0 100755 --- a/asimtools/scripts/lammps.py +++ b/asimtools/asimmodules/lammps/lammps.py @@ -28,7 +28,7 @@ def lammps( if variables is None: variables = {} - # Make sure the provided script follows standard for reading + # Make sure the provided asimmodule follows standard for reading # in arbitrary image provided by asimtools if image is not None: atoms = get_atoms(**image) @@ -63,7 +63,7 @@ def lammps( assert 'read_data ' in lmp_txt, \ 'Make sure "read_data image_input.lmpdat" command is used \ (with correct atom style) appropriately in lammps input \ - script if you specify image keyword' + file if you specify image keyword' lmp_inp_file = join_names([prefix, 'input.lammps']) with open(lmp_inp_file, 'w', encoding='utf-8') as f: diff --git a/examples/eos/results/step-0/id-3_x1.02/stdout.txt b/asimtools/asimmodules/phonons/__init__.py similarity index 100% rename from examples/eos/results/step-0/id-3_x1.02/stdout.txt rename to asimtools/asimmodules/phonons/__init__.py diff --git a/asimtools/scripts/phonons/ase_phonons.py b/asimtools/asimmodules/phonons/ase_phonons.py similarity index 100% rename from asimtools/scripts/phonons/ase_phonons.py rename to asimtools/asimmodules/phonons/ase_phonons.py diff --git a/asimtools/scripts/singlepoint.py b/asimtools/asimmodules/singlepoint.py similarity index 100% rename from asimtools/scripts/singlepoint.py rename to asimtools/asimmodules/singlepoint.py diff --git a/examples/eos/results/step-0/id-4_x1.05/stdout.txt b/asimtools/asimmodules/surface_energies/__init__.py similarity index 100% rename from examples/eos/results/step-0/id-4_x1.05/stdout.txt rename to asimtools/asimmodules/surface_energies/__init__.py diff --git a/asimtools/scripts/surface_energies/surface_energies.py b/asimtools/asimmodules/surface_energies/surface_energies.py similarity index 82% rename from asimtools/scripts/surface_energies/surface_energies.py rename to asimtools/asimmodules/surface_energies/surface_energies.py index 87c8e01..ea73cd1 100755 --- a/asimtools/scripts/surface_energies/surface_energies.py +++ b/asimtools/asimmodules/surface_energies/surface_energies.py @@ -1,9 +1,9 @@ #!/usr/bin/env python ''' -Describe the script briefly here. If this is a script that runs multiple steps, +Describe the asimmodule briefly here. If this is a asimmodule that runs multiple steps, describe it here using reStructuredText to generate autodocs -Cite the papers where the method/script was first introduced here as well +Cite the papers where the method/asimmodule was first introduced here as well Author: mkphuthi@github.com ''' @@ -15,7 +15,7 @@ from pymatgen.core.surface import generate_all_slabs from pymatgen.io.ase import AseAtomsAdaptor as AAA from asimtools.calculators import load_calc -from asimtools.scripts.atom_relax import atom_relax +from asimtools.asimmodules.geometry_optimization.atom_relax import atom_relax from asimtools.utils import ( get_atoms, ) @@ -35,7 +35,7 @@ def get_surface_energy(slab, calc, bulk_e_per_atom): surf_en = (slab_en - nslab * bulk_e_per_atom) / (2 * area) else: surf_en = None - return surf_en, slab, converged + return converged, surf_en, slab_en, area def surface_energies( image: Dict, @@ -93,7 +93,7 @@ def surface_energies( image={'atoms': atoms}, optimizer=atom_relax_args.get('optimizer', 'BFGS'), properties=('energy','forces'), - fmax=atom_relax_args.get('fmax', 0.02), + fmax=atom_relax_args.get('fmax', 0.01), prefix=f'{miller}_relaxed' ) atoms = get_atoms( @@ -109,15 +109,20 @@ def surface_energies( f'Multiple terminations for {miller}' slab_dict[miller] = {} - surf_en, slab_atoms, converged = get_surface_energy( + converged, surf_en, slab_en, area = get_surface_energy( atoms, load_calc(calc_id), bulk_e_per_atom ) if converged: slab_dict[miller]['surf_energy'] = float(surf_en) slab_dict[miller]['natoms'] = len(atoms) - slab_atoms.write(f'{miller}.xyz') + slab_dict[miller]['slab_energy'] = float(slab_en) + slab_dict['bulk_energy_per_atom'] = float(bulk_e_per_atom) + slab_dict[miller]['area'] = float(area) + atoms.write(f'{miller}.xyz') - results = slab_dict + results = { + 'surface_energies': slab_dict, + } return results # Always return a dictionary! Use {} if necessary diff --git a/asimtools/asimmodules/template.py b/asimtools/asimmodules/template.py new file mode 100755 index 0000000..543741a --- /dev/null +++ b/asimtools/asimmodules/template.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +''' +Describe the asimmodule briefly here. If this is a asimmodule that runs multiple steps, +describe it here using reStructuredText to generate autodocs + +Cite the papers where the method/asimmodule was first introduced here as well + +Author: mkphuthi@github.com +''' + +from typing import Dict +# from asimtools.calculators import load_calc +# from asimtools.utils import ( +# get_atoms, +# get_images, +# ) + +def template( + # calc_id: str, + # image: Dict, # Optional: if using a structure + # images: Dict, # Optional: if using multiple structures +) -> Dict: + ''' + asimmodule does xyz specifically + ''' + + # Some useful lines + # calc = load_calc(calc_id) + # atoms = get_atoms(**image) + # images = get_images(**images) + + ############################# + # Do some cool science here # + ############################# + + results = { + 'my_result': 'placeholder_for_my_result', + 'files': {'file_description': 'placeholder_for_output_file'} + } + return results # Always return a dictionary! Use {} if necessary diff --git a/examples/eos/results/step-1/stdout.txt b/asimtools/asimmodules/transformations/__init__.py similarity index 100% rename from examples/eos/results/step-1/stdout.txt rename to asimtools/asimmodules/transformations/__init__.py diff --git a/asimtools/scripts/scale_unit_cells.py b/asimtools/asimmodules/transformations/scale_unit_cells.py similarity index 100% rename from asimtools/scripts/scale_unit_cells.py rename to asimtools/asimmodules/transformations/scale_unit_cells.py diff --git a/asimtools/asimmodules/vacancy_formation_energy/__init__.py b/asimtools/asimmodules/vacancy_formation_energy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asimtools/asimmodules/vacancy_formation_energy/vacancy_formation_energy.py b/asimtools/asimmodules/vacancy_formation_energy/vacancy_formation_energy.py new file mode 100755 index 0000000..58e6bce --- /dev/null +++ b/asimtools/asimmodules/vacancy_formation_energy/vacancy_formation_energy.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +''' +Describe the asimmodule briefly here. If this is a asimmodule that runs multiple steps, +describe it here using reStructuredText to generate autodocs + +Cite the papers where the method/asimmodule was first introduced here as well + +Author: mkphuthi@github.com +''' + +from typing import Dict, Sequence, Optional +import logging +from asimtools.calculators import load_calc +from asimtools.asimmodules.geometry_optimization.atom_relax import atom_relax +from asimtools.utils import ( + get_atoms, +) + +def vacancy_formation_energy( + calc_id: str, + image: Dict, + vacancy_index: int = 0, + atom_relax_args: Optional[Dict] = None, + repeat: Optional[Sequence] = None +) -> Dict: + + calc = load_calc(calc_id) + bulk = get_atoms(**image).repeat(repeat) + bulk.set_calculator(calc) + + vacant = bulk.copy() + del vacant[vacancy_index] + + try: + bulk_e = calc.get_potential_energy(bulk) + logging.info('Finished bulk calculation') + except ValueError: + logging.error("Failed to calculate energy of bulk structure, " + "see calculator output") + raise + + bulk.write('bulk_image_output.xyz') + + try: + relax_results = atom_relax( + calc_id=calc_id, + image={'atoms': vacant}, + optimizer=atom_relax_args.get('optimizer', 'BFGS'), + properties=('energy','forces'), + fmax=atom_relax_args.get('fmax', 0.01), + prefix=f'vacant_relaxed', + ) + logging.info('Relaxed vacant structure') + except ValueError: + logging.error("Failed to calculate energy of bulk structure, " + "see calculator output") + raise + + vacant_e = relax_results['energy'] + vac_form_e = vacant_e - (len(vacant) / len(bulk) * bulk_e) + + res_dict = {} + res_dict['bulk_e'] = float(bulk_e) + res_dict['vacant_e'] = float(vacant_e) + res_dict['bulk_n'] = len(bulk) + res_dict['vfe'] = float(vac_form_e) + + results = {'vfe': res_dict} + return results diff --git a/asimtools/asimmodules/workflows/__init__.py b/asimtools/asimmodules/workflows/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/asimtools/scripts/calc_array.py b/asimtools/asimmodules/workflows/calc_array.py similarity index 51% rename from asimtools/scripts/calc_array.py rename to asimtools/asimmodules/workflows/calc_array.py index 7aa400c..d299b1a 100755 --- a/asimtools/scripts/calc_array.py +++ b/asimtools/asimmodules/workflows/calc_array.py @@ -1,18 +1,23 @@ #!/usr/bin/env python ''' -Apply the same script using multiple calculators +Apply the same asimmodule using multiple calculators Author: mkphuthi@github.com ''' from typing import Dict, Sequence, Optional, Union +from glob import glob from copy import deepcopy from asimtools.job import DistributedJob -from asimtools.utils import get_calc_input, change_dict_value +from asimtools.utils import ( + get_calc_input, + change_dict_value, + get_str_btn, +) def calc_array( - subscript_input: Dict, + subasimmodule_input: Dict, calc_ids: Sequence[str] = None, template_calc_id: Optional[str] = None, key_sequence: Optional[Sequence[str]] = None, @@ -20,17 +25,19 @@ def calc_array( env_ids: Optional[Union[Sequence[str],str]] = None, calc_input: Optional[Dict] = None, env_input: Optional[Dict] = None, - ids: Sequence = None, + file_pattern: Optional[str] = None, + labels: Optional[Union[Sequence,str]] = 'values', + str_btn_args: Optional[Dict] = None, ) -> Dict: - """Apply the same script using different calculators and if necessary + """Apply the same asimmodule using different calculators and if necessary different environments :param calc_ids: Iterable with calc_ids, defaults to None :type calc_ids: Sequence, optional :param calc_input: Dictionary of calculator inputs :type calc_input: Dictionary, optional - :param ids: Iterable with custom ids for each calc, defaults to None - :type ids: Sequence, optional + :param labels: Iterable with custom labels for each calc, defaults to None + :type labels: Sequence, optional :return: Dictionary of results :rtype: Dict """ @@ -38,16 +45,26 @@ def calc_array( and template_calc_id is not None\ and array_values is not None assert calc_ids is not None or using_array_values, \ - 'Specify either a sequence of calc_ids or all of key_sequence, \ - template_calc_id and array_values to iterate over' + 'Specify either an iterable of calc_ids or all of "key_sequence", \ + "template_calc_id" and "array_values" to iterate over' if using_array_values: if calc_input is None: calc_input = get_calc_input() calc_params = calc_input[template_calc_id] new_calc_input = {} - if ids is None: - ids = [f'{key_sequence[-1]}-{val}' for val in array_values] + if file_pattern is not None: + array_values = glob(file_pattern) + + assert len(array_values) > 0, 'No array values or files found' + if labels == 'str_btn': + assert str_btn_args is not None, 'Provide str_btn_args for labels' + labels = [get_str_btn(s, *str_btn_args) for s in array_values] + elif labels == 'values': + labels = [f'{key_sequence[-1]}-{val}' for val in array_values] + elif labels is None: + labels = [str(i) for i in range(len(array_values))] + for i, val in enumerate(array_values): new_calc_params = change_dict_value( d=calc_params, @@ -55,15 +72,17 @@ def calc_array( key_sequence=key_sequence, return_copy=True, ) - new_calc_input[ids[i]] = new_calc_params + new_calc_input[labels[i]] = new_calc_params calc_input = new_calc_input - calc_ids = ids + calc_ids = labels elif calc_ids is not None: - if ids is None: - ids = calc_ids + assert labels != 'get_str_btn', \ + 'get_str_btn only works when using the key_sequence argument.' + if labels is None or labels == 'values': + labels = calc_ids - assert len(ids) == len(calc_ids), \ - 'Num. of calc_ids or array_values must match num. of ids' + assert len(labels) == len(calc_ids), \ + 'Num. of calc_ids or array_values must match num. of labels' if env_ids is not None: assert len(env_ids) == len(calc_ids) or isinstance(env_ids, str), \ @@ -73,15 +92,15 @@ def calc_array( # Make individual sim_inputs for each calc for i, calc_id in enumerate(calc_ids): - new_subscript_input = deepcopy(subscript_input) - new_subscript_input['args']['calc_id'] = calc_id - array_sim_input[f'{ids[i]}'] = new_subscript_input + new_subasimmodule_input = deepcopy(subasimmodule_input) + new_subasimmodule_input['args']['calc_id'] = calc_id + array_sim_input[f'{labels[i]}'] = new_subasimmodule_input if env_ids is not None: if isinstance(env_ids, str): - new_subscript_input['env_id'] = env_ids + new_subasimmodule_input['env_id'] = env_ids else: - new_subscript_input['env_id'] = env_ids[i] + new_subasimmodule_input['env_id'] = env_ids[i] # Create a distributed job object djob = DistributedJob(array_sim_input, env_input, calc_input) diff --git a/asimtools/scripts/chained.py b/asimtools/asimmodules/workflows/chained.py similarity index 83% rename from asimtools/scripts/chained.py rename to asimtools/asimmodules/workflows/chained.py index 4779610..908989d 100755 --- a/asimtools/scripts/chained.py +++ b/asimtools/asimmodules/workflows/chained.py @@ -1,6 +1,6 @@ #!/usr/bin/env python ''' -Submits a chain of scripts in the order specified by steps +Submits a chain of asimmodules in the order specified by steps in input Author: mkphuthi@github.com @@ -18,8 +18,8 @@ def chained( ) -> Dict: """Run a workflow using the provided sim_input.yaml one after the other, The jobs will run one after the other, if slurm is used, job dependencies - will be used. Note that if one of the subscripts internally launches another - script e.g. image_array, calc_array etc, slurm job dependencies will not + will be used. Note that if one of the subasimmodules internally launches another + asimmodule e.g. image_array, calc_array etc, slurm job dependencies will not work as expected. In those cases, we recommend running the steps one by one or interactively diff --git a/asimtools/scripts/distributed.py b/asimtools/asimmodules/workflows/distributed.py similarity index 88% rename from asimtools/scripts/distributed.py rename to asimtools/asimmodules/workflows/distributed.py index e921e06..6aed36f 100755 --- a/asimtools/scripts/distributed.py +++ b/asimtools/asimmodules/workflows/distributed.py @@ -10,7 +10,7 @@ from asimtools.job import DistributedJob def distributed( - subscripts: Dict, + subasimmodules: Dict, env_input: Optional[Dict] = None, calc_input: Optional[Dict] = None, array_max: Optional[int] = None, @@ -23,10 +23,9 @@ def distributed( #. use_slurm: False, interactive: False, different env_ids -> Launches \ # multiple jobs. - - :param subscripts: Dictionary of scripts, each key is an ID and each \ + :param subasimmodules: Dictionary of asimmodules, each key is an ID and each \ value is a sim_input file - :type subscripts: Dict + :type subasimmodules: Dict :param env_input: env_input that overrides global file, defaults to None :type env_input: Optional[Dict], optional :param calc_input: calc_input that overrides global file, defaults to None @@ -37,7 +36,7 @@ def distributed( :rtype: Dict """ djob = DistributedJob( - sim_input=subscripts, + sim_input=subasimmodules, env_input=env_input, calc_input=calc_input ) diff --git a/asimtools/scripts/image_array.py b/asimtools/asimmodules/workflows/image_array.py similarity index 68% rename from asimtools/scripts/image_array.py rename to asimtools/asimmodules/workflows/image_array.py index 1ff2265..e034440 100755 --- a/asimtools/scripts/image_array.py +++ b/asimtools/asimmodules/workflows/image_array.py @@ -1,6 +1,6 @@ #!/usr/bin/env python ''' -Apply the same script on multiple images +Apply the same asimmodule on multiple images Author: mkphuthi@github.com @@ -13,26 +13,26 @@ def image_array( images: Dict, - subscript_input: Dict, + subasimmodule_input: Dict, calc_input: Optional[Dict] = None, env_input: Optional[Dict] = None, - ids: Sequence[str] = None, + labels: Sequence[str] = None, env_ids: Sequence[str] = None, array_max: Optional[int] = None, ) -> Dict: - """Submit the same script on multiple images and if specified, use + """Submit the same asimmodule on multiple images and if specified, use different env_ids :param images: Image config, see :func:`asimtools.utils.get_atoms` :type images: Dict - :param subscript_input: sim_input of script to be run - :type subscript_input: Dict + :param subasimmodule_input: sim_input of asimmodule to be run + :type subasimmodule_input: Dict :param calc_input: calc_input to override global file, defaults to None :type calc_input: Optional[Dict], optional :param env_input: env_input to override global file, defaults to None :type env_input: Optional[Dict], optional - :param ids: Custom ids for each image, defaults to None - :type ids: Sequence[str], optional + :param labels: Custom labels for each image, defaults to None + :type labels: Sequence[str], optional :param env_ids: Sequence of envs for each image, must be the same length \ as the total number of images, defaults to None :type env_ids: Sequence[str], optional @@ -46,11 +46,11 @@ def image_array( array_sim_input = {} # Allow user to customize subdirectory names if needed - if ids is None: - ids = [str(i) for i in range(len(images))] + if labels is None: + labels = [str(i) for i in range(len(images))] else: - assert len(ids) == len(images), \ - 'Num. of images must match num. of ids' + assert len(labels) == len(images), \ + 'Num. of images must match num. of labels' if env_ids is not None: assert len(env_ids) == len(images), \ @@ -58,14 +58,14 @@ def image_array( # Make individual sim_inputs for each image for i, image in enumerate(images): - new_subscript_input = deepcopy(subscript_input) - new_subscript_input['args']['image'] = { + new_subasimmodule_input = deepcopy(subasimmodule_input) + new_subasimmodule_input['args']['image'] = { 'atoms': image } - array_sim_input[f'{ids[i]}'] = new_subscript_input + array_sim_input[f'{labels[i]}'] = new_subasimmodule_input if env_ids is not None: - new_subscript_input['env_id'] = env_ids[i] + new_subasimmodule_input['env_id'] = env_ids[i] # Create a distributed job object djob = DistributedJob(array_sim_input, env_input, calc_input) diff --git a/asimtools/scripts/sim_array.py b/asimtools/asimmodules/workflows/sim_array.py similarity index 75% rename from asimtools/scripts/sim_array.py rename to asimtools/asimmodules/workflows/sim_array.py index 594b2cc..51d843c 100755 --- a/asimtools/scripts/sim_array.py +++ b/asimtools/asimmodules/workflows/sim_array.py @@ -1,6 +1,6 @@ #!/usr/bin/env python ''' -Runs the same script, iterating over multiple values of a specified argument +Runs the same asimmodule, iterating over multiple values of a specified argument based on a sim_input template provided by the user Author: mkphuthi@github.com @@ -21,10 +21,10 @@ def sim_array( env_ids: Optional[Union[Sequence[str],str]] = None, calc_input: Optional[Dict] = None, env_input: Optional[Dict] = None, - ids: Optional[Union[Sequence,str]] = 'values', + labels: Optional[Union[Sequence,str]] = 'values', str_btn_args: Optional[Dict] = None, ) -> Dict: - """Runs the same script, iterating over multiple values of a specified + """Runs the same asimmodule, iterating over multiple values of a specified argument based on a sim_input template provided by the user :param template_sim_input: sim_input containing all the default parameters @@ -39,8 +39,8 @@ def sim_array( :type calc_input: Optional[Dict], optional :param env_input: env_input to use, defaults to None :type env_input: Optional[Dict], optional - :param ids: Custom ids to use for each simulation, defaults to None - :type ids: Sequence, optional + :param labels: Custom labels to use for each simulation, defaults to None + :type labels: Sequence, optional :return: Results :rtype: Dict """ @@ -50,26 +50,26 @@ def sim_array( if file_pattern is not None: array_values = glob(file_pattern) - print(array_values) + assert len(array_values) > 0, 'No array values or files found' - if ids == 'str_btn': - assert str_btn_args is not None, 'Provide str_btn_args for ids' - ids = [get_str_btn(s, *str_btn_args) for s in array_values] - elif ids == 'values': - ids = [f'{key_sequence[-1]}-{val}' for val in array_values] - elif ids is None: - ids = [str(i) for i in range(len(array_values))] + if labels == 'str_btn': + assert str_btn_args is not None, 'Provide str_btn_args for labels' + labels = [get_str_btn(s, *str_btn_args) for s in array_values] + elif labels == 'values': + labels = [f'{key_sequence[-1]}-{val}' for val in array_values] + elif labels is None: + labels = [str(i) for i in range(len(array_values))] - assert len(ids) == len(array_values), \ - 'Num. of array_values must match num. of ids' + assert len(labels) == len(array_values), \ + 'Num. of array_values must match num. of labels' if env_ids is None: pass elif isinstance(env_ids, str): - env_ids = [env_ids for i in range(len(ids))] + env_ids = [env_ids for i in range(len(labels))] else: - assert len(env_ids) == len(ids) or isinstance(env_ids, str), \ + assert len(env_ids) == len(labels) or isinstance(env_ids, str), \ 'Provide one env_id or as many as there are array_values' # Create sim_inputs for each array value and to create input for @@ -84,11 +84,10 @@ def sim_array( ) if env_ids is not None: new_sim_input['env_id'] = env_ids[i] - sim_inputs[ids[i]] = new_sim_input + sim_inputs[labels[i]] = new_sim_input # Create a distributed job object djob = DistributedJob(sim_inputs, env_input, calc_input) - print('dist workdir', djob.workdir) job_ids = djob.submit() results = {'job_ids': job_ids} diff --git a/asimtools/calculators.py b/asimtools/calculators.py index d1af56f..363d8ef 100644 --- a/asimtools/calculators.py +++ b/asimtools/calculators.py @@ -116,6 +116,35 @@ def load_ase_calc(calc_params): raise return calc + +# def load_m3gnet(calc_params): +# """Load and M3GNet calculator + +# :param calc_params: parameters to be passed to matgl.ext.ase.M3GNetCalculator. Must include a key "model" that points to the model used to instantiate the potential +# :type calc_params: Dict +# :return: M3GNet calculator +# :rtype: :class:`matgl.ext.ase.M3GNetCalculator` +# """ +# from matgl.ext.ase import M3GNetCalculator +# import matgl + +# model = calc_params.pop("model") +# try: +# pot = matgl.load_model(model) +# calc = M3GNetCalculator( +# pot, +# **calc_params, +# ) +# except Exception: +# logging.error("Failed to load M3GNet with parameters:\n %s", calc_params) +# raise + +# return calc + + +# def load_chgnet(calc_params): +# pass + # def load_espresso(calc_params): # ''' # Load the Espresso calculator as in newer versions of ASE. This is @@ -137,5 +166,7 @@ def load_ase_calc(calc_params): 'NequIP': load_nequip, 'Allegro': load_nequip, 'DeepPotential': load_dp, + # 'ChgNet': load_chgnet, + # 'M3GNet': load_m3gnet, # 'Espresso': load_espresso, } diff --git a/asimtools/job.py b/asimtools/job.py index dfb120c..e069062 100644 --- a/asimtools/job.py +++ b/asimtools/job.py @@ -14,6 +14,7 @@ from glob import glob from typing import List, TypeVar, Dict, Tuple, Union from copy import deepcopy +from colorama import Fore import ase.io from ase.parallel import paropen import numpy as np @@ -31,9 +32,11 @@ Atoms = TypeVar('Atoms') +START_MESSAGE = '+' * 15 + ' ASIMTOOLS START' + '+' * 15 + '\n' +STOP_MESSAGE = '+' * 15 + ' ASIMTOOLS STOP' + '+' * 15 + '\n' + class Job(): ''' Abstract class for the job object ''' - # pylint: disable=too-many-instance-attributes def __init__( self, @@ -48,12 +51,12 @@ def __init__( self.env_input = deepcopy(env_input) self.calc_input = deepcopy(calc_input) self.sim_input = deepcopy(sim_input) - self.workdir = Path( - sim_input.get('workdir', '.') - ).resolve() + self.workdir = Path(sim_input.get('workdir', '.')).resolve() - # Any subsequent input files should use the full path - self.sim_input['workdir'] = str(self.workdir) + # sim_input will be in workdir from now on so set workdir as a relative + # path, but in this class/subclasses, always use self.workdir + # which is the full path to avoid confusion + self.sim_input['workdir'] = './' self.launchdir = Path(os.getcwd()) if self.sim_input.get('src_dir', False): self.sim_input['src_dir'] = self.launchdir @@ -119,8 +122,7 @@ def get_env_input(self) -> Dict: def get_output_yaml(self) -> Dict: ''' Get current output file ''' - prefix = '' - out_fname = join_names([prefix, 'output.yaml']) + out_fname = 'output.yaml' output_yaml = self.workdir / out_fname return output_yaml @@ -149,7 +151,7 @@ def update_env_input(self, new_params) -> None: self.env_input.update(new_params) def set_workdir(self, workdir) -> None: - ''' Set working directory ''' + ''' Set working directory both in sim_input and instance ''' workdir = Path(workdir) self.sim_input['workdir'] = str(workdir.resolve()) self.workdir = workdir @@ -169,7 +171,7 @@ def get_status(self, display=False) -> Tuple[bool,str]: status = output.get('status', 'clean') complete = status == 'complete' if display: - print(f'Current status in "{self.workdir}" is "{status}"') + print(f'Status: {status}') return complete, status def update_output(self, output_update: Dict) -> None: @@ -208,7 +210,7 @@ def __init__( } } - # Check if the script being called uses a calc_id to + # Check if the asimmodule being called uses a calc_id to # get precommands, postcommands, run_prefixes and run_suffixes self.calc_id = self.sim_input.get('args', {}).get('calc_id', None) if self.calc_id is not None: @@ -220,8 +222,8 @@ def gen_run_command(self) -> str: ''' Generates the command to run the job, independent of slurm ''' - script = self.sim_input.get('script', False) - assert script, 'Specify a script in sim_input' + asimmodule = self.sim_input.get('asimmodule', False) + assert asimmodule, 'Specify a asimmodule in sim_input' mode_params = self.env.get('mode', { 'use_slurm': False, 'interactive': True @@ -237,10 +239,10 @@ def gen_run_command(self) -> str: else: debug_flag = '' - # GPAW requires python script to be called with gpaw python + # GPAW requires python asimmodule to be called with gpaw python # For now this is a work around but perhaps there exists a cleaner way if self.calc_params.get('name', '') == 'GPAW': - asloader = pkgutil.get_loader('asimtools.scripts.asim_run') + asloader = pkgutil.get_loader('asimtools.asimmodules.asim_run') asfile = asloader.get_filename() alias = f'gpaw python {asfile}' else: @@ -260,7 +262,22 @@ def _gen_slurm_script(self, write: bool = True) -> None: calc_params = self.calc_params txt = '#!/usr/bin/sh\n' - for flag in slurm_params.get('flags'): + flags = slurm_params.get('flags') + if isinstance(flags, dict): + flag_list = [] + for k, v in flags.items(): + assert k.startswith('-'), \ + 'Slurm flags must start with "-" or "--"' + if k.startswith('--'): + delim = '=' + else: + delim = ' ' + + flag_list.append(delim.join([k, v])) + + flags = flag_list + + for flag in flags: txt += f'#SBATCH {flag}\n' txt += '\n' for command in slurm_params.get('precommands', []): @@ -292,9 +309,16 @@ def _gen_slurm_interactive_txt(self) -> str: return txt - def gen_input_files(self) -> None: + def gen_input_files( + self, + write_calc_input: bool = True, + write_env_input: bool = False, + ) -> None: ''' Write input files to working directory ''' workdir = self.workdir + + # This is the sim_input that will be written to disk so it will have + # slightly different paths and the image(s) will be image_files sim_input = deepcopy(self.sim_input) # The sim_input file will already be in the work directory sim_input['workdir'] = '.' @@ -310,15 +334,16 @@ def gen_input_files(self) -> None: if not workdir.exists(): workdir.mkdir() elif output_file.exists() and sim_input_file.exists(): - complete = read_yaml(output_file).get('start_time', False) + complete = read_yaml(output_file).get('end_time', False) if complete and not overwrite: - txt = f'Skipped writing in {self.workdir}, files already ' - txt += 'written. Set overwrite=True to overwrite' + txt = f'Skipped writing in {self.workdir}, files/directory ' + txt += 'already exist. Set overwrite=True to overwrite files ' + txt += 'or delete results directory first (recommended)' self.get_logger().warning(txt) - print(txt) + print(Fore.RED + 'WARNING: ' + txt + Fore.WHITE) return None - # If the sim_input uses an image/images, we want to write it in + # If the sim_input uses an image/images, we want to write it in # the workdir and point to it using a filename. This is so that # there is a record of the input image for debugging and the input # method is consistent with complex workflows @@ -326,30 +351,36 @@ def gen_input_files(self) -> None: if image: atoms = get_atoms(**image) - input_image_file = self.workdir.resolve() / 'input_image.xyz' - atoms.write(input_image_file, format='extxyz') - self.sim_input['args']['image'] = { + input_image_file = 'image_input.xyz' # Relative to workdir + # input_image_file = self.workdir.resolve() / 'image_input.xyz' + atoms.write( + self.workdir / input_image_file, + format='extxyz' + ) + sim_input['args']['image'] = { 'image_file': str(input_image_file), + # 'image_file': str((self.workdir / input_image_file).resolve()) } images = self.sim_input.get('args', {}).get('images', False) if images: - if images.get('images', False): + if images.get('images', False) or images.get('image_file', False): images = get_images(**images) - input_images_file = self.workdir / 'input_images.xyz' - ase.io.write(input_images_file, images, format='extxyz') - self.sim_input['args']['images'] = { - 'image_file': str(input_images_file), - } - elif images.get('image_file', False): - input_images_file = Path(images['image_file']).resolve() - self.sim_input['args']['images'] = { + input_images_file = 'images_input.xyz' # Relative to workdir + ase.io.write( + self.workdir / input_images_file, + images, + format='extxyz' + ) + sim_input['args']['images'] = { 'image_file': str(input_images_file), } - write_yaml(workdir / 'sim_input.yaml', self.sim_input) - write_yaml(workdir / 'calc_input.yaml', self.calc_input) - write_yaml(workdir / 'env_input.yaml', self.env_input) + write_yaml(workdir / 'sim_input.yaml', sim_input) + if write_calc_input: + write_yaml(workdir / 'calc_input.yaml', self.calc_input) + if write_env_input: + write_yaml(workdir / 'env_input.yaml', self.env_input) write_yaml(self.get_output_yaml(), {'status': 'clean'}) if use_slurm and not interactive: @@ -357,20 +388,32 @@ def gen_input_files(self) -> None: return None def submit( - self, dependency: Union[List,None] = None + self, + dependency: Union[List,None] = None, ) -> Union[None,List[str]]: ''' Submit a job using slurm, interactively or in the terminal ''' + + msg = START_MESSAGE + msg += f'asimmodule: {self.sim_input["asimmodule"]}\n' + msg += f'Work directory: {self.workdir}' + logmsg = f'Submitting "{self.sim_input["asimmodule"]}" in "{self.workdir}"' + logging.info(logmsg) + print(msg) # For printing to console + self.gen_input_files() logger = self.get_logger() _, status = self.get_status(display=True) - if status in ('discard'): + if status in ('discard', 'complete'): txt = f'Current job status in "{self.workdir}" is "{status}", ' txt += 'skipping submission, set "overwrite=True" to ignore' logger.warning(txt) + if status in ('complete'): + print('Job already completed, not submitting again') + print(STOP_MESSAGE) return None if not self.sim_input.get('submit', True): @@ -378,9 +421,6 @@ def submit( %s', self.workdir) return None - msg = f'Submitting "{self.sim_input["script"]}" in "{self.workdir}"' - logging.info(msg) - print(msg) # For printing to console, maybe can be cleaner cur_dir = Path('.').resolve() os.chdir(self.workdir) mode_params = self.env.get('mode', {}) @@ -440,8 +480,10 @@ def submit( self.update_output({'job_ids': job_ids}) return job_ids - msg = f'Submitted "{self.sim_input["script"]}" in "{self.workdir}"' - logger.info(msg) + logmsg = f'Submitted "{self.sim_input["asimmodule"]}" in "{self.workdir}"' + msg = 'Successfully submitted\n' + msg += STOP_MESSAGE + logger.info(logmsg) print(msg) return None @@ -458,26 +500,35 @@ def __init__( # Set a standard for all subdirectories to start # with "id-{sim_id}" followed by user labels new_sim_input = {} - num_digits =len(str(len(sim_input))) + njobs = len(sim_input) + num_digits = 4 + assert njobs < 1000, \ + f'ASIMTools id_nums are limited to {num_digits} digits, found \ + {njobs} jobs! Having that many jobs is not very storage efficient.' + sim_id_changed = False for i, (sim_id, subsim_input) in enumerate(sim_input.items()): - assert 'script' in subsim_input, 'Check sim_input format,\ - must have script and each job with a unique key' + assert 'asimmodule' in subsim_input, 'Check sim_input format,\ + must have asimmodule and each job with a unique key' # We set a fixed number of digits so that the slurm array ID # matches the directory name for easy resubmission of arrays prefix = 'id-'+f'{i}'.rjust(num_digits, '0') if not sim_id.startswith(prefix): sim_id_changed = True - new_sim_id = join_names([prefix,sim_id]) - new_sim_input[new_sim_id] = deepcopy(sim_input[sim_id]) if sim_id_changed: + for i, (sim_id, subsim_input) in enumerate(sim_input.items()): + prefix = 'id-'+f'{i}'.rjust(num_digits, '0') + new_sim_id = join_names([prefix,sim_id]) + new_subsim_input = deepcopy(sim_input[sim_id]) + new_subsim_input['distributed_id'] = i + new_sim_input[new_sim_id] = new_subsim_input sim_input = new_sim_input unitjobs = [] for sim_id, subsim_input in sim_input.items(): - assert 'script' in subsim_input, 'Check sim_input format,\ - must have script and each job with a unique key' + assert 'asimmodule' in subsim_input, 'Check sim_input format,\ + must have asimmodule and each job with a unique key' subsim_input['workdir'] = sim_id unitjob = UnitJob(subsim_input, env_input, calc_input) @@ -652,7 +703,7 @@ def submit(self, **kwargs) -> None: class ChainedJob(Job): ''' Jobs made of smaller sequential unitjobs. Note that this only works - well with unit jobs. If one of the UnitJobs calls scripts, internally, + 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 ''' @@ -721,7 +772,7 @@ def submit(self, dependency: Union[List,None] = None) -> List: if not cur_step_complete: unitjob.env['slurm']['flags'].append(f'-J step-{step+i}') # Get latest job_id if previous step output was updated with new IDs - # This should allow scripts that run scripts to use correct dependencies + # This should allow asimmodules that run asimmodules to use correct dependencies # print(f'Job id for step-{i} is {dependency}') # if i > 0: # dependency = self.unitjobs[i-1].get_output().get('job_ids', dependency) @@ -772,4 +823,24 @@ def load_job_from_directory(workdir: str): calc_input = None job = Job(sim_input, env_input, calc_input) + + # This makes sure that wherever we may be loading the job from, we refer + # to the correct input/output files. As of now, it does not seem necessary + # to also change the workdir in sim_input.yaml for consistency + job.workdir = Path(workdir) return job + +def create_unitjob(sim_input, env_input, workdir, calc_input=None): + """Helper for making a generic UnitJob object, mostly for testing""" + env_id = list(env_input.keys())[0] + sim_input['env_id'] = env_id + if calc_input is not None: + calc_id = list(calc_input.keys())[0] + sim_input['calc_id'] = calc_id + sim_input['workdir'] = workdir + unitjob = UnitJob( + sim_input, + env_input, + calc_input + ) + return unitjob diff --git a/asimtools/scripts/asim_check.py b/asimtools/scripts/asim_check.py index 36793c9..6869973 100755 --- a/asimtools/scripts/asim_check.py +++ b/asimtools/scripts/asim_check.py @@ -4,6 +4,7 @@ and pring out the progress ''' import sys +import os from pathlib import Path import argparse from typing import Dict, Tuple @@ -12,7 +13,7 @@ from asimtools.job import load_job_from_directory -def parse_command_line(args) -> Tuple[Dict, str]: +def parse_command_line(args) -> Tuple[Dict, os.PathLike]: ''' Parse command line input ''' parser = argparse.ArgumentParser() parser.add_argument( @@ -23,7 +24,8 @@ def parse_command_line(args) -> Tuple[Dict, str]: ) args = parser.parse_args(args) sim_input = read_yaml(args.sim_input_file) - return sim_input + rootdir = Path('/'.join(args.sim_input_file.split('/')[:-1])) + return sim_input, rootdir def main(args=None) -> None: """Main @@ -31,9 +33,12 @@ def main(args=None) -> None: :param args: cmdline args, defaults to None :type args: _type_, optional """ - sim_input = parse_command_line(args) - rootdir = sim_input['workdir'] - jobtree = load_job_tree(rootdir) + sim_input, rootdir = parse_command_line(args) + if not sim_input['workdir'].startswith('/'): + workdir = rootdir / sim_input['workdir'] + else: + workdir = sim_input['workdir'] + jobtree = load_job_tree(workdir) print_job_tree(jobtree) def get_subjobs(workdir): @@ -61,7 +66,7 @@ def load_job_tree( :return: dictionary mimicking the job tree :rtype: Dict """ - workdir = Path(workdir) + workdir = Path(workdir).resolve() subjob_dirs = get_subjobs(workdir) if len(subjob_dirs) > 0: @@ -73,7 +78,7 @@ def load_job_tree( job = load_job_from_directory(workdir) job_dict = { - 'workdir': workdir.name, + 'workdir_name': workdir.name, 'job': job, 'subjobs': subjob_dict, } @@ -103,10 +108,10 @@ def print_job_tree(job_tree: Dict, indent_str='', level=0) -> None: reset = Fore.WHITE subjobs = job_tree['subjobs'] if subjobs is not None: - workdir = job_tree['workdir'] + workdir = job_tree['workdir_name'] status, color = get_status_and_color(job_tree['job']) - script = job_tree['job'].sim_input['script'] - print(color + f'{indent_str}{workdir}, script: {script},' + \ + asimmodule = job_tree['job'].sim_input['asimmodule'] + print(color + f'{indent_str}{workdir}, asimmodule: {asimmodule},' + \ f'status: {status}' + reset) if level > 0: indent_str = '| ' + ' ' * level @@ -119,11 +124,11 @@ def print_job_tree(job_tree: Dict, indent_str='', level=0) -> None: subjob = job_tree['job'] else: - subjob_dir = job_tree['workdir'] + subjob_dir = job_tree['workdir_name'] subjob = job_tree['job'] - script = subjob.sim_input['script'] + asimmodule = subjob.sim_input['asimmodule'] status, color = get_status_and_color(subjob) - print(color + f'{indent_str}{subjob_dir}, script: {script}, '+\ + print(color + f'{indent_str}{subjob_dir}, asimmodule: {asimmodule}, '+\ f'status: {status}' + reset) if __name__ == "__main__": diff --git a/asimtools/scripts/asim_execute.py b/asimtools/scripts/asim_execute.py index 039fad4..95a45e6 100755 --- a/asimtools/scripts/asim_execute.py +++ b/asimtools/scripts/asim_execute.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ''' Execute a workflow given the sim_input.yaml and optionally, -a calc_input.yaml and/or env_input.yaml. The called script will be run +a calc_input.yaml and/or env_input.yaml. The called asimmodule will be run in the specified workdir and env_id Author: mkphuthi@github.com @@ -60,18 +60,19 @@ def parse_command_line(args) -> Tuple[Dict, Dict]: return sim_input, env_input, calc_input - def main(args=None) -> None: - """Execute a workflow given the sim_input.yaml and optinally, - a calc_input.yaml and/or env_input.yaml. The called script will be run + """Execute a workflow given the sim_input.yaml and optionally, + a calc_input.yaml and/or env_input.yaml. The called asimmodule will be run in the specified workdir and env_id """ sim_input, env_input, calc_input = parse_command_line(args) + sim_input['workdir'] = sim_input.get('workdir', 'results') + job = UnitJob( sim_input=sim_input, env_input=env_input, calc_input=calc_input ) - job.gen_input_files() + job.gen_input_files(write_calc_input=True, write_env_input=True) job.go_to_workdir() try: job.submit() diff --git a/asimtools/scripts/asim_run.py b/asimtools/scripts/asim_run.py index 95ef566..7de5860 100755 --- a/asimtools/scripts/asim_run.py +++ b/asimtools/scripts/asim_run.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ''' -Execute a script given the sim_input.yaml and optionally, -a calc_input.yaml. The called script will be run directly in the current +Execute a asimmodule given the sim_input.yaml and optionally, +a calc_input.yaml. The called asimmodule will be run directly in the current directory and environment ''' @@ -64,7 +64,7 @@ def main(args=None) -> None: if calc_input_file is not None: assert Path(calc_input_file).exists(), 'Specify valid calc input file' os.environ["ASIMTOOLS_CALC_INPUT"] = calc_input_file - script = sim_input['script'] + asimmodule = sim_input['asimmodule'] precommands = sim_input.get('precommands', []) for precommand in precommands: @@ -84,36 +84,36 @@ def main(args=None) -> None: completed_process.check_returncode() - module_name = script.split('/')[-1].split('.py')[0] - func_name = module_name.split('.')[-1] # must match script name + module_name = asimmodule.split('/')[-1].split('.py')[0] + func_name = module_name.split('.')[-1] # must match asimmodule name - # Check if the script is a core script - spec = importlib.util.find_spec('.'+module_name,'asimtools.scripts') + # Check if the asimmodule is a core asimmodule + spec = importlib.util.find_spec('.'+module_name,'asimtools.asimmodules') if spec is not None: sim_module = importlib.import_module( - '.'+module_name,'asimtools.scripts' + '.'+module_name,'asimtools.asimmodules' ) - logger.debug('Loaded core script %s', module_name) + logger.debug('Loaded core asimmodule %s', module_name) else: - # Try and find a script from the set script directory - script_dir = os.environ.get('ASIMTOOLS_SCRIPT_DIR', False) - if script_dir: - # script = script.replace('.', '/') - script = Path(script_dir) / script + # Try and find a asimmodule from the set asimmodule directory + asimmodule_dir = os.environ.get('ASIMTOOLS_ASIMMODULE_DIR', False) + if asimmodule_dir: + # asimmodule = asimmodule.replace('.', '/') + asimmodule = Path(asimmodule_dir) / asimmodule spec = importlib.util.spec_from_file_location( - module_name, script + module_name, asimmodule ) - logger.debug('Loading script from script directory %s', script) + logger.debug('Loading asimmodule from asimmodule directory %s', asimmodule) if spec is None: spec = importlib.util.spec_from_file_location( - module_name, script + module_name, asimmodule ) - logger.debug('Loading script from full path %s', script) + logger.debug('Loading asimmodule from full path %s', asimmodule) try: sim_module = importlib.util.module_from_spec(spec) except Exception as exc: - err_msg = f'Failed to load module for script: "{script}"' + err_msg = f'Failed to load module for asimmodule: "{asimmodule}"' logger.error(err_msg) raise AttributeError(err_msg) from exc @@ -121,9 +121,9 @@ def main(args=None) -> None: try: spec.loader.exec_module(sim_module) except Exception as exc: - err_txt = f'Failed to load script: {script}. Check your ' - err_txt += 'ASIMTOOLS_SCRIPT_DIR environment variable, ' - err_txt += 'provide the full path and ensure the script works.' + err_txt = f'Failed to load asimmodule: {asimmodule}. Check your ' + err_txt += 'ASIMTOOLS_ASIMMODULE_DIR environment variable, ' + err_txt += 'provide the full path and ensure the asimmodule works.' logger.error(err_txt) raise FileNotFoundError(err_txt) from exc @@ -136,9 +136,9 @@ def main(args=None) -> None: try: results = sim_func(**sim_input.get('args', {})) - logger.info('Successfully ran script "%s" in "%s"', script, cwd) + logger.info('Successfully ran asimmodule "%s" in "%s"', asimmodule, cwd) except: - logger.info('Failed to run script "%s" in "%s"', script, cwd) + logger.info('Failed to run asimmodule "%s" in "%s"', asimmodule, cwd) # Restore the CALC_INPUT environment variable if old_calc_input_var is not None: os.environ["ASIMTOOLS_CALC_INPUT"] = old_calc_input_var @@ -157,9 +157,9 @@ def main(args=None) -> None: if os.getenv("ASIMTOOLS_CALC_INPUT", None) is not None: del os.environ["ASIMTOOLS_CALC_INPUT"] - # Get job IDs from internally run scripts if any + # Get job IDs from internally run asimmodules if any job_ids = results.get('job_ids', False) - # Otherwise get job_ids from wherever this script is running + # Otherwise get job_ids from wherever this asimmodule is running if not job_ids: job_ids = os.getenv('SLURM_JOB_ID') results['job_ids'] = job_ids diff --git a/asimtools/scripts/env_array.py b/asimtools/scripts/env_array.py deleted file mode 100755 index 76ed981..0000000 --- a/asimtools/scripts/env_array.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python -''' -Apply the same script using different envs - -Author: mkphuthi@github.com - -''' - -from typing import Dict, Sequence, Optional -from copy import deepcopy -from asimtools.job import DistributedJob - -def env_array( - subscript_input: Dict, - env_ids: Sequence[str], - calc_input: Optional[Dict] = None, - env_input: Optional[Dict] = None, - ids: Sequence = None, -) -> Dict: - """Run the same script using multiple env_ids - - :param subscript_input: sim_input for the script to be run - :type subscript_input: Dict - :param env_ids: Sequence of env_ids to iterate over - :type env_ids: Sequence[str] - :param calc_input: calc_input to override global file, defaults to None - :type calc_input: Optional[Dict], optional - :param env_input: env_input defaults to overide global file, defaults to\ - None - :type env_input: Optional[Dict], optional - :param ids: Sequence of custom ids for each env_id, defaults to None - :type ids: Sequence, optional - :return: Dictionary of results - :rtype: Dict - """ - array_sim_input = {} - - # Allow user to customize subdirectory names if needed - if ids is None: - ids = [str(i) for i in range(len(env_ids))] - else: - assert len(ids) == len(env_ids), \ - 'Num. of env_ids must match num. of ids' - - # Make individual sim_inputs for each env - for i, env_id in enumerate(env_ids): - new_subscript_input = deepcopy(subscript_input) - new_subscript_input['env_id'] = env_id - array_sim_input[f'{ids[i]}'] = new_subscript_input - - # Create a distributed job object - djob = DistributedJob(array_sim_input, env_input, calc_input) - job_ids = djob.submit() - - results = { - 'job_ids': job_ids, - } - return results diff --git a/asimtools/scripts/eos/eos.py b/asimtools/scripts/eos/eos.py deleted file mode 100644 index ab9758b..0000000 --- a/asimtools/scripts/eos/eos.py +++ /dev/null @@ -1,54 +0,0 @@ -'''.Xauthority''' - -from typing import Dict, Tuple -from asimtools.job import ChainedJob - -def eos( - image: dict, - singlepoint_env_id: str, - calc_id: str, - preprocess_env_id: str, - nimages: int = 5, - scale_range: Tuple[float] = (0.95, 1.05), - postprocess: bool = True, -) -> Dict: - ''' EOS calculation ''' - - sim_input = { - 'step-0': { - 'script': 'eos.preprocess', - 'env_id': preprocess_env_id, - 'args': { - 'image': image, - 'nimages': nimages, - 'scale_range': scale_range, - } - }, - 'step-1': { - 'script': 'image_array', - 'env_id': preprocess_env_id, - 'args': { - 'images': {'image_file': 'step-0/preprocess_images_output.xyz'}, - 'subscript_input': { - 'script': 'singlepoint', - 'env_id': singlepoint_env_id, - 'args': { - 'calc_id': calc_id, - 'properties': ['energy'], - } - } - } - }, - 'step-2': { - 'script': 'eos.postprocess', - 'env_id': preprocess_env_id, - 'submit': postprocess, #Fails if previous step is in a slurm queue - 'args': { - 'images': {'pattern': '../step-1/id-*/image_output.xyz'}, - } - } - } - - chain = ChainedJob(sim_input, env_input=None, calc_input=None) - chain.submit() - return chain.get_last_output() diff --git a/asimtools/scripts/eos/preprocess.py b/asimtools/scripts/eos/preprocess.py deleted file mode 100644 index 283663e..0000000 --- a/asimtools/scripts/eos/preprocess.py +++ /dev/null @@ -1,35 +0,0 @@ -'''.Xauthority''' - -from typing import Dict, Tuple -import numpy as np -from ase.io import write -from asimtools.utils import ( - get_atoms, -) - -def preprocess( - image: Dict, - nimages: int = 5, - scale_range: Tuple[float,float] = (0.95, 1.05), -) -> Dict: - ''' Prepares structures for EOS calculation ''' - - assert scale_range[0] < scale_range[1], 'x_scale[0] should be smaller than\ - x_scale[1]' - - atoms = get_atoms(**image) - - # Make a database of structures with the volumes scaled appropriately - scales = np.linspace(scale_range[0], scale_range[1], nimages) - scaled_images = [] - for scale in scales: - new_cell = atoms.get_cell() * scale - new_atoms = atoms.copy() - new_atoms.set_cell(new_cell, scale_atoms=True) - new_atoms.info['scale'] = f'{scale:.2f}' - scaled_images.append(new_atoms) - - scaled_images_file = 'preprocess_images_output.xyz' - write(scaled_images_file, scaled_images, format='extxyz') - - return {'files': {'images': scaled_images_file}} diff --git a/asimtools/scripts/template.py b/asimtools/scripts/template.py deleted file mode 100755 index c75a4a4..0000000 --- a/asimtools/scripts/template.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python -''' -Describe the script briefly here. If this is a script that runs multiple steps, -describe it here using reStructuredText to generate autodocs - -Cite the papers where the method/script was first introduced here as well - -Author: mkphuthi@github.com -''' - -from typing import Dict -# from asimtools.calculators import load_calc -from asimtools.utils import ( - get_atoms, - # get_images, -) - -def template( - # calc_id: str, - # image: Dict, # Optional: if using a structure - # images: Dict, # Optional: if using multiple structures -) -> Dict: - ''' - Script does xyz specifically - ''' - - # calc = load_calc(calc_id) # Optional: Any code that loads a calculator is replaced by this one line! - # atoms = get_atoms(**image) # Optional - # images = get_images(**images) # Optional - # atoms.set_calculator(calc) # Optional - - ############################# - # Do some cool science here # - ############################# - - results = { - 'energy': 'Placeholder_for_my_result', - 'files': {'image': 'placeholder_four_image_output'} - } - return results # Always return a dictionary! Use {} if necessary diff --git a/asimtools/scripts/vacancy_formation_energy/vacancy_formation_energy.py b/asimtools/scripts/vacancy_formation_energy/vacancy_formation_energy.py deleted file mode 100755 index 443c459..0000000 --- a/asimtools/scripts/vacancy_formation_energy/vacancy_formation_energy.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python -''' -Describe the script briefly here. If this is a script that runs multiple steps, -describe it here using reStructuredText to generate autodocs - -Cite the papers where the method/script was first introduced here as well - -Author: mkphuthi@github.com -''' - -from typing import Dict, Sequence, Optional -import logging -from asimtools.calculators import load_calc -from asimtools.utils import ( - get_atoms, -) - -def vacancy_formation_energy( - calc_id: str, - image: Dict, -) -> Dict: - ''' - - ''' - calc = load_calc(calc_id) - atoms = get_atoms(**image) - atoms.set_calculator(calc) - - results = {} - return results diff --git a/asimtools/utils.py b/asimtools/utils.py index c6e9ad6..75deb00 100644 --- a/asimtools/utils.py +++ b/asimtools/utils.py @@ -121,7 +121,7 @@ def join_names(substrs: Sequence[str]) -> str: for substr in new_substrs: if len(substr) > 0: final_substrs.append(substr) - name = '_'.join(final_substrs) + name = '__'.join(final_substrs) + '__' return name def get_atoms( @@ -160,6 +160,8 @@ def get_atoms( "Specify atoms, image_file or use ase.build tools" if image_file is not None: + assert Path(image_file).exists(), \ + f'The image_file {image_file} does not exist from {os.getcwd()}' atoms = read(image_file, **kwargs) elif atoms is None: builder_func = getattr(ase.build, builder) @@ -242,7 +244,7 @@ def get_images( image_file = Path(image_file).resolve() images = read(image_file, index=index, **kwargs) if not isinstance(images, list): - images = [images] + images = [images] elif pattern is not None: image_files = glob(pattern) assert len(image_files) > 0, \ @@ -275,14 +277,20 @@ def get_images( for pattern in patterns: image_files = glob(pattern) assert len(image_files) > 0, \ - f'No images matching patterns "{pattern}" from "{os.getcwd()}"' - images += get_images(pattern=pattern, index=index, **kwargs) + f'Don\'t include pattern "{pattern}" if no files match' + images += get_images( + pattern=pattern, + index=index, + skip_failed=skip_failed, + **kwargs + ) elif images is not None: images = images[parse_slice(str(index))] else: images = [] - assert len(images) > 0, 'No images found' + if not skip_failed: + assert len(images) > 0, 'No images found' return images @@ -410,6 +418,31 @@ def change_dict_value( d[key_sequence[0]] = new_d return d +def change_dict_values( + d: Dict, + new_values: Sequence, + key_sequences: Sequence, + return_copy: bool = True +) -> Dict: + """Changes values in the specified dictionary given by following the + key sequences. Key-value pairs are set in the given order + + :param d: dictionary to be changed + :type d: Dict + :param new_values: The new values that will replace the old one + :type new_values: Sequence + :param key_sequence: List of list of keys in the order in which they access the dictionary key + :type key_sequence: Sequence + :param return_copy: Whether to return a copy only or to modify the dictionary in-place as well, defaults to True + :type return_copy: bool, optional + :return: The changed dictionary + :rtype: Dict + """ + for key_sequence, new_value in zip(key_sequences, new_values): + d = change_dict_value(d, new_value, key_sequence, return_copy) + + return d + def get_logger( logfile='job.log', fstr='%(asctime)s |%(module)s|%(funcName)s| %(levelname)s: %(message)s', @@ -432,16 +465,41 @@ def get_logger( return logging.getLogger(__name__) -def get_str_btn(s, s1, s2): +def get_str_btn( + s: str, + s1: str, + s2: str, + start: int = 0, + end: int = 0 +): ''' Return substring between the first instance of s1 and the first instance of s2 after s1 in s. None corresponds to beginning or end of string for s1 and s2 respectively ''' + if s1 is not None: - i1 = s.index(s1) + len(s1) + i1 = s.index(s1, start) + len(s1) else: i1 = 0 if s2 is not None: - i2 = s[i1:].index(s2) + i1 + i2 = s[i1:].index(s2, end) + i1 else: i2 = len(s) return s[i1:i2] + +def find_nth(haystack: str, needle: str, n: int) -> int: + ''' Return index of nth occurence of substrin in string ''' + start = haystack.find(needle) + while start >= 0 and n > 1: + start = haystack.find(needle, start+len(needle)) + n -= 1 + return start + +def get_nth_label( + s: str, + n: int = 1, +): + ''' Return nth label in a string potentially containin multiple labels, + indexing starts from 0 ''' + start = find_nth(s, '__', n=(n*2+1)) + print('start', start) + return get_str_btn(s, '__', '__', start=start) diff --git a/build/.doctrees/CONTRIBUTING.doctree b/build/.doctrees/CONTRIBUTING.doctree new file mode 100644 index 0000000..24eae18 Binary files /dev/null and b/build/.doctrees/CONTRIBUTING.doctree differ diff --git a/build/.doctrees/README.doctree b/build/.doctrees/README.doctree new file mode 100644 index 0000000..1185b46 Binary files /dev/null and b/build/.doctrees/README.doctree differ diff --git a/build/.doctrees/asimplify.doctree b/build/.doctrees/asimplify.doctree new file mode 100644 index 0000000..68df9ae Binary files /dev/null and b/build/.doctrees/asimplify.doctree differ diff --git a/build/.doctrees/asimtools.doctree b/build/.doctrees/asimtools.doctree new file mode 100644 index 0000000..a298e7d Binary files /dev/null and b/build/.doctrees/asimtools.doctree differ diff --git a/build/.doctrees/asimtools.scripts.doctree b/build/.doctrees/asimtools.scripts.doctree new file mode 100644 index 0000000..77b0927 Binary files /dev/null and b/build/.doctrees/asimtools.scripts.doctree differ diff --git a/build/.doctrees/asimtools.scripts.eos.doctree b/build/.doctrees/asimtools.scripts.eos.doctree new file mode 100644 index 0000000..d6c7121 Binary files /dev/null and b/build/.doctrees/asimtools.scripts.eos.doctree differ diff --git a/build/.doctrees/environment.pickle b/build/.doctrees/environment.pickle new file mode 100644 index 0000000..d81c84f Binary files /dev/null and b/build/.doctrees/environment.pickle differ diff --git a/build/.doctrees/include_contributing.doctree b/build/.doctrees/include_contributing.doctree new file mode 100644 index 0000000..21e26fb Binary files /dev/null and b/build/.doctrees/include_contributing.doctree differ diff --git a/build/.doctrees/include_readme.doctree b/build/.doctrees/include_readme.doctree new file mode 100644 index 0000000..3e4df33 Binary files /dev/null and b/build/.doctrees/include_readme.doctree differ diff --git a/build/.doctrees/index.doctree b/build/.doctrees/index.doctree new file mode 100644 index 0000000..0e7da71 Binary files /dev/null and b/build/.doctrees/index.doctree differ diff --git a/build/.doctrees/modules.doctree b/build/.doctrees/modules.doctree new file mode 100644 index 0000000..286b722 Binary files /dev/null and b/build/.doctrees/modules.doctree differ diff --git a/build/.doctrees/usage.doctree b/build/.doctrees/usage.doctree new file mode 100644 index 0000000..f3e1029 Binary files /dev/null and b/build/.doctrees/usage.doctree differ diff --git a/build/.doctrees/workflows.doctree b/build/.doctrees/workflows.doctree new file mode 100644 index 0000000..abeb235 Binary files /dev/null and b/build/.doctrees/workflows.doctree differ diff --git a/build/CONTRIBUTING.html b/build/CONTRIBUTING.html deleted file mode 100644 index 28906fa..0000000 --- a/build/CONTRIBUTING.html +++ /dev/null @@ -1,198 +0,0 @@ - - - - - - - Contributing — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

Contributing

-

When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change.

-

Please note we have a code of conduct, please follow it in all your interactions with the project.

-

There are two main ways to contribute. One is to make changes to the existing -codes and scripts. In this case, please make sure your change passes tests or -submit a test accordinly. The second way to contribute is to add a new script -to the core set of scripts. Reviewers will work with you to make sure your new -script follows best practice including using utils, syntax highlighting, docs -and tests.

-
-

Pull Request Process

-
    -
  1. Ensure any install or build dependencies are removed before the end of the layer when doing a build.

  2. -
  3. Update the README.md with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters.

  4. -
  5. Increase the version numbers in any examples files and the README.md to the new version that this Pull Request would represent. The versioning scheme we use is SemVer.

  6. -
  7. You may merge the Pull Request in once you have the sign-off of one other developer, or if you do not have permission to do that, you may request the other reviewer to merge it for you.

  8. -
-
-
-

Code of Conduct

-
-

Our Pledge

-

In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation.

-
-
-

Our Standards

-

Examples of behavior that contributes to creating a positive environment -include:

-
    -
  • Using welcoming and inclusive language

  • -
  • Being respectful of differing viewpoints and experiences

  • -
  • Gracefully accepting constructive criticism

  • -
  • Focusing on what is best for the community

  • -
  • Showing empathy towards other community members

  • -
-

Examples of unacceptable behavior by participants include:

-
    -
  • The use of sexualized language or imagery and unwelcome sexual attention or -advances

  • -
  • Trolling, insulting/derogatory comments, and personal or political attacks

  • -
  • Public or private harassment

  • -
  • Publishing others’ private information, such as a physical or electronic -address, without explicit permission

  • -
  • Other conduct which could reasonably be considered inappropriate in a -professional setting

  • -
-
-
-

Our Responsibilities

-

Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior.

-

Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful.

-
-
-

Scope

-

This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers.

-
-
-

Enforcement

-

Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at [INSERT EMAIL ADDRESS]. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately.

-

Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project’s leadership.

-
-
-

Attribution

-

This Code of Conduct is adapted from the Contributor Covenant, version 1.4, -available at http://contributor-covenant.org/version/1/4

-
-
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/README.html b/build/README.html deleted file mode 100644 index 7423f81..0000000 --- a/build/README.html +++ /dev/null @@ -1,287 +0,0 @@ - - - - - - - Atomic SIMulation Tools — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

Atomic SIMulation Tools

-

This package is a lightweight workflow and simulation manager for reproducible -atomistic simulations that can be transferred across environments, calculators -and structures. By using in-built or user-defined scripts and utilities, users -can run/build their own simulation recipes and automagically scale them on -slurm based clusters. The core idea is to separate the dependence of the -atomistic potential/calculator and the simulations steps thereby allowing the -same simulation to be run with multiple calculators and the same calculator to -be used for multiple simulations. Input and output files must follow a strict -format so that consistent analysis pipelines can be used across users

-
-

Developer philosophy

-

The goal of asimtools is to push all the complexity of workflow -management, best-practices, file management etc. into the backend such that the -everyday user only has to handle input files for existing workflows and script -files for workflows they want to implement. The following are the guiding -principles for how asimtools should work:

-
    -
  • Scripts should resemble boilerplate ASE code as much as possible.

  • -
  • Scripts should not explicitly depend on a calculator

  • -
  • Scripts should not explicitly depend on the context in which they run

  • -
  • It should be easy to debug individual scripts/parts in large workflows. In -addition, it should always be possible to debug/resubmit jobs without using -asimtools.

  • -
  • input file structure and format should be standard across all scripts. In -addition all input parameters should match how they would like without -asimtools i.e. do not provide an API! This means extensive use of importlib.

  • -
  • output file structure should be predictable across all scripts

  • -
  • Job progress tracking must be incorporated

  • -
  • Best practices should be built-in e.g. if multiple jobs of the same slurm -context are submitted simulataneously, it must be a job array

  • -
-
-
-

Philosophy on User Experience

-

The philosophy is to build “simulations” using building blocks of scripts. -These scripts can be as complicated/efficient as you make them using any -external packages you want and can be optimized with time but can still be run -within the framework. This allows a test friendly way to transition from say a -tutorial on the ASE/pymatgen website to an asimtools script. So while -complicated wrappers are discouraged, they would still work as long as the -script works. The benefit out of the box is that you can make your script -independent of calculator or input structures and submit them easily.

-
-
-

Getting Started

-

These instructions will give you a copy of the project up and running on -your local machine for development and testing purposes.

-
-

Installing

-

You can install asimtools in a new conda environment using:

-
conda create -n asimtools python=3.9
-conda activate asimtools
-
-conda install ase -c conda-forge
-
-git clone https://github.com/BattModels/asimtools.git
-cd asimtools
-pip install -e .
-
-
-

This installs the base package in developer mode so that you do not have to -reinstall if you make changes to the core package.

-

Individual calculators may need external packages for loading those -calculators. Similarly some scripts e.g. lammps.py might also need external packages to be used. It is up to the user to make sure those are installed.

-

You will also need to setup some environment variables, these variables point -to a env_input.yaml and calc_input.yaml with your favorite configs since -these are commonly shared among simulations. You can also directly specify them -when running asim-execute (See asim-execute -h). -Examples of these files can be found in the examples.

-

Add the following to your .bashrc

-
export ASIMTOOLS_ENV_INPUT=/path/to/my/global/env_input.yaml
-export ASIMTOOLS_CALC_INPUT=/path/to/my/global/calc_input.yaml
-export ASIMTOOLS_SCRIPT_DIR=/path/to/my/script/dir
-
-
- - - - -
-
-
-

Running the tests

-

To run all tests from the tests directory, call:

-
pytest
-
-
-

To run the test suite on a component component.py , call:

-
pytest test_component.py
-
-
- -
-
-

Contributing

-

Please read CONTRIBUTING.md for details on our code -of conduct, and the process for submitting pull requests to us.

-
-
-

Authors

- -

See also the list of -contributors -who participated in this project.

-
-
-

License

-

This project is licensed under the MIT License

- -
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/calculators.html b/build/_modules/asimtools/calculators.html deleted file mode 100644 index a9115dc..0000000 --- a/build/_modules/asimtools/calculators.html +++ /dev/null @@ -1,229 +0,0 @@ - - - - - - asimtools.calculators — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for asimtools.calculators

-'''
-Tools for loading and returning ASE calculator objects for use in simulations
-'''
-import importlib
-import logging
-from typing import Dict, Optional
-from asimtools.utils import get_calc_input
-
-# pylint: disable=import-outside-toplevel
-# pylint: disable=import-error
-
-
[docs]def load_calc( - calc_id: str = None, - calc_input: Optional[Dict] = None -): - """Loads a calculator using given calc_id or calc_input - - :param calc_id: ID/key to use to load calculator from the supplied/global\ - calc_input file, defaults to None - :type calc_id: str, optional - :param calc_input: calc_input dictionary for a single calculator\ - , defaults to None - :type calc_input: Optional[Dict], optional - :return: ASE calculator instance - :rtype: :class:`ase.calculators.calculators.Calculator` - """ - if calc_id is not None: - if calc_input is None: - calc_input = get_calc_input() - try: - calc_params = calc_input[calc_id] - except KeyError as exc: - msg = f'Calculator with calc_id: {calc_id} not found in' - msg += f'calc_input {calc_input}' - raise KeyError(msg) from exc - except AttributeError as exc: - raise AttributeError('No calc_input found') from exc - name = calc_params.get('name', None) - - if 'module' in calc_params: - loader = load_ase_calc - else: - try: - loader = external_calcs[name] - except KeyError as exc: - msg = 'Provide ASE module for calc or use' - msg += f' one of {external_calcs.keys()}' - raise KeyError(msg) from exc - - calc = loader(calc_params=calc_params) - label = calc_params.get('label', name) - calc.label = label - logging.debug('Loaded calculator %s', calc_id) - return calc
- - -
[docs]def load_nequip(calc_params): - """Load NequIP or Allegro calculator - - :param calc_params: args to pass to loader - :type calc_params: Dict - :return: NequIP ase calculator - :rtype: :class:`nequip.ase.nequip_calculator.NequIPCalculator` - """ - from nequip.ase.nequip_calculator import NequIPCalculator - if calc_params.get('float64', False): - import torch - torch.set_default_dtype(torch.float64) - try: - calc = NequIPCalculator.from_deployed_model(**calc_params.get('args', {})) - except Exception as exc: - msg = f"Failed to load NequIP with parameters:\n {calc_params}" - raise RuntimeError(msg) from exc - - return calc
- - -
[docs]def load_dp(calc_params): - """Load Deep Potential Calculator - - :param calc_params: args to pass to loader - :type calc_params: Dict - :return: DP calculator - :rtype: :class:`deepmd.calculator.DP` - """ - from deepmd.calculator import DP - - try: - calc = DP(**calc_params['args']) - except Exception: - print(f"Failed to load DP with parameters:\n {calc_params}") - raise - - return calc
- - -
[docs]def load_ase_calc(calc_params): - ''' Load any builtin ASE calculator ''' - module_name = calc_params.get('module', '') - try: - module = importlib.import_module(module_name) - except: - print("Check calc module") - raise - name = calc_params.get('name', None) - try: - calc_class = getattr(module, name) - except: - print("Check calc name") - raise - calc_args = calc_params.get('args', {}) - try: - calc = calc_class(**calc_args) - except: - print("Check calc args") - raise - return calc
- -external_calcs = { - 'NequIP': load_nequip, - 'Allegro': load_nequip, - 'DeepPotential': load_dp, -} -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/job.html b/build/_modules/asimtools/job.html deleted file mode 100644 index 7ce9b21..0000000 --- a/build/_modules/asimtools/job.html +++ /dev/null @@ -1,877 +0,0 @@ - - - - - - asimtools.job — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for asimtools.job

-'''
-Set of tools for handling jobs in slurm, interactively or in the teminal for
-a specific calculator.
-
-Author: mkphuthi@github.com
-'''
-
-import subprocess
-import os
-from pathlib import Path
-from datetime import datetime
-import logging
-from glob import glob
-from typing import List, TypeVar, Dict, Tuple, Union
-from copy import deepcopy
-import ase.io
-import numpy as np
-from asimtools.utils import (
-    read_yaml,
-    write_yaml,
-    join_names,
-    get_atoms,
-    get_images,
-    get_env_input,
-    get_calc_input,
-    get_logger,
-    check_if_slurm_job_is_running,
-)
-
-Atoms = TypeVar('Atoms')
-
-
[docs]class Job(): - ''' Abstract class for the job object ''' - - # pylint: disable=too-many-instance-attributes - def __init__( - self, - sim_input: Dict, - env_input: Union[Dict,None] = None, - calc_input: Union[Dict,None] = None, - ) -> None: - if env_input is None: - env_input = get_env_input() - if calc_input is None: - calc_input = get_calc_input() - self.env_input = deepcopy(env_input) - self.calc_input = deepcopy(calc_input) - self.sim_input = deepcopy(sim_input) - self.workdir = Path( - sim_input.get('workdir', '.') - ).resolve() - # Any subsequent input files should use the full path - self.sim_input['workdir'] = str(self.workdir) - self.launchdir = Path(os.getcwd()) - - # # See python logging docs - # logging.basicConfig( - # filename=self.workdir / 'job.log', - # level=logging.DEBUG, - # format='%(asctime)s %(name)s %(levelname)s: %(message)s' - # ) - # logger = logging.getLogger(__name__) - # self.logger = logger - -
[docs] def mkworkdir(self) -> None: - ''' Creates the work directory if it doesn't exist ''' - if not self.workdir.exists(): - self.workdir.mkdir()
- -
[docs] def update_status(self, status: str) -> None: - ''' Updates job status to specificied value ''' - self.update_output({'status': status})
- -
[docs] def start(self) -> None: - ''' Updates the output to signal that the job was started ''' - self.update_output({ - 'start_time': datetime.now().strftime('%H:%M:%S, %m/%d/%y') - }) - self.update_status('started')
- -
[docs] def complete(self) -> None: - ''' Updates the output to signal that the job was started ''' - self.update_output({ - 'end_time': datetime.now().strftime('%H:%M:%S, %m/%d/%y') - }) - self.update_status('complete')
- -
[docs] def fail(self) -> None: - ''' Updates status to failed ''' - self.update_status('failed')
- -
[docs] def discard(self) -> None: - ''' Updates status to discarded ''' - self.update_status('discard')
- -
[docs] def go_to_workdir(self) -> None: - ''' Go to workdir ''' - if not self.workdir.exists(): - self.mkworkdir() - os.chdir(self.workdir)
- -
[docs] def leave_workdir(self) -> None: - ''' goes to directory from which job was launched ''' - os.chdir(self.launchdir)
- -
[docs] def add_output_files(self, file_dict: Dict) -> None: - ''' Adds a file to the output file list ''' - files = self.get_output().get('files', {}) - files.update(file_dict) - self.update_output({'files': file_dict})
- -
[docs] def get_sim_input(self) -> Dict: - ''' Get simulation input ''' - return deepcopy(self.sim_input)
- -
[docs] def get_calc_input(self) -> Dict: - ''' Get calculator input ''' - return deepcopy(self.calc_input)
- -
[docs] def get_env_input(self) -> Dict: - ''' Get environment input ''' - return deepcopy(self.env_input)
- -
[docs] def get_output_yaml(self) -> Dict: - ''' Get current output file ''' - prefix = '' - out_fname = join_names([prefix, 'output.yaml']) - output_yaml = self.workdir / out_fname - return output_yaml
- -
[docs] def get_workdir(self) -> Path: - ''' Get working directory ''' - return self.workdir
- -
[docs] def get_output(self) -> Dict: - ''' Get values in output.yaml ''' - output_yaml = self.get_output_yaml() - if output_yaml.exists(): - return read_yaml(output_yaml) - else: - return {}
- -
[docs] def update_sim_input(self, new_params) -> None: - ''' Update simulation parameters ''' - self.sim_input.update(new_params)
- -
[docs] def update_calc_input(self, new_params) -> None: - ''' Update calculator parameters ''' - self.calc_input.update(new_params)
- -
[docs] def update_env_input(self, new_params) -> None: - ''' Update calculator parameters ''' - self.env_input.update(new_params)
- -
[docs] def set_workdir(self, workdir) -> None: - ''' Set working directory ''' - workdir = Path(workdir) - self.sim_input['workdir'] = str(workdir.resolve()) - self.workdir = workdir
- -
[docs] def get_status(self, display=False) -> Tuple[bool,str]: - ''' Check job status ''' - output = self.get_output() - job_id = output.get('job_id', False) - running = False - if job_id: - running = check_if_slurm_job_is_running(job_id) - if running: - status = 'started' - self.update_status(status) - complete = False - else: - status = output.get('status', 'clean') - complete = status == 'complete' - if display: - print(f'Current status in "{self.workdir}" is "{status}"') - return complete, status
- -
[docs] def update_output(self, output_update: Dict) -> None: - ''' Update output.yaml if it exists or write a new one ''' - output = self.get_output() - output.update(output_update) - write_yaml(self.get_output_yaml(), output)
- -
[docs] def get_logger(self, logfilename='job.log', level='info'): - ''' Get the logger ''' - assert self.workdir.exists(), 'Work directory does not exist yet' - logger = get_logger(logfile=self.workdir / logfilename, level=level) - return logger
- -
[docs]class UnitJob(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 - ''' - def __init__( - self, - sim_input: Dict, - env_input: Union[Dict,None] = None, - calc_input: Union[Dict,None] = None, - ) -> None: - super().__init__(sim_input, env_input, calc_input) - self.env_id = self.sim_input.get('env_id', None) - if self.env_id is not None: - self.env = self.env_input[self.env_id] - else: - self.env = {'mode': { - 'use_slurm': False, - 'interactive': True, - } - } - - # Check if the script being called uses a calc_id to - # get precommands, postcommands, run_prefixes and run_suffixes - self.calc_id = self.sim_input.get('args', {}).get('calc_id', None) - if self.calc_id is not None: - self.calc_params = self.calc_input[self.calc_id] - else: - self.calc_params = {} - -
[docs] def gen_run_command(self) -> str: - ''' - Generates the command to run the job, independent of slurm - ''' - script = self.sim_input.get('script', False) - assert script, 'Specify a script in sim_input' - - mode_params = self.env.get('mode', { - 'use_slurm': False, 'interactive': True - }) - env_run_prefix = mode_params.get('run_prefix', '') - env_run_suffix = mode_params.get('run_suffix', '') - - calc_run_prefix = self.calc_params.get('run_prefix', '') - calc_run_suffix = self.calc_params.get('run_suffix', '') - - if self.sim_input.get('debug', False): - debug_flag = ' -d' - else: - debug_flag = '' - - txt = f'{env_run_prefix} {calc_run_prefix} ' - txt += 'asim-run sim_input.yaml -c calc_input.yaml' + debug_flag - txt += f' {env_run_suffix} {calc_run_suffix} ' - return txt
- - def _gen_slurm_script(self, write: bool = True) -> None: - ''' - Generates a slurm job file and if necessary writes it in - the work directory for the job - ''' - slurm_params = self.env.get('slurm', {}) - calc_params = self.calc_params - - txt = '#!/usr/bin/sh\n' - for flag in slurm_params.get('flags'): - txt += f'#SBATCH {flag}\n' - txt += '\n' - for command in slurm_params.get('precommands', []): - txt += command + '\n' - for command in calc_params.get('precommands', []): - txt += command + '\n' - txt += '\n' - txt += 'echo "Job started on `hostname` at `date`"\n' - txt += self.gen_run_command() + '\n' - for command in slurm_params.get('postcommands', []): - txt += command + '\n' - for command in calc_params.get('postcommands', []): - txt += command + '\n' - txt += 'echo "Job ended at `date`"' - - if write: - slurm_file = self.workdir / 'job.sh' - slurm_file.write_text(txt) - - def _gen_slurm_interactive_txt(self) -> str: - ''' - Generates an srun command to run the job - ''' - slurm_params = self.env.get('slurm', False) - - txt = 'salloc --unbuffered ' # unbuffered to avoid IO lag - txt += ' '.join(slurm_params.get('flags')) - txt += self.gen_run_command() - - return txt - -
[docs] def gen_input_files(self) -> None: - ''' Write input files to working directory ''' - workdir = self.workdir - sim_input = deepcopy(self.sim_input) - # The sim_input file will already be in the work directory - sim_input['workdir'] = '.' - # Collect useful variables - mode_params = self.env.get('mode', {}) - use_slurm = mode_params.get('use_slurm', False) - interactive = mode_params.get('interactive', True) - overwrite = self.sim_input.get('overwrite', False) - output_file = workdir / 'output.yaml' - sim_input_file = workdir / 'sim_input.yaml' - - # Check if the job already exists or not and if to overwrite - if not workdir.exists(): - workdir.mkdir() - elif output_file.exists() and sim_input_file.exists(): - complete = read_yaml(output_file).get('start_time', False) - if complete and not overwrite: - txt = f'Skipped writing in {self.workdir}, files already ' - txt += 'written. Set overwrite=True to overwrite' - self.get_logger().warning(txt) - return None - - # If the sim_input uses an image/images, we want to write it in - # the workdir and point to it using a filename. This is so that - # there is a record of the input image for debugging and the input - # method is consistent with complex workflows - image = self.sim_input.get('args', {}).get('image', False) - - if image: - atoms = get_atoms(**image) - input_image_file = self.workdir.resolve() / 'input_image.xyz' - atoms.write(input_image_file, format='extxyz') - self.sim_input['args']['image'] = { - 'image_file': str(input_image_file), - } - - images = self.sim_input.get('args', {}).get('images', False) - if images: - if images.get('images', False): - images = get_images(**images) - input_images_file = self.workdir / 'input_images.xyz' - ase.io.write(input_images_file, images, format='extxyz') - self.sim_input['args']['images'] = { - 'image_file': str(input_images_file), - } - elif images.get('image_file', False): - input_images_file = Path(images['image_file']).resolve() - self.sim_input['args']['images'] = { - 'image_file': str(input_images_file), - } - - write_yaml(workdir / 'sim_input.yaml', self.sim_input) - write_yaml(workdir / 'calc_input.yaml', self.calc_input) - write_yaml(workdir / 'env_input.yaml', self.env_input) - write_yaml(self.get_output_yaml(), {'status': 'clean'}) - - if use_slurm and not interactive: - self._gen_slurm_script() - return None
- -
[docs] def submit( - self, dependency: Union[List,None] = None - ) -> Union[None,List[str]]: - ''' - Submit a job using slurm, interactively or in the terminal - ''' - self.gen_input_files() - - logger = self.get_logger() - - _, status = self.get_status(display=True) - if status in ('discard'): - txt = f'Current job status in "{self.workdir}" is "{status}", ' - txt += 'skipping submission, set "overwrite=True" to ignore' - logger.warning(txt) - return None - - if not self.sim_input.get('submit', True): - logger.warning('Keyword submit=False, skipping submission in \ - %s', self.workdir) - return None - - msg = f'Submitting "{self.sim_input["script"]}" in "{self.workdir}"' - logging.info(msg) - print(msg) - cur_dir = Path('.').resolve() - os.chdir(self.workdir) - mode_params = self.env.get('mode', {}) - sim_input = self.sim_input - use_slurm = mode_params.get('use_slurm', False) - interactive = mode_params.get('interactive', True) - - # Case when running batch jobs, handles job dependency - if use_slurm and not interactive: - if dependency is not None: - dependstr = None - for dep in dependency: - if dependstr is None: - dependstr = str(dep) if dep is not None else None - else: - dependstr += f':{dep}' if dep is not None else '' - - command = ['sbatch', '-d', f'afterok:{dependstr}', 'job.sh'] - else: - command = ['sbatch', 'job.sh'] - - # Interactive jobs launched with srun - elif use_slurm and interactive: - command = self._gen_slurm_interactive_txt().split() - - # Launching the job inline - else: - command = self.gen_run_command().split() - - run_job = False - overwrite = sim_input.get('overwrite', False) - _, status = self.get_status() - if overwrite or status == 'clean': - run_job = True - - if run_job: - completed_process = subprocess.run( - command, check=False, capture_output=True, text=True, - ) - - with open('stdout.txt', 'w', encoding='utf-8') as output_file: - output_file.write(completed_process.stdout) - - if completed_process.returncode != 0: - err_msg = f'See {self.workdir / "stderr.txt"} for traceback.' - logger.error(err_msg) - with open('stderr.txt', 'w', encoding='utf-8') as err_file: - err_file.write(completed_process.stderr) - completed_process.check_returncode() - - os.chdir(cur_dir) - - # Return job ids for chaining dependencies in batch mode - if use_slurm and not interactive and run_job: - job_ids = [int(completed_process.stdout.split(' ')[-1])] - self.update_output({'job_ids': job_ids}) - return job_ids - - msg = f'Submitted "{self.sim_input["script"]}" in "{self.workdir}"' - logger.info(msg) - print(msg) - - return None
- -
[docs]class DistributedJob(Job): - ''' Array job object with ability to submit simultaneous jobs ''' - def __init__( - self, - sim_input: Dict, - env_input: Union[None,Dict] = None, - calc_input: Union[None,Dict] = None, - ) -> None: - super().__init__(sim_input, env_input, calc_input) - # Set a standard for all subdirectories to start - # with "id-{sim_id}" followed by user labels - new_sim_input = {} - num_digits =len(str(len(sim_input))) - sim_id_changed = False - for i, (sim_id, subsim_input) in enumerate(sim_input.items()): - assert 'script' in subsim_input, 'Check sim_input format,\ - must have script and each job with a unique key' - - # We set a fixed number of digits so that the slurm array ID - # matches the directory name for easy resubmission of arrays - prefix = 'id-'+f'{i}'.rjust(num_digits, '0') - if not sim_id.startswith(prefix): - sim_id_changed = True - new_sim_id = join_names([prefix,sim_id]) - new_sim_input[new_sim_id] = deepcopy(sim_input[sim_id]) - - if sim_id_changed: - sim_input = new_sim_input - unitjobs = [] - for sim_id, subsim_input in sim_input.items(): - assert 'script' in subsim_input, 'Check sim_input format,\ - must have script and each job with a unique key' - - subsim_input['workdir'] = sim_id - unitjob = UnitJob(subsim_input, env_input, calc_input) - unitjobs.append(unitjob) - - # If all the jobs have the same config and use slurm, use a job array - env_id = unitjobs[0].env_id - same_env = np.all( - [(uj.env_id == env_id) for uj in unitjobs] - ) - - all_slurm = np.all( - [uj.env['mode'].get('use_slurm', False) for uj in unitjobs] - ) - - if same_env and all_slurm: - self.use_array = True - else: - self.use_array = False - - self.unitjobs = unitjobs - -
[docs] def submit_jobs( - self, - **kwargs, # Necessary for compatibility with job.submit - ) -> Union[None,List[str]]: - ''' - Submits the jobs. If submitting lots of batch jobs, we - recommend using DistributedJob.submit_array - ''' - # It is possible to do an implementation where we limit - # the number of simultaneous jobs like an array using job - # dependencies but that can be implemented later - job_ids = [] - for unitjob in self.unitjobs: - job_id = unitjob.submit() - job_ids.append(job_id) - return job_ids
- -
[docs] def submit_array( - self, - array_max=None, - dependency: Union[List[str],None] = None, - **kwargs, # Necessary for compatibility with job.submit - ) -> Union[None,List[str]]: - ''' - Submits a job array if all the jobs have the same env and use slurm - ''' - self.gen_input_files() - logger = self.get_logger() - - njobs = len(self.unitjobs) - - # For limiting number of jobs launched in array - if array_max is not None: - arr_max_str = f'%{array_max}' - else: - arr_max = self.unitjobs[0].env['mode'].get('array_max', False) - if arr_max: - arr_max_str = f'%{arr_max}' - else: - arr_max_str = '' - - if dependency is not None: - dependstr = None - for dep in dependency: - if dependstr is None: - dependstr = str(dep) if dep is not None else None - else: - dependstr += f':{dep}' if dep is not None else '' - - command = [ - 'sbatch', - f'--array=[0-{njobs-1}]{arr_max_str}', - '-d', f'afterok:{dependstr}', - 'job_array.sh' - ] - else: - command = [ - 'sbatch', - f'--array=[0-{njobs-1}]{arr_max_str}', - 'job_array.sh' - ] - - completed_process = subprocess.run( - command, check=False, capture_output=True, text=True, - ) - - with open('stdout.txt', 'w', encoding='utf-8') as output_file: - output_file.write(completed_process.stdout) - - # if completed_process.returncode != 0: - # err_txt = 'ERROR: Failed to run array in ' - # err_txt += f'{self.workdir} with command:\n' - # err_txt += f'{" ".join(command)}\n' - # err_txt += f'See {self.workdir / "stderr.txt"} for details.' - # print(err_txt) - # with open('stderr.txt', 'w', encoding='utf-8') as err_file: - # err_file.write(completed_process.stderr) - # completed_process.check_returncode() - - if completed_process.returncode != 0: - err_msg = f'See {self.workdir / "stderr.txt"} for traceback.' - logger.error(err_msg) - with open('stderr.txt', 'w', encoding='utf-8') as err_file: - err_file.write(completed_process.stderr) - completed_process.check_returncode() - - job_ids = [int(completed_process.stdout.split(' ')[-1])] - return job_ids
- - def _gen_array_script(self, write: bool = True) -> None: - ''' - Generates a slurm job array file and if necessary - writes it in the work directory for the job. Only works - if there is one config_id for all jobs - ''' - env_id = self.unitjobs[0].env_id - env = self.env_input[env_id] - - slurm_params = env.get('slurm', {}) - - txt = '#!/usr/bin/sh\n' - for flag in slurm_params.get('flags'): - txt += f'#SBATCH {flag}\n' - - txt += 'if [[ ! -z ${SLURM_ARRAY_TASK_ID} ]]; then\n' - txt += ' fls=( id-* )\n' - txt += ' WORKDIR=${fls[${SLURM_ARRAY_TASK_ID}]}\n' - txt += 'fi\n\n' - txt += 'CUR_DIR=`pwd`\n' - txt += 'cd ${WORKDIR}\n' - txt += '\n' - txt += '\n'.join(slurm_params.get('precommands', [])) - txt += '\n' - txt += '\n'.join(self.unitjobs[0].calc_params.get('precommands', [])) - txt += '\n' - txt += 'echo "LAUNCHDIR: ${CUR_DIR}"\n' - txt += 'echo "WORKDIR: ${WORKDIR}"\n' - txt += 'echo "Job started on `hostname` at `date`"\n' - txt += self.unitjobs[0].gen_run_command() + '\n' - txt += '\n'.join(slurm_params.get('postcommands', [])) - txt += '\n' - txt += 'cd ${CUR_DIR}\n' - txt += 'echo "Job ended at `date`"' - - if write: - slurm_file = self.workdir / 'job_array.sh' - slurm_file.write_text(txt) - -
[docs] def gen_input_files(self) -> None: - ''' Write input files to working directory ''' - - if self.use_array: - self._gen_array_script() - for unitjob in self.unitjobs: - unitjob.gen_input_files()
- -
[docs] def submit(self, **kwargs) -> None: - ''' - Submit a job using slurm, interactively or in the terminal - ''' - - cur_dir = Path('.').resolve() - os.chdir(self.workdir) - job_ids = None - if self.use_array: - job_ids = self.submit_array(**kwargs) - else: - job_ids = self.submit_jobs(**kwargs) - self.update_output({'job_ids': job_ids}) # For chained job - os.chdir(cur_dir) - return job_ids
- -
[docs]class ChainedJob(Job): - ''' - Jobs made of smaller sequential unitjobs. Note that this only works - well with unit jobs. If one of the UnitJobs calls scripts, internally, - slurm will fail to build the correct dependencies but the files will be - generated appropriately, you can submit them manually - ''' - def __init__( - self, - sim_input: Dict, - env_input: Union[None,Dict] = None, - calc_input: Union[None,Dict] = None, - ) -> None: - super().__init__(sim_input, env_input, calc_input) - - for key in sim_input: - assert key.startswith('step-'), \ - f'Keys take the form "step-[step_id]" from id=0 not "{key}"' - - # Each key in sim_input should be in the form "step-id" where id - # is replaced by the order in which the commands are run - unitjobs = [] - for i in range(len(sim_input)): - step_id = f'step-{i}' - subsim_input = deepcopy(sim_input[step_id]) - unitjob = UnitJob(subsim_input, env_input, calc_input) - unitjob.set_workdir(step_id) - unitjobs.append(unitjob) - - self.unitjobs = unitjobs - -
[docs] def get_last_output(self) -> Dict: - ''' Returns the output of the last job in the chain ''' - return self.unitjobs[-1].get_output()
- -
[docs] def submit(self, dependency: Union[List,None] = None) -> List: - ''' - Submit a job using slurm, interactively or in the terminal - ''' - - cur_dir = Path('.').resolve() - os.chdir(self.workdir) - logger = self.get_logger() - step = 0 #self.get_current_step() TODO: This feature is not used yet - are_interactive_jobs = [ - uj.env['mode'].get('interactive', False) \ - for uj in self.unitjobs - ] - all_interactive = np.all(are_interactive_jobs) - all_not_interactive = np.sum(are_interactive_jobs) == 0 - assert all_interactive or all_not_interactive, \ - 'Jobs in chain must all be interactive=True or interactive=False' - - are_using_slurm = [ - uj.env['mode'].get('use_slurm', False) \ - for uj in self.unitjobs - ] - all_slurm = np.all(are_using_slurm) - all_not_slurm = np.sum(are_using_slurm) == 0 - - assert all_slurm or all_not_slurm, \ - 'Jobs in chain must all have use_slurm=True or use_slurm=False' - - # Only use slurm dependencies if all the jobs are batch jobs - if all_slurm and all_not_interactive: - if step != len(self.unitjobs): - dependency = None - for i, unitjob in enumerate(self.unitjobs[step:]): - cur_step_complete, status = unitjob.get_status() - if not cur_step_complete: - unitjob.env['slurm']['flags'].append(f'-J step-{step+i}') - # Get latest job_id if previous step output was updated with new IDs - # This should allow scripts that run scripts to use correct dependencies - # print(f'Job id for step-{i} is {dependency}') - # if i > 0: - # dependency = self.unitjobs[i-1].get_output().get('job_ids', dependency) - # print(f'Dependency for step-{i} is {dependency}') - # print('===================') - dependency = unitjob.submit(dependency=dependency) - else: - txt = f'Skipping step-{step+i} since ' - txt += f'status is {status}' - logger.warning(txt) - - job_ids = dependency - - # Otherwise just submit them one after the other - else: - for unitjob in self.unitjobs[step:]: - job_ids = unitjob.submit() - - os.chdir(cur_dir) - - return job_ids
- - -
[docs]def load_job_from_directory(workdir: str): - ''' Loads a job from a given directory ''' - workdir = Path(workdir) - assert workdir.exists(), f'Work director {workdir} does not exist' - logger = get_logger() - sim_inputs = glob(str(workdir / 'sim_input.yaml')) - if len(sim_inputs) != 1: - logger('Multiple or no sim_input.yaml files in %s', {workdir}) - try: - sim_input = read_yaml(glob(str(workdir / 'sim_input.yaml'))[0]) - except: - logger('sim_input.yaml not found in %s', {workdir}) - raise - - env_inputs = glob(str(workdir / 'env_input.yaml')) - if len(env_inputs) == 1: - env_input = read_yaml(env_inputs[0]) - else: - env_input = None - - calc_inputs = glob(str(workdir / 'calc_input.yaml')) - if len(calc_inputs) == 1: - calc_input = read_yaml(calc_inputs[0]) - else: - calc_input = None - - job = Job(sim_input, env_input, calc_input) - return job
-
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/scripts/asim_check.html b/build/_modules/asimtools/scripts/asim_check.html deleted file mode 100644 index 2d6b0df..0000000 --- a/build/_modules/asimtools/scripts/asim_check.html +++ /dev/null @@ -1,236 +0,0 @@ - - - - - - asimtools.scripts.asim_check — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for asimtools.scripts.asim_check

-#!/usr/bin/env python
-'''
-Recursively check progress of jobs starting from a specified sim_input.yaml
-and pring out the progress
-'''
-import sys
-from pathlib import Path
-import argparse
-from typing import Dict, Tuple
-from colorama import Fore
-from asimtools.utils import read_yaml
-from asimtools.job import load_job_from_directory
-
-
-
[docs]def parse_command_line(args) -> Tuple[Dict, str]: - ''' Parse command line input ''' - parser = argparse.ArgumentParser() - parser.add_argument( - 'sim_input_file', - metavar='simulation_input_file', - type=str, - help='simulation input yaml file' - ) - args = parser.parse_args(args) - sim_input = read_yaml(args.sim_input_file) - return sim_input
- -
[docs]def main(args=None) -> None: - """Main - - :param args: cmdline args, defaults to None - :type args: _type_, optional - """ - sim_input = parse_command_line(args) - rootdir = sim_input['workdir'] - jobtree = load_job_tree(rootdir) - print_job_tree(jobtree)
- -
[docs]def get_subjobs(workdir): - """Get all the directories with jobs in them - - :param workdir: _description_ - :type workdir: _type_ - :return: _description_ - :rtype: _type_ - """ - subjob_dirs = [] - for subdir in workdir.iterdir(): - subsim_input = subdir / 'sim_input.yaml' - if subsim_input.exists(): - subjob_dirs.append(subdir) - return subjob_dirs
- -
[docs]def load_job_tree( - workdir: str = './', -) -> Dict: - """Loads all the jobs in a directory in a tree format using recursion - - :param workdir: root directory from which to recursively find jobs, defaults to './' - :type workdir: str, optional - :return: dictionary mimicking the job tree - :rtype: Dict - """ - workdir = Path(workdir) - - subjob_dirs = get_subjobs(workdir) - if len(subjob_dirs) > 0: - subjob_dict = {} - for subjob_dir in subjob_dirs: - subjob_dict[subjob_dir.name] = load_job_tree(subjob_dir) - else: - subjob_dict = None - - job = load_job_from_directory(workdir) - job_dict = { - 'workdir': workdir.name, - 'job': job, - 'subjobs': subjob_dict, - } - return job_dict
- -
[docs]def get_status_and_color(job): - ''' Helper to get printing colors ''' - status = job.get_status()[1] - if status == 'complete': - color = Fore.GREEN - elif status == 'failed': - color = Fore.RED - elif status == 'started': - color = Fore.BLUE - else: - color = Fore.WHITE - return status, color
- - - -if __name__ == "__main__": - main(sys.argv[1:]) -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/scripts/asim_execute.html b/build/_modules/asimtools/scripts/asim_execute.html deleted file mode 100644 index 82826d6..0000000 --- a/build/_modules/asimtools/scripts/asim_execute.html +++ /dev/null @@ -1,191 +0,0 @@ - - - - - - asimtools.scripts.asim_execute — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for asimtools.scripts.asim_execute

-#!/usr/bin/env python
-'''
-Execute a workflow given the sim_input.yaml and optionally,
-a calc_input.yaml and/or env_input.yaml. The called script will be run
-in the specified workdir and env_id
-
-Author: mkphuthi@github.com
-'''
-
-import sys
-import argparse
-from typing import Dict, Tuple
-from asimtools.utils import read_yaml
-from asimtools.job import UnitJob
-
-
-
[docs]def parse_command_line(args) -> Tuple[Dict, Dict]: - ''' Parse command line input ''' - parser = argparse.ArgumentParser(add_help=True) - parser.add_argument( - 'sim_input_file', - metavar='simulation_input_file', - type=str, - help='simulation input yaml file', - ) - parser.add_argument( - '-e', - '--env', - metavar='environment_input_file', - type=str, - help='environment input yaml file', - ) - parser.add_argument( - '-c', - '--calc', - metavar='calculator_input_file', - type=str, - help='calculator input yaml file', - ) - parser.add_argument( - '-d', - '--debug', - action='store_true', - help='Set logging level' - ) - args = parser.parse_args(args) - sim_input = read_yaml(args.sim_input_file) - # Do some cleanup of arguments - if args.debug: - sim_input['debug'] = True - - calc_input = args.calc - env_input = args.env - if env_input is not None: - env_input = read_yaml(env_input) - - calc_input = args.calc - if calc_input is not None: - calc_input = read_yaml(calc_input) - - return sim_input, env_input, calc_input
- - -
[docs]def main(args=None) -> None: - """Execute a workflow given the sim_input.yaml and optinally, - a calc_input.yaml and/or env_input.yaml. The called script will be run - in the specified workdir and env_id """ - sim_input, env_input, calc_input = parse_command_line(args) - job = UnitJob( - sim_input=sim_input, - env_input=env_input, - calc_input=calc_input - ) - job.gen_input_files() - job.go_to_workdir() - try: - job.submit() - except: - job.fail() - raise - job.leave_workdir()
- - -if __name__ == "__main__": - main(sys.argv[1:]) -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/scripts/asim_run.html b/build/_modules/asimtools/scripts/asim_run.html deleted file mode 100644 index c32c40d..0000000 --- a/build/_modules/asimtools/scripts/asim_run.html +++ /dev/null @@ -1,274 +0,0 @@ - - - - - - asimtools.scripts.asim_run — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for asimtools.scripts.asim_run

-#!/usr/bin/env python
-'''
-Execute a script given the sim_input.yaml and optionally,
-a calc_input.yaml. The called script will be run directly in the current
-directory and environment
-'''
-
-import importlib
-import sys
-import os
-from pathlib import Path
-import argparse
-import subprocess
-from typing import Dict, Tuple
-from asimtools.utils import read_yaml, get_logger
-from asimtools.job import load_job_from_directory
-
-
-
[docs]def parse_command_line(args) -> Tuple[Dict, str]: - ''' Parse command line input ''' - parser = argparse.ArgumentParser() - parser.add_argument( - 'sim_input_file', - metavar='simulation_input_file', - type=str, - help='simulation input yaml file' - ) - parser.add_argument( - '-c', - '--calc', - metavar='calculator_input_file', - type=str, - help='calculator input yaml file' - ) - parser.add_argument( - '-d', - '--debug', - action='store_true', - help='Set logging level' - ) - args = parser.parse_args(args) - - sim_input = read_yaml(args.sim_input_file) - if args.debug: - sim_input['debug'] = True - calc_input_file = args.calc - - return sim_input, calc_input_file
- - -
[docs]def main(args=None) -> None: - ''' Main ''' - sim_input, calc_input_file = parse_command_line(args) - - if sim_input.get('debug', False): - level = 'debug' - else: - level = 'warning' - logger = get_logger(level=level) - logger.debug('Entered asim_run') - - old_calc_input_var = os.getenv("ASIMTOOLS_CALC_INPUT", None) - if calc_input_file is not None: - assert Path(calc_input_file).exists(), 'Specify valid calc input file' - os.environ["ASIMTOOLS_CALC_INPUT"] = calc_input_file - script = sim_input['script'] - precommands = sim_input.get('precommands', []) - - for precommand in precommands: - command = precommand.split() - completed_process = subprocess.run(command, check=True) - completed_process = subprocess.run( - command, check=False, capture_output=True, text=True, - ) - - if completed_process.returncode != 0: - logger.error('asim-run: Failed to run precommand %s', precommand) - # print(completed_process.stderr) - logger.error(completed_process.stderr) - err_txt = f'ERROR: sim_input precommand "{" ".join(command)}" ' - err_txt += 'failed as above. Make sure precommand can be ' - err_txt += "executed by python's subprocess.run() routine" - logger.error(err_txt) - - completed_process.check_returncode() - - module_name = script.split('/')[-1].split('.py')[0] - func_name = module_name.split('.')[-1] # must match script name - - # Check if the script is a core script - spec = importlib.util.find_spec('.'+module_name,'asimtools.scripts') - if spec is not None: - sim_module = importlib.import_module( - '.'+module_name,'asimtools.scripts' - ) - logger.debug('Loaded core script %s', module_name) - else: - # Try and find a script from the set script directory - script_dir = os.environ.get('ASIMTOOLS_SCRIPT_DIR', False) - if script_dir: - # script = script.replace('.', '/') - script = Path(script_dir) / script - spec = importlib.util.spec_from_file_location( - module_name, script - ) - logger.debug('Loading script from script directory %s', script) - if spec is None: - spec = importlib.util.spec_from_file_location( - module_name, script - ) - logger.debug('Loading script from full path %s', script) - - sim_module = importlib.util.module_from_spec(spec) - sys.modules[module_name] = sim_module - try: - spec.loader.exec_module(sim_module) - except Exception as exc: - err_txt = f'Failed to find script: {script}. Check your ' - err_txt += 'ASIMTOOLS_SCRIPT_DIR environment variable or ' - err_txt += 'provide the full path' - logger.error(err_txt) - raise FileNotFoundError(err_txt) from exc - - sim_func = getattr(sim_module, func_name) - - cwd = Path('.').resolve() - job = load_job_from_directory(cwd) - job.start() - try: - results = sim_func(**sim_input.get('args', {})) - logger.info('Successfully ran script "%s" in "%s"', script, cwd) - except: - logger.info('Failed to run script "%s" in "%s"', script, cwd) - # Restore the CALC_INPUT environment variable - if old_calc_input_var is not None: - os.environ["ASIMTOOLS_CALC_INPUT"] = old_calc_input_var - else: - if os.getenv("ASIMTOOLS_CALC_INPUT", None) is not None: - del os.environ["ASIMTOOLS_CALC_INPUT"] - job.fail() - raise - - # Restore the previous calc_input in case other srcipts depend on it - # This is pretty messy right now, could probably be better - if old_calc_input_var is not None: - os.environ["ASIMTOOLS_CALC_INPUT"] = old_calc_input_var - else: - if os.getenv("ASIMTOOLS_CALC_INPUT", None) is not None: - del os.environ["ASIMTOOLS_CALC_INPUT"] - - # Get job IDs from internally run scripts if any - job_ids = results.get('job_ids', False) - # Otherwise get job_ids from wherever this script is running - if not job_ids: - job_ids = os.getenv('SLURM_JOB_ID') - results['job_ids'] = job_ids - job.update_output(results) - job.complete() - - postcommands = sim_input.get('postcommands', []) - for postcommand in postcommands: - command = postcommand.split() - completed_process = subprocess.run(command, check=True) - - logger.debug('Complete')
- -if __name__ == "__main__": - main(sys.argv[1:]) -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/scripts/atom_relax.html b/build/_modules/asimtools/scripts/atom_relax.html deleted file mode 100644 index 292d109..0000000 --- a/build/_modules/asimtools/scripts/atom_relax.html +++ /dev/null @@ -1,177 +0,0 @@ - - - - - - asimtools.scripts.atom_relax — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for asimtools.scripts.atom_relax

-#!/usr/bin/env python
-'''
-Relaxes the given atomic structure using ASE's built-in structure
-optimizers
-'''
-
-from typing import Dict, Tuple
-import numpy as np
-import ase.optimize
-from ase.io.trajectory import Trajectory
-from asimtools.calculators import load_calc
-from asimtools.utils import get_atoms, get_logger
-
-
[docs]def atom_relax( - calc_id: str, - image: Dict, - optimizer: str = 'GPMin', #GPMin is fastest according to ASE docs - properties: Tuple[str] = ('energy', 'forces'), - fmax: float = 0.02, -) -> Dict: - """Relaxes the given tomic structure using ASE's built-in structure - optimizers - - :param calc_id: Calculator ID - :type calc_id: str - :param image: image configuration - :type image: Dict - :param prefix: Prefix of output files, defaults to '' - :type prefix: str, optional - :param optimizer: ASE Optimizer class, defaults to 'GPMin' - :type optimizer: str, optional - :param fmax: Force convergence threshold in optimizer, defaults to 0.02 - :type fmax: float, optional - :return: Dictionary of results - :rtype: Dict - """ - calc = load_calc(calc_id) - atoms = get_atoms(**image) - atoms.set_calculator(calc) - logger = get_logger() - - traj_file = 'atom_relax.traj' - dyn = getattr(ase.optimize, optimizer)(atoms) - traj = Trajectory( - traj_file, - 'w', - atoms, - properties=properties - ) - dyn.attach(traj) - try: - dyn.run(fmax=fmax) - except Exception: - logger.error('Failed to relax atoms') - raise - - image_file = 'image_output.xyz' - atoms.write(image_file, format='extxyz') - - energy = float(atoms.get_potential_energy()) - final_fmax = float(np.sqrt((atoms.get_forces() ** 2).sum(axis=1).max())) - - results = { - 'energy': energy, - 'final_fmax': final_fmax, - 'files':{ - 'image': image_file, - 'traj': traj_file, - } - } - return results
-
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/scripts/calc_array.html b/build/_modules/asimtools/scripts/calc_array.html deleted file mode 100644 index c1eb6ab..0000000 --- a/build/_modules/asimtools/scripts/calc_array.html +++ /dev/null @@ -1,197 +0,0 @@ - - - - - - asimtools.scripts.calc_array — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for asimtools.scripts.calc_array

-#!/usr/bin/env python
-'''
-Apply the same script using multiple calculators
-
-Author: mkphuthi@github.com
-
-'''
-
-from typing import Dict, Sequence, Optional, Union
-from copy import deepcopy
-from asimtools.job import DistributedJob
-from asimtools.utils import get_calc_input, change_dict_value
-
-
[docs]def calc_array( - subscript_input: Dict, - calc_ids: Sequence[str] = None, - template_calc_id: Optional[str] = None, - key_sequence: Optional[Sequence[str]] = None, - array_values: Optional[Sequence] = None, - env_ids: Optional[Union[Sequence[str],str]] = None, - calc_input: Optional[Dict] = None, - env_input: Optional[Dict] = None, - ids: Sequence = None, -) -> Dict: - """Apply the same script using different calculators and if necessary - different environments - - :param calc_ids: Iterable with calc_ids, defaults to None - :type calc_ids: Sequence, optional - :param calc_input: Dictionary of calculator inputs - :type calc_input: Dictionary, optional - :param ids: Iterable with custom ids for each calc, defaults to None - :type ids: Sequence, optional - :return: Dictionary of results - :rtype: Dict - """ - using_array_values = key_sequence is not None\ - and template_calc_id is not None\ - and array_values is not None - assert calc_ids is not None or using_array_values, \ - 'Specify either a sequence of calc_ids or all of key_sequence, \ - template_calc_id and array_values to iterate over' - - if using_array_values: - if calc_input is None: - calc_input = get_calc_input() - calc_params = calc_input[template_calc_id] - new_calc_input = {} - if ids is None: - ids = [f'{key_sequence[-1]}-{val}' for val in array_values] - for i, val in enumerate(array_values): - new_calc_params = change_dict_value( - d=calc_params, - new_value=val, - key_sequence=key_sequence, - return_copy=True, - ) - new_calc_input[ids[i]] = new_calc_params - calc_input = new_calc_input - calc_ids = ids - elif calc_ids is not None: - if ids is None: - ids = calc_ids - - assert len(ids) == len(calc_ids), \ - 'Num. of calc_ids or array_values must match num. of ids' - - if env_ids is not None: - assert len(env_ids) == len(calc_ids) or isinstance(env_ids, str), \ - 'Provide one env_id or as many as there are calc_ids/array_values' - - array_sim_input = {} - - # Make individual sim_inputs for each calc - for i, calc_id in enumerate(calc_ids): - new_subscript_input = deepcopy(subscript_input) - new_subscript_input['args']['calc_id'] = calc_id - array_sim_input[f'{ids[i]}'] = new_subscript_input - - if env_ids is not None: - if isinstance(env_ids, str): - new_subscript_input['env_id'] = env_ids - else: - new_subscript_input['env_id'] = env_ids[i] - - # Create a distributed job object - djob = DistributedJob(array_sim_input, env_input, calc_input) - job_ids = djob.submit() - - results = {'job_ids': job_ids} - return results
-
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/scripts/cell_relax.html b/build/_modules/asimtools/scripts/cell_relax.html deleted file mode 100644 index bcb53f0..0000000 --- a/build/_modules/asimtools/scripts/cell_relax.html +++ /dev/null @@ -1,184 +0,0 @@ - - - - - - asimtools.scripts.cell_relax — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for asimtools.scripts.cell_relax

-#!/usr/bin/env python
-'''
-Relaxes structure using ASE
-
-Author: mkphuthi@github.com
-'''
-
-#pylint: disable=unused-argument
-#pylint: disable=too-many-locals
-#pylint: disable=too-many-arguments
-
-from typing import Dict, Optional, Sequence
-import ase.optimize
-from ase.constraints import StrainFilter
-from ase.io.trajectory import Trajectory
-from asimtools.calculators import load_calc
-from asimtools.utils import get_atoms, join_names
-
-
[docs]def cell_relax( - calc_id: str, - image: Dict, - prefix: str = '', - optimizer: str = 'BFGS', - smax: float = 0.002, - mask: Optional[Sequence] = None, -) -> Dict: - """Relax cell using ASE Optimizer - - :param calc_id: Key for calculator provided in calc_input - :type calc_id: str - :param image: Image parameters, see :func: asimtools.utils.get_atoms - :type image: Dict - :param prefix: Prefix to output files, defaults to '' - :type prefix: str, optional - :param optimizer: Optimizer class to use from ase.optimize, defaults to 'BFGS' - :type optimizer: str, optional - :param smax: Maximum stress in eV/$\AA^3$, defaults to 0.002 - :type smax: float, optional - :param mask: Mask to constrain cell deformation while relaxing, defaults to None - :type mask: Optional[Sequence], optional - :return: Dictionary of results including, final energy, stress and output files - :rtype: Dict - """ - calc = load_calc(calc_id) - atoms = get_atoms(**image) - atoms.set_calculator(calc) - - traj_file = join_names([prefix, 'cell_relax.traj']) - sf = StrainFilter(atoms, mask=mask) - dyn = getattr(ase.optimize, optimizer)(sf) - traj = Trajectory( - traj_file, - 'w', - atoms, - properties=['energy', 'forces', 'stress'], - ) - dyn.attach(traj) - try: - dyn.run(fmax=smax) - except Exception: - print('Failed to optimize atoms') - raise - - image_file = join_names([prefix, 'image_output.xyz']) - atoms.write(image_file, format='extxyz') - - energy = float(atoms.get_potential_energy()) - final_smax = float(atoms.get_stress().max()) - - results = { - 'energy': energy, - 'final_smax': final_smax, - 'files':{ - 'image': image_file, - 'traj': traj_file, - } - } - return results
-
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/scripts/chained.html b/build/_modules/asimtools/scripts/chained.html deleted file mode 100644 index d7a6171..0000000 --- a/build/_modules/asimtools/scripts/chained.html +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - asimtools.scripts.chained — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for asimtools.scripts.chained

-#!/usr/bin/env python
-'''
-Submits a chain of scripts in the order specified by steps
-in input
-
-Author: mkphuthi@github.com
-
-'''
-
-from typing import Dict, Optional
-from asimtools.job import ChainedJob
-
-
-
[docs]def chained( - steps: Dict, - env_input: Optional[Dict] = None, - calc_input: Optional[Dict] = None, -) -> Dict: - """Run a workflow using the provided sim_input.yaml one after the other, - The jobs will run one after the other, if slurm is used, job dependencies - will be used. Note that if one of the subscripts internally launches another - script e.g. image_array, calc_array etc, slurm job dependencies will not - work as expected. In those cases, we recommend running the steps one by one - or interactively - - - :param steps: Dictionary where each key is an ID and the value is a \ - standard sim_input - :type steps: Dict - :param env_input: env_input that overrides global file, defaults to None - :type env_input: Optional[Dict], optional - :param calc_input: calc_input that overrides global file, defaults to None - :type calc_input: Optional[Dict], optional - :return: Dictionary of results - :rtype: Dict - """ - cjob = ChainedJob( - sim_input=steps, env_input=env_input, calc_input=calc_input - ) - job_ids = cjob.submit() - - results = cjob.unitjobs[-1].get_output() - results.update({'job_ids': job_ids}) - return results
-
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/scripts/distributed.html b/build/_modules/asimtools/scripts/distributed.html deleted file mode 100644 index 297f0bb..0000000 --- a/build/_modules/asimtools/scripts/distributed.html +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - asimtools.scripts.distributed — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for asimtools.scripts.distributed

-#!/usr/bin/env python
-'''
-Distributes a number of simulations at the same time if possible
-
-Author: mkphuthi@github.com
-
-'''
-
-from typing import Dict, Optional
-from asimtools.job import DistributedJob
-
-
[docs]def distributed( - subscripts: Dict, - env_input: Optional[Dict] = None, - calc_input: Optional[Dict] = None, - array_max: Optional[int] = None, -) -> Dict: - """Distributes a set of jobs based on inpout parameter. The scnarios are - as follows: - #. use_slurm: False, interactive: True -> Runs one after the other. - #. use_slurm: True, interactive: False, same env_id -> Launches a job \ - # array. - #. use_slurm: False, interactive: False, different env_ids -> Launches \ - # multiple jobs. - - - :param subscripts: Dictionary of scripts, each key is an ID and each \ - value is a sim_input file - :type subscripts: Dict - :param env_input: env_input that overrides global file, defaults to None - :type env_input: Optional[Dict], optional - :param calc_input: calc_input that overrides global file, defaults to None - :type calc_input: Optional[Dict], optional - :param array_max: Maximum number of jobs to run in array, defaults to None - :type array_max: Optional[int], optional - :return: Dictionary of results - :rtype: Dict - """ - djob = DistributedJob( - sim_input=subscripts, - env_input=env_input, - calc_input=calc_input - ) - job_ids = djob.submit(array_max=array_max) - - results = {'job_ids': job_ids} - return results
-
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/scripts/do_nothing.html b/build/_modules/asimtools/scripts/do_nothing.html deleted file mode 100644 index d2680c4..0000000 --- a/build/_modules/asimtools/scripts/do_nothing.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - asimtools.scripts.do_nothing — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for asimtools.scripts.do_nothing

-#!/usr/bin/env python
-'''
-Script for unit tests and debugging, does nothing for specified duration
-'''
-from time import sleep
-from typing import Dict, Optional
-
-
[docs]def do_nothing(duration: Optional[int] = 5) -> Dict: - """Sleep for the specified duration - - :param duration: time in seconds, defaults to 60 - :type duration: int, optional - :return: Dictionary with duration slept for - :rtype: Dict - """ - sleep(duration) - results = {'duration': duration} - return results
-
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/scripts/env_array.html b/build/_modules/asimtools/scripts/env_array.html deleted file mode 100644 index adb9f4a..0000000 --- a/build/_modules/asimtools/scripts/env_array.html +++ /dev/null @@ -1,164 +0,0 @@ - - - - - - asimtools.scripts.env_array — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for asimtools.scripts.env_array

-#!/usr/bin/env python
-'''
-Apply the same script using different envs
-
-Author: mkphuthi@github.com
-
-'''
-
-from typing import Dict, Sequence, Optional
-from copy import deepcopy
-from asimtools.job import DistributedJob
-
-
[docs]def env_array( - subscript_input: Dict, - env_ids: Sequence[str], - calc_input: Optional[Dict] = None, - env_input: Optional[Dict] = None, - ids: Sequence = None, -) -> Dict: - """Run the same script using multiple env_ids - - :param subscript_input: sim_input for the script to be run - :type subscript_input: Dict - :param env_ids: Sequence of env_ids to iterate over - :type env_ids: Sequence[str] - :param calc_input: calc_input to override global file, defaults to None - :type calc_input: Optional[Dict], optional - :param env_input: env_input defaults to overide global file, defaults to\ - None - :type env_input: Optional[Dict], optional - :param ids: Sequence of custom ids for each env_id, defaults to None - :type ids: Sequence, optional - :return: Dictionary of results - :rtype: Dict - """ - array_sim_input = {} - - # Allow user to customize subdirectory names if needed - if ids is None: - ids = [str(i) for i in range(len(env_ids))] - else: - assert len(ids) == len(env_ids), \ - 'Num. of env_ids must match num. of ids' - - # Make individual sim_inputs for each env - for i, env_id in enumerate(env_ids): - new_subscript_input = deepcopy(subscript_input) - new_subscript_input['env_id'] = env_id - array_sim_input[f'{ids[i]}'] = new_subscript_input - - # Create a distributed job object - djob = DistributedJob(array_sim_input, env_input, calc_input) - job_ids = djob.submit() - - results = { - 'job_ids': job_ids, - } - return results
-
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/scripts/eos/eos.html b/build/_modules/asimtools/scripts/eos/eos.html deleted file mode 100644 index 3482c8e..0000000 --- a/build/_modules/asimtools/scripts/eos/eos.html +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - asimtools.scripts.eos.eos — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for asimtools.scripts.eos.eos

-'''.Xauthority'''
-
-from typing import Dict, Tuple
-from asimtools.job import ChainedJob
-
-
[docs]def eos( - image: dict, - singlepoint_env_id: str, - calc_id: str, - preprocess_env_id: str, - nimages: int = 5, - scale_range: Tuple[float] = (0.95, 1.05), - postprocess: bool = True, -) -> Dict: - ''' EOS calculation ''' - - sim_input = { - 'step-0': { - 'script': 'eos.preprocess', - 'env_id': preprocess_env_id, - 'args': { - 'image': image, - 'nimages': nimages, - 'scale_range': scale_range, - } - }, - 'step-1': { - 'script': 'image_array', - 'env_id': preprocess_env_id, - 'args': { - 'images': {'image_file': 'step-0/preprocess_images_output.xyz'}, - 'subscript_input': { - 'script': 'singlepoint', - 'env_id': singlepoint_env_id, - 'args': { - 'calc_id': calc_id, - 'properties': ['energy'], - } - } - } - }, - 'step-2': { - 'script': 'eos.postprocess', - 'env_id': preprocess_env_id, - 'submit': postprocess, #Fails if previous step is in a slurm queue - 'args': { - 'images': {'pattern': '../step-1/id-*/image_output.xyz'}, - } - } - } - - chain = ChainedJob(sim_input, env_input=None, calc_input=None) - chain.submit() - return chain.get_last_output()
-
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/scripts/eos/postprocess.html b/build/_modules/asimtools/scripts/eos/postprocess.html deleted file mode 100644 index 1fde9e8..0000000 --- a/build/_modules/asimtools/scripts/eos/postprocess.html +++ /dev/null @@ -1,169 +0,0 @@ - - - - - - asimtools.scripts.eos.postprocess — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for asimtools.scripts.eos.postprocess

-'''.Xauthority'''
-
-from typing import Dict, Tuple
-import numpy as np
-import matplotlib.pyplot as plt
-from ase.eos import EquationOfState
-from ase.io import read
-from ase. units import GPa
-# from asimtools.job import load_output_images, load_input_images
-from asimtools.utils import (
-    write_csv_from_dict,
-    get_images,
-)
-
-
[docs]def postprocess( - images: Dict = None, -) -> Tuple[None,Dict]: - ''' plot things ''' - images = get_images(**images) - volumes = np.array([at.get_volume() for at in images]) - energies = np.array([at.get_potential_energy() for at in images]) - write_csv_from_dict( - 'eos_output.csv', - {'volumes': volumes, 'energies': energies} - ) - eos_fit = EquationOfState(volumes, energies) - - try: - v0, e0, B = eos_fit.fit() - except ValueError: - print('ERROR: Could not fit EOS, check structures') - v0, e0, B = None, None, None - - fig, ax = plt.subplots() - if v0 is not None: - B_GPa = B / GPa # eV/Ang^3 to GPa - eos_fit.plot(ax=ax) - - # Get equilibrium lattice scaling by interpolation - xs = [atoms.info['scale'] for atoms in images] - vs = [] - atoms = read('../step-0/input_image.xyz') - for x in xs: - samp_atoms = atoms.copy() - samp_atoms.cell = samp_atoms.cell*x - vs.append(samp_atoms.get_volume()) - x0 = np.interp(v0, vs, xs) - - results = { - 'equilibrium_volume': float(v0), - 'equilibrium_energy': float(e0), - 'equilibrium_scale': float(x0), - 'bulk_modulus': float(B_GPa), - 'bulk_modulus_unit': 'GPa', - } - else: - ax.plot(volumes, energies) - results = {} - ax.set_xlabel(r'Volume ($\AA$)') - ax.set_ylabel(r'Energy (eV)') - plt.savefig('eos.png') - plt.close(fig) - return results
-
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/scripts/eos/preprocess.html b/build/_modules/asimtools/scripts/eos/preprocess.html deleted file mode 100644 index 9ed44de..0000000 --- a/build/_modules/asimtools/scripts/eos/preprocess.html +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - asimtools.scripts.eos.preprocess — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for asimtools.scripts.eos.preprocess

-'''.Xauthority'''
-
-from typing import Dict, Tuple
-import numpy as np
-from ase.io import write
-from asimtools.utils import (
-    get_atoms,
-)
-
-
[docs]def preprocess( - image: Dict, - nimages: int = 5, - scale_range: Tuple[float,float] = (0.95, 1.05), -) -> Dict: - ''' Prepares structures for EOS calculation ''' - - assert scale_range[0] < scale_range[1], 'x_scale[0] should be smaller than\ - x_scale[1]' - - atoms = get_atoms(**image) - - # Make a database of structures with the volumes scaled appropriately - scales = np.linspace(scale_range[0], scale_range[1], nimages) - scaled_images = [] - for scale in scales: - new_cell = atoms.get_cell() * scale - new_atoms = atoms.copy() - new_atoms.set_cell(new_cell) - new_atoms.info['scale'] = f'{scale:.2f}' - scaled_images.append(new_atoms) - - scaled_images_file = 'preprocess_images_output.xyz' - write(scaled_images_file, scaled_images, format='extxyz') - - return {'files': {'images': scaled_images_file}}
-
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/scripts/image_array.html b/build/_modules/asimtools/scripts/image_array.html deleted file mode 100644 index dfde927..0000000 --- a/build/_modules/asimtools/scripts/image_array.html +++ /dev/null @@ -1,181 +0,0 @@ - - - - - - asimtools.scripts.image_array — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for asimtools.scripts.image_array

-#!/usr/bin/env python
-'''
-Apply the same script on multiple images
-
-Author: mkphuthi@github.com
-
-'''
-
-from typing import Dict, Sequence, Optional
-from copy import deepcopy
-from asimtools.job import DistributedJob
-from asimtools.utils import get_images
-
-
[docs]def image_array( - images: Dict, - subscript_input: Dict, - calc_input: Optional[Dict] = None, - env_input: Optional[Dict] = None, - ids: Sequence[str] = None, - env_ids: Sequence[str] = None, - array_max: Optional[int] = None, -) -> Dict: - """Submit the same script on multiple images and if specified, use - different env_ids - - :param images: Image config, see :func:`asimtools.utils.get_atoms` - :type images: Dict - :param subscript_input: sim_input of script to be run - :type subscript_input: Dict - :param calc_input: calc_input to override global file, defaults to None - :type calc_input: Optional[Dict], optional - :param env_input: env_input to override global file, defaults to None - :type env_input: Optional[Dict], optional - :param ids: Custom ids for each image, defaults to None - :type ids: Sequence[str], optional - :param env_ids: Sequence of envs for each image, must be the same length \ - as the total number of images, defaults to None - :type env_ids: Sequence[str], optional - :param array_max: Maximum jobs to run simultanteously in job array, \ - defaults to None - :type array_max: Optional[int], optional - :return: Dictionary of results - :rtype: Dict - """ - images = get_images(**images) - array_sim_input = {} - - # Allow user to customize subdirectory names if needed - if ids is None: - ids = [str(i) for i in range(len(images))] - else: - assert len(ids) == len(images), \ - 'Num. of images must match num. of ids' - - if env_ids is not None: - assert len(env_ids) == len(images), \ - f'Num. images ({len(images)}) must match env_ids ({len(env_ids)})' - - # Make individual sim_inputs for each image - for i, image in enumerate(images): - new_subscript_input = deepcopy(subscript_input) - new_subscript_input['args']['image'] = { - 'atoms': image - } - array_sim_input[f'{ids[i]}'] = new_subscript_input - - if env_ids is not None: - new_subscript_input['env_id'] = env_ids[i] - - # Create a distributed job object - djob = DistributedJob(array_sim_input, env_input, calc_input) - job_ids = djob.submit(array_max=array_max) - - results = {'job_ids': job_ids} - return results
-
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/scripts/lammps.html b/build/_modules/asimtools/scripts/lammps.html deleted file mode 100644 index 75d5e7a..0000000 --- a/build/_modules/asimtools/scripts/lammps.html +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - asimtools.scripts.lammps — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for asimtools.scripts.lammps

-#!/usr/bin/env python
-'''
-Runs a user defined lammps script or template 
-
-Author: mkphuthi@github.com
-'''
-from typing import Dict
-from pathlib import Path
-import subprocess
-from asimtools.utils import (
-    get_atoms,
-    join_names,
-)
-
-
[docs]def lammps( - template: str, - image: Dict = None, - prefix: str = '', - atom_style: str = 'atomic', - variables: Dict = None, - lmp_cmd: str = 'lmp', -) -> Dict: - ''' - Runs a lammps simulation based on a template lammps input script - ''' - if variables is None: - variables = {} - - # Make sure the provided script follows standard for reading - # in arbitrary image provided by asimtools - if image is not None: - atoms = get_atoms(**image) - atoms.write( - 'image_input.lmpdat', - format='lammps-data', - atom_style=atom_style, - masses=True, - ) - variables['IMAGE_FILE'] = 'image_input.lmpdat' - - lmp_txt = '' - for variable, value in variables.items(): - lmp_txt += f'variable {variable} equal {value}\n' - - lmp_txt += '\n' - template = Path(template).resolve() - with open(template, 'r', encoding='utf-8') as f: - lines = f.readlines() - - for line in lines: - lmp_txt += line - - if image is not None: - assert 'read_data ' in lmp_txt, \ - 'Make sure "read_data image_input.lmpdat" command is used \ - (with correct atom style) appropriately in lammps input \ - script if you specify image keyword' - - lmp_inp_file = join_names([prefix, 'input.lammps']) - with open(lmp_inp_file, 'w', encoding='utf-8') as f: - f.write(lmp_txt) - - command = lmp_cmd + f' -i {lmp_inp_file}' - command = command.split(' ') - completed_process = subprocess.run( - command, check=False, capture_output=True, text=True, - ) - - with open('lmp_stdout.txt', 'w', encoding='utf-8') as f: - f.write(completed_process.stdout) - - if completed_process.returncode != 0: - err_txt = f'Failed to run {lmp_inp_file}\n' - err_txt += 'See lmp.stderr.txt for details.' - print(err_txt) - with open('lmp_stderr.txt', 'w', encoding='utf-8') as f: - f.write(completed_process.stderr) - completed_process.check_returncode() - return None - - results = {'files': { - 'log': 'log.lammps', - 'thermo': 'log.lammps', - }} - - return results
-
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/scripts/parity.html b/build/_modules/asimtools/scripts/parity.html deleted file mode 100644 index 1f3d578..0000000 --- a/build/_modules/asimtools/scripts/parity.html +++ /dev/null @@ -1,329 +0,0 @@ - - - - - - asimtools.scripts.parity — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for asimtools.scripts.parity

-#!/usr/bin/env python
-'''
-Generates a parity plot and collects evaluation statistics comparing energy
-and/or forces and/or stress to existing values in the provided dataset. This
-script can work in parallel based on the number of cores specified.
-
-Author: mkphuthi@github.com
-
-'''
-from typing import Dict, List, TypeVar, Sequence
-from functools import partial
-from multiprocessing import Pool
-import numpy as np
-import matplotlib.pyplot as plt
-from asimtools.calculators import load_calc
-from asimtools.utils import (
-    write_csv_from_dict,
-    get_images,
-    parse_slice,
-    get_axis_lims,
-)
-
-Calculator = TypeVar('Calculator')
-
[docs]def calc_parity_data( - subset: List, - calc_id: str, - properties: Sequence = ('energy', 'forces', 'stress'), - force_prob: float = 1.0, -) -> Dict: - """Calculates parity data for each atoms instance in subset - - :param subset: List of atoms instances - :type subset: List - :param calc: ASE Cailculator instance - :type calc: Calculator - :param properties: Properties to evaluate, choose from "energy", \ - "forces" and "stress", defaults to ('energy', 'forces', 'stress') - :type properties: List, optional - :param force_prob: Fraction of forces to consider, may speed up sampling \ - large structures by subsampling forces randomly, defaults to 1.0 - :type force_prob: float, optional - :return: Dictionary with reference and predicted values for each \ - property - :rtype: Dict - """ - - res = {prop: [] for prop in properties} - ervals = [] - epvals = [] - frvals = [] - fpvals = [] - srvals = [] - spvals = [] - for i, atoms in enumerate(subset): - calc = load_calc(calc_id) - n_atoms = len(atoms) - if 'energy' in properties: - prop = 'energy' - ervals = np.hstack( - [ervals, atoms.get_potential_energy()/n_atoms] - ) - epvals = np.hstack( - [epvals, float(calc.get_potential_energy(atoms)/n_atoms)] - ) - - if 'forces' in properties: - if np.random.rand() < force_prob: - prop = 'forces' - frvals = np.hstack( - [frvals, np.array(atoms.get_forces()).flatten()] - ) - - fpvals = np.hstack( - [fpvals, np.array(calc.get_forces(atoms)).flatten()] - ) - - if 'stress' in properties: - prop = 'stress' - srvals = np.hstack( - [srvals, np.array(atoms.get_stress()).flatten()] - ) - - spvals = np.hstack( - [spvals, np.array(calc.get_stress(atoms)).flatten()] - ) - res[prop] = {'ref': srvals, 'pred': spvals} - - if i % 20 == 0: - print(f'Progress {i}/{len(subset)}') - - res['energy'] = {'ref': ervals, 'pred': epvals} - res['forces'] = {'ref': frvals, 'pred': fpvals} - res['stress'] = {'ref': srvals, 'pred': spvals} - return res
- -def _split_data(data: Sequence, chunks: int) -> List: - """Splits data into equal chunks and tosses the remainder - - :param data: Sequence of datapoints - :type data: Sequence - :param chunks: Number of chunks to split the data into - :type nprocs: int - :return: List of split datasets - :rtype: List - """ - subsets = [] - subset_len = int(np.ceil(len(data) // chunks)) - for subset_id in range(chunks): - start = subset_id * subset_len - stop = (subset_id + 1) * subset_len - subsets.append(data[start:stop]) - - return subsets - -
[docs]def rmse(yhat: Sequence, y: Sequence) -> float: - """Calculate Root Mean Square Error between to sequences - - :param yhat: Predicted values - :type yhat: Sequence - :param y: True values - :type y: Sequence - :return: RMSE - :rtype: float - """ - return np.sqrt(np.sum(np.square(yhat - y)) / len(y))
- -
[docs]def parity( - images: Dict, - calc_id: str, - force_prob: float = 1.0, - nprocs: int = 1, - unit: str = 'meV', - index: str = ':', - properties: Sequence = ('energy', 'forces', 'stress'), -) -> Dict: - """Generates a parity plot and collects evaluation statistics comparing energy - and/or forces and/or stress to existing values in the provided dataset - - :param images: Image config, see :func:`asimtools.utils.get_atoms` - :type images: Dict - :param calc_id: ID of calculator provided in calc_input or global file - :type calc_id: str - :param force_prob: Fraction of forces to consider in force parity, \ - can be used for speeding up large structures by only subsampling\ - randomly, defaults to 1.0 - :type force_prob: float, optional - :param nprocs: Number of process to parallelize over, must be less than \ - number of datapoints, defaults to 1 - :type nprocs: int, optional - :param unit: Unit to plot, choose from meV, eV or kcal/mol, \ - defaults to 'meV' - :type unit: str, optional - :param index: index of data to use from full dataset, defaults to ':' - :type index: int, optional - :param properties: Properties to evaluate, choose from "energy",\ - "forces" and "stress", defaults to ('energy', 'forces', 'stress') - :type properties: Sequence, optional - :return: Dictionary of results - :rtype: Dict - """ - - index = parse_slice(index) - data = get_images(**images)[index] - assert len(data) > nprocs, \ - f'Too little samples ({len(data)}) for {nprocs}' - - unit_factors = {'meV': 1000, 'eV': 1, 'kcal/mol': 23.0621} - unit_factor = unit_factors[unit] - units = { - 'energy': f'{unit}/atom', - 'forces': f'{unit}'+r'/$\AA$', - 'stress': f'{unit}'+r'/$\AA^3$', - } - - subsets = _split_data(data, nprocs) - reses = [] - with Pool(nprocs) as pool: - reses = pool.map(partial( - calc_parity_data, - calc_id=calc_id, - properties=properties, - force_prob=force_prob, - ), - subsets, - ) - - res = {prop: {'ref': [], 'pred': []} for prop in properties} - results = {} - for prop in properties: - for source in ('ref', 'pred'): - res[prop][source] = np.hstack( - [reses[jj][prop][source] for jj in range(len(reses))] - ) * unit_factor - - write_csv_from_dict( - f'{prop}_parity.csv', - res[prop], - header=f'Units: {unit}', - ) - - rmse_val = rmse(res[prop]['ref'], res[prop]['pred']) - results[f'{prop}_rmse'] = rmse_val - - _, ax = plt.subplots() - lims = np.array(get_axis_lims(res[prop]['ref'], res[prop]['pred'])) - - ax.plot(lims, lims, 'k-') - ax.plot( - res[prop]['ref'], - res[prop]['pred'], - 'ro', - label=f'{prop.capitalize()} RMSE={rmse_val:.3g}{units[prop]}', - ) - ax.set_xticks(np.round(np.linspace(*lims, 5), 1)) - ax.set_yticks(np.round(np.linspace(*lims, 5), 1)) - ax.set_aspect('equal', 'box') - ax.set_xlabel(f'Reference {prop.capitalize()} ({units[prop]})') - ax.set_ylabel(f'Predicted {prop.capitalize()} ({units[prop]})') - ax.legend() - plt.savefig(f'{prop}_parity.png') - - results['Energy unit'] = unit - return results
-
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/scripts/sim_array.html b/build/_modules/asimtools/scripts/sim_array.html deleted file mode 100644 index 5955fe4..0000000 --- a/build/_modules/asimtools/scripts/sim_array.html +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - asimtools.scripts.sim_array — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for asimtools.scripts.sim_array

-#!/usr/bin/env python
-'''
-Runs the same script, iterating over multiple values of a specified argument
-based on a sim_input template provided by the user
-
-Author: mkphuthi@github.com
-
-'''
-
-from typing import Dict, Sequence, Optional, Union
-from asimtools.job import DistributedJob
-from asimtools.utils import change_dict_value
-
-
[docs]def sim_array( - template_sim_input: Dict, - key_sequence: Optional[Sequence[str]] = None, - array_values: Optional[Sequence] = None, - env_ids: Optional[Union[Sequence[str],str]] = None, - calc_input: Optional[Dict] = None, - env_input: Optional[Dict] = None, - ids: Sequence = None, -) -> Dict: - """Runs the same script, iterating over multiple values of a specified - argument based on a sim_input template provided by the user - - :param template_sim_input: sim_input containing all the default parameters - :type template_sim_input: Dict - :param key_sequence: Sequence of keys to access value to be iterated over, defaults to None - :type key_sequence: Optional[Sequence[str]], optional - :param array_values: values to be iterated over in each simulation, defaults to None - :type array_values: Optional[Sequence], optional - :param env_ids: Environment(s) to be used for each simulation, must either be a list with as many env_ids as array values or a string with the env_id to be used by all simulations, defaults to None - :type env_ids: Optional[Union[Sequence[str],str]], optional - :param calc_input: calc_input file to use, defaults to None - :type calc_input: Optional[Dict], optional - :param env_input: env_input to use, defaults to None - :type env_input: Optional[Dict], optional - :param ids: Custom ids to use for each simulation, defaults to None - :type ids: Sequence, optional - :return: Results - :rtype: Dict - """ - if ids is None: - ids = [f'{key_sequence[-1]}-{val}' for val in array_values] - - assert len(ids) == len(array_values), \ - 'Num. of array_values must match num. of ids' - - if env_ids is None: - pass - elif isinstance(env_ids, str): - env_ids = [env_ids for i in range(len(ids))] - else: - assert len(env_ids) == len(ids) or isinstance(env_ids, str), \ - 'Provide one env_id or as many as there are array_values' - - # Create sim_inputs for each array value and to create input for - # a DistributedJob object - sim_inputs = {} - for i, val in enumerate(array_values): - new_sim_input = change_dict_value( - d=template_sim_input, - new_value=val, - key_sequence=key_sequence, - return_copy=True, - ) - if env_ids is not None: - new_sim_input['env_id'] = env_ids[i] - sim_inputs[ids[i]] = new_sim_input - - # Create a distributed job object - djob = DistributedJob(sim_inputs, env_input, calc_input) - job_ids = djob.submit() - - results = {'job_ids': job_ids} - return results
-
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/scripts/singlepoint.html b/build/_modules/asimtools/scripts/singlepoint.html deleted file mode 100644 index 661911a..0000000 --- a/build/_modules/asimtools/scripts/singlepoint.html +++ /dev/null @@ -1,174 +0,0 @@ - - - - - - asimtools.scripts.singlepoint — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for asimtools.scripts.singlepoint

-#!/usr/bin/env python
-'''
-Calculates single point energy 
-
-Author: mkphuthi@github.com
-'''
-
-from typing import Tuple, Dict
-import logging
-from asimtools.calculators import load_calc
-# from asimtools.job import uses_calc
-from asimtools.utils import (
-    get_atoms,
-)
-
-
[docs]def singlepoint( - calc_id: str, - image: Dict, - properties: Tuple[str] = ('energy', 'forces'), -) -> Dict: - """Evaluates the properties of a single image, currently implemented - properties are energy, forces and stress - - :param calc_id: ID of calculator provided in calc_input or global file - :type calc_id: str - :param image: Image config, see :func:`asimtools.utils.get_atoms` - :type image: Dict - :param properties: properties to evaluate, defaults to ('energy', 'forces') - :type properties: Tuple[str], optional - :return: Dictionary of results - :rtype: Dict - """ - calc = load_calc(calc_id) - atoms = get_atoms(**image) - atoms.set_calculator(calc) - - if 'energy' in properties: - try: - energy = atoms.get_potential_energy() - logging.debug('Calculated energy') - except Exception: - logging.error('Failed to calculate energy') - raise - - if 'forces' in properties: - try: - atoms.get_forces() - logging.debug('Calculated forces') - except Exception: - logging.error('Failed to calculate forces') - raise - - if 'stress' in properties: - try: - atoms.get_stress() - logging.debug('Calculated stress') - except Exception: - logging.error('Failed to calculate stress') - raise - - image_file = 'image_output.xyz' - atoms.write(image_file, format='extxyz') - - results = { - 'energy': float(energy), - 'files': {'image': image_file} - } - return results
-
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/scripts/template.html b/build/_modules/asimtools/scripts/template.html deleted file mode 100644 index 859f338..0000000 --- a/build/_modules/asimtools/scripts/template.html +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - asimtools.scripts.template — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for asimtools.scripts.template

-#!/usr/bin/env python
-'''
-Describe the script briefly here. If this is a script that runs multiple steps,
-describe it here using reStructuredText to generate autodocs
-
-Cite the papers where the method/script was first introduced here as well
-
-Author: mkphuthi@github.com
-'''
-
-from typing import Dict 
-# from asimtools.calculators import load_calc
-from asimtools.utils import (
-    get_atoms,
-    # get_images,
-)
-
-
[docs]def template( - # calc_id: str, - # image: Dict, # Optional: if using a structure - # images: Dict, # Optional: if using multiple structures -) -> Dict: - ''' - Script does xyz specifically - ''' - - # calc = load_calc(calc_id) # Optional: Any code that loads a calculator is replaced by this one line! - # atoms = get_atoms(**image) # Optional - # images = get_images(**images) # Optional - # atoms.set_calculator(calc) # Optional - - ############################# - # Do some cool science here # - ############################# - - results = { - 'energy': 'Placeholder_for_my_result', - 'files': {'image': 'placeholder_four_image_output'} - } - return results # Always return a dictionary! Use {} if necessary
-
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/asimtools/utils.html b/build/_modules/asimtools/utils.html deleted file mode 100644 index 9497036..0000000 --- a/build/_modules/asimtools/utils.html +++ /dev/null @@ -1,515 +0,0 @@ - - - - - - asimtools.utils — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for asimtools.utils

-'''
-Utilities and helper functions for reading and writing data using set
-standards
-'''
-from typing import (
-    TypeVar, Iterable, Tuple, Union, List, Dict, Sequence, Optional
-)
-from copy import deepcopy
-from glob import glob
-import os
-import logging
-import subprocess
-from pathlib import Path
-import yaml
-import numpy as np
-import pandas as pd
-from ase.io import read
-import ase.db
-import ase.build
-
-Atoms = TypeVar('Atoms')
-
-
[docs]def read_yaml(yaml_path: str) -> Dict: - """Read a yaml file - - :param yaml_path: Path to yaml file - :type yaml_path: str - :return: Dictionary - :rtype: Dict - """ - with open(yaml_path, 'r', encoding='utf-8') as f: - output = yaml.safe_load(f) - return output
- -
[docs]def write_yaml(yaml_path: str, yaml_Dict: Dict) -> None: - """Write a dictionary to a yaml file - - :param yaml_path: Path to write yaml to - :type yaml_path: str - :param yaml_Dict: Dictionary to write - :type yaml_Dict: Dict - """ - with open(yaml_path, 'w', encoding='utf-8') as f: - yaml.dump(yaml_Dict, f)
- -
[docs]def get_axis_lims(x: Sequence, y: Sequence, padding: float=0.1): - """Get an estimate of good limits for a plot axis""" - data_min = np.min([x, y]) - data_max = np.max([x, y]) - diff = data_max - data_min - lims = [data_min - padding * diff, data_max + padding * diff] - return lims
- -
[docs]def write_csv_from_dict( - fname: str, - data: Dict, - columns: Optional[Sequence] = None, - header: str = '', - **kwargs -) -> pd.DataFrame: - """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 - :func:`pandas.Dataframe.to_csv()` - - :param fname: File name to be return to - :type fname: str - :param data: Dictionary to write - :type data: Dict - :param columns: Keys to use as columns of csv, defaults to None - :type columns: Iterable, optional - :return: Pandas dataframe of results - :rtype: pd.DataFrame - """ - if columns is None: - columns = list(data.keys()) - - csv_data = pd.DataFrame(data, columns=columns, **kwargs) - with open(fname, 'w', encoding='utf-8') as f: - f.write('#' + header + '\n') - - csv_data.to_csv(fname, index=False, header=True, mode='a') - return csv_data
- -
[docs]def strip_symbols(substr: str) -> str: - """Helper function to format filenames using standard - - :param substr: substring - :type substr: str - :return: stripped string - :rtype: str - """ - bad_symbols = ['_', '-', '.', ' '] - if len(substr) == 0: - return '' - if substr[0] in bad_symbols or substr[-1] in bad_symbols: - for bad_symbol in bad_symbols: - substr = substr.strip(bad_symbol) - return strip_symbols(substr) - return substr
- -
[docs]def join_names(substrs: Sequence[str]) -> str: - """Join multiple strings using a hyphen neatly - - :param substrs: Sequence of strings to merge - :type substrs: Sequence[str] - :return: Joined strings - :rtype: str - """ - name = '' - new_substrs = [] - for substr in substrs: - new_substrs.append(strip_symbols(substr)) - - final_substrs = [] - for substr in new_substrs: - if len(substr) > 0: - final_substrs.append(substr) - name = '_'.join(final_substrs) - return name
- -
[docs]def get_atoms( - image_file: str = None, - builder: str = 'bulk', - atoms: Atoms = None, - repeat: Tuple[int, int, int] = None, - rattle_stdev: float = None, - **kwargs -) -> Atoms: - """Return an atoms object based on specified config. This is the - recommended way to load atoms objects. There are three options to specify: - #. image_file. - #. builder, kwargs. - #. atoms. - - :param image_file: Path to an ASE-readable image file, defaults to None - :type image_file: str, optional - :param builder: Builder to use from :mod:`ase.build`, defaults to 'bulk'. \ - Any extra keyword arguments specified are passed to the build - :type builder: str, optional - :param atoms: :class:`ase.Atoms` object, defaults to None - :type atoms: Atoms, optional - :param repeat: Number of times to repeat input to create supercell, \ - defaults to None - :type repeat: Tuple[int, int, int], optional - :param rattle_stdev: stdev to be provided to :meth:`ase.Atoms.rattle`, \ - defaults to None - :type rattle_stdev: float, optional - :return: One :class:`ase.Atoms` instance - :rtype: Atoms - """ - assert image_file is not None or \ - len(kwargs) > 0 or \ - atoms is not None, \ - "Specify atoms, image_file or use ase.build tools" - - if image_file is not None: - atoms = read(image_file, **kwargs) - elif atoms is None: - builder_func = getattr(ase.build, builder) - try: - atoms = builder_func(**kwargs) - except ValueError: - print('Make sure you choose a valid builder and appropriate\ - arguments matching the functions in ase.build. \n\ - See https://wiki.fysik.dtu.dk/ase/ase/build/build.html') - raise - else: - assert atoms is not None, 'Specify an input structure' - - if repeat is not None: - atoms = atoms.repeat(repeat) - - if rattle_stdev is not None: - atoms.rattle(stdev=rattle_stdev) - - return atoms
- -
[docs]def parse_slice(value: str) -> slice: - """Parses a :func:`slice` from string, like `start:stop:step`. - - :param value: Slice string - :type value: str - :return: slice object - :rtype: slice - """ - indices = str(value).split(':') - parts = [] - for index in indices: - if index == '': - parts.append(False) - else: - parts.append(index) - return slice(*[int(p) if p else None for p in parts])
- -
[docs]def get_images( - image_file: str = None, - pattern: str = None, - patterns: List[str] = None, - images: Iterable[Atoms] = None, - index: Union[str, int] = ':', - **kwargs -) -> List[Atoms]: - """Return a list of atoms objects based on the input arguments. Options \ - to specify are: - #. image_file - #. pattern - #. images - - :param image_file: Path to ASE-readable file with one or more images, \ - defaults to None - :type image_file: str, optional - :param pattern: String pattern of paths from which to search for images, \ - defaults to None. This only gets one image from each file as in \ - :func:`ase.io.read` without specifying an index - :type pattern: str, optional - :param 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 :func:`ase.io.read` without specifying an index - :type pattern: str, optional - :param images: A list of atoms objects, defaults to None - :type images: Iterable[Atoms], optional - :param index: Index to specify when using :func:`ase.io.read`, \ - defaults to ':' - :type index: Union[str, int], optional - :return: List of :class:`ase.Atoms` - :rtype: List[Atoms] - """ - assert (image_file is not None) or \ - (pattern is not None) or \ - (patterns is not None) or \ - (images is not None), \ - "Please specify file, pattern or iterable" - - if image_file is not None: - images = read(image_file, index=index, **kwargs) - elif pattern is not None: - image_files = glob(pattern) - assert len(image_files) > 0, \ - f'No images matching pattern "{pattern}" from "{os.getcwd()}"' - - images = [] - for image_file in image_files: - new_images = read(image_file, index=index, **kwargs) - # Output of read can either be list of atoms or Atoms, depending on index - if not isinstance(new_images, list): - new_images = [new_images] - images += new_images - - elif patterns is not None: - images = [] - for pattern in patterns: - image_files = glob(pattern) - assert len(image_files) > 0, \ - f'No images matching patterns "{pattern}" from "{os.getcwd()}"' - images += get_images(pattern=pattern, index=index, **kwargs) - elif images is not None: - images = images[parse_slice(str(index))] - else: - images = [] - - assert len(images) > 0, 'No images found' - - return images
- -
[docs]def new_db(dbname: str): - """Creates a new ASE database overwriting an old one - - :param dbname: Name for the new database - :type dbname: str - :return: Connection to database - :rtype: :mod:`ase.db` connection - """ - with open(dbname, 'w', encoding='utf-8') as f: - f.write("") - return ase.db.connect(dbname)
- -
[docs]def get_env_input() -> Dict: - """Gets the global env_input from the environment variable, then tries - then ``.asimtools`` dotfile - - :return: env_input dictionary or empty dictionary if not found - :rtype: Dict - """ - env_input_file = os.getenv('ASIMTOOLS_ENV_INPUT') - if env_input_file is None: - dotfile = Path('~/.asimtools') - if dotfile.exists(): - with open(dotfile, 'r', encoding='utf-8') as f: - lines = f.readlines() - for line in lines: - if line.startswith('ENV_INPUT_FILE'): - env_input_file = Path(line.split('=')) - break - if env_input_file.exists(): - try: - env_input = read_yaml(env_input_file) - return env_input - except FileNotFoundError: - print('WARNING: No env_input yaml found') - else: - env_input = read_yaml(env_input_file) - return env_input - return {}
- -
[docs]def get_calc_input(): - """Gets the global calc_input from the environment variable, then tries - then ``.asimtools`` dotfile - - :return: calc_input dictionary or empty dictionary if not found - :rtype: Dict - """ - calc_input_file = os.getenv('ASIMTOOLS_CALC_INPUT') - if calc_input_file is None: - dotfile = Path('~/.asimtools') - if dotfile.exists(): - with open(dotfile, 'r', encoding='utf-8') as f: - lines = f.readlines() - for line in lines: - if line.startswith('CALC_INPUT_FILE'): - calc_input_file = Path(line.split('=')) - break - if calc_input_file.exists(): - try: - calc_input = read_yaml(calc_input_file) - return calc_input - except FileNotFoundError: - print('WARNING: No calc_input yaml found') - else: - calc_input = read_yaml(calc_input_file) - return calc_input - return {}
- -
[docs]def check_if_slurm_job_is_running(slurm_job_id: Union[str,int]): - """Checks if the slurm job with specifying job_id is running - - :param slurm_job_id: Job ID as str or int - :type slurm_job_id: Union[str,int] - :return: Whether job is running or not - :rtype: bool - """ - slurm_job_id = str(slurm_job_id) - completed_process = subprocess.run( - ['squeue', '--job', slurm_job_id], - check=False, - capture_output=True, - text=True, - ) - stdout = completed_process.stdout - if str(' '+slurm_job_id) in stdout: - return True - else: - return False
- -
[docs]def change_dict_value( - d: Dict, - new_value, - key_sequence: Sequence, - return_copy: bool = True -) -> Dict: - """Changes a value in the specified dictionary given by following the - key sequence - - :param d: dictionary to be changed - :type d: Dict - :param new_value: The new value that will replace the old one - :type new_value: _type_ - :param key_sequence: List of keys in the order in which they access the dictionary key - :type key_sequence: Sequence - :param return_copy: Whether to return a copy only or to modify the dictionary in-place as well, defaults to True - :type return_copy: bool, optional - :return: The changed dictionary - :rtype: Dict - """ - if return_copy: - d = deepcopy(d) - if len(key_sequence) == 1: - d[key_sequence[0]] = new_value - return d - else: - new_d = change_dict_value( - d[key_sequence[0]], - new_value, - key_sequence[1:], - return_copy=return_copy - ) - d[key_sequence[0]] = new_d - return d
- -
[docs]def get_logger( - logfile='job.log', - fstr='%(asctime)s |%(module)s|%(funcName)s| %(levelname)s: %(message)s', - level='debug', -): - ''' Get the logger ''' - - level_dict = { - 'debug': logging.DEBUG, - 'info': logging.INFO, - 'warning': logging.WARNING, - } - assert level in level_dict, f'level must be one of {level_dict.keys()}' - - logging.basicConfig( - filename=logfile, - level=level_dict[level], - format=fstr, - ) - return logging.getLogger(__name__)
-
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/_modules/index.html b/build/_modules/index.html deleted file mode 100644 index 6b65d1c..0000000 --- a/build/_modules/index.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - Overview: module code — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- - -
-
- - - - \ No newline at end of file diff --git a/build/_sources/CONTRIBUTING.md.txt b/build/_sources/CONTRIBUTING.md.txt deleted file mode 100644 index cd24d2d..0000000 --- a/build/_sources/CONTRIBUTING.md.txt +++ /dev/null @@ -1,94 +0,0 @@ -# Contributing - -When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change. - -Please note we have a code of conduct, please follow it in all your interactions with the project. - -There are two main ways to contribute. One is to make changes to the existing -codes and scripts. In this case, please make sure your change passes tests or -submit a test accordinly. The second way to contribute is to add a new script -to the core set of scripts. Reviewers will work with you to make sure your new -script follows best practice including using utils, syntax highlighting, docs -and tests. - -## Pull Request Process - -1. Ensure any install or build dependencies are removed before the end of the layer when doing a build. -2. Update the README.md with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters. -3. Increase the version numbers in any examples files and the README.md to the new version that this Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). -4. You may merge the Pull Request in once you have the sign-off of one other developer, or if you do not have permission to do that, you may request the other reviewer to merge it for you. - -## Code of Conduct - -### Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation. - -### Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or -advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -### Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -### Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -### Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at [INSERT EMAIL ADDRESS]. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -### Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ diff --git a/build/_sources/README.md.txt b/build/_sources/README.md.txt deleted file mode 100644 index a1f4720..0000000 --- a/build/_sources/README.md.txt +++ /dev/null @@ -1,181 +0,0 @@ -# Atomic SIMulation Tools - -This package is a lightweight workflow and simulation manager for reproducible -atomistic simulations that can be transferred across environments, calculators -and structures. By using in-built or user-defined scripts and utilities, users -can run/build their own simulation recipes and automagically scale them on -slurm based clusters. The core idea is to separate the dependence of the -atomistic potential/calculator and the simulations steps thereby allowing the -same simulation to be run with multiple calculators and the same calculator to -be used for multiple simulations. Input and output files must follow a strict -format so that consistent analysis pipelines can be used across users - -## Developer philosophy -The goal of asimtools is to push all the complexity of workflow -management, best-practices, file management etc. into the backend such that the -everyday user only has to handle input files for existing workflows and script -files for workflows they want to implement. The following are the guiding -principles for how asimtools should work: - -- Scripts should resemble boilerplate ASE code as much as possible. -- Scripts should not explicitly depend on a calculator -- Scripts should not explicitly depend on the context in which they run -- It should be easy to debug individual scripts/parts in large workflows. In - addition, it should always be possible to debug/resubmit jobs without using - asimtools. -- input file structure and format should be standard across all scripts. In - addition all input parameters should match how they would like without - asimtools i.e. do not provide an API! This means extensive use of importlib. -- output file structure should be predictable across all scripts -- Job progress tracking must be incorporated -- Best practices should be built-in e.g. if multiple jobs of the same slurm - context are submitted simulataneously, it must be a job array - -## Philosophy on User Experience -The philosophy is to build "simulations" using building blocks of scripts. -These scripts can be as complicated/efficient as you make them using any -external packages you want and can be optimized with time but can still be run -within the framework. This allows a test friendly way to transition from say a -tutorial on the ASE/pymatgen website to an asimtools script. So while -complicated wrappers are discouraged, they would still work as long as the -script works. The benefit out of the box is that you can make your script -independent of calculator or input structures and submit them easily. - -## Getting Started - -These instructions will give you a copy of the project up and running on -your local machine for development and testing purposes. - -### Installing -You can install asimtools in a new conda environment using: -``` -conda create -n asimtools python=3.9 -conda activate asimtools - -conda install ase -c conda-forge - -git clone https://github.com/BattModels/asimtools.git -cd asimtools -pip install -e . -``` - -This installs the base package in developer mode so that you do not have to -reinstall if you make changes to the core package. - -Individual calculators may need external packages for loading those -calculators. Similarly some scripts e.g. `lammps.py` might also need external packages to be used. It is up to the user to make sure those are installed. - -You will also need to setup some environment variables, these variables point -to a `env_input.yaml` and `calc_input.yaml` with your favorite configs since -these are commonly shared among simulations. You can also directly specify them -when running `asim-execute` (See `asim-execute -h`). -Examples of these files can be found in the examples. - -Add the following to your `.bashrc` -``` -export ASIMTOOLS_ENV_INPUT=/path/to/my/global/env_input.yaml -export ASIMTOOLS_CALC_INPUT=/path/to/my/global/calc_input.yaml -export ASIMTOOLS_SCRIPT_DIR=/path/to/my/script/dir -``` - - - - - - -## Running the tests - -To run all tests from the tests directory, call: - - pytest - -To run the test suite on a component `component.py` , call: - - pytest test_component.py - - - -## Contributing - -Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code -of conduct, and the process for submitting pull requests to us. - -## Authors - - - **Keith Phuthi** - [mkphuthi](https://github.com/mkphuthi) - - **Emil Annevelink** - [emilannevelink](https://github.com/emilannevelink) - -See also the list of -[contributors](https://github.com/BattModels/asimtools.git/contributors) -who participated in this project. - -## License - -This project is licensed under the MIT License - - diff --git a/build/_sources/asimplify.rst.txt b/build/_sources/asimplify.rst.txt deleted file mode 100644 index 07e9b16..0000000 --- a/build/_sources/asimplify.rst.txt +++ /dev/null @@ -1,277 +0,0 @@ -Developing Custom Scripts -========================= - -This section will guide you through taking your own in-house simulation and -integrating it in to asimtools. The process is designed to be as -straight-forward as reasonably possible. - -As an example, we will ASIMplify the calculation of adsorption_energies as -shown in ASE. To understand the code, visit the ASE `tutorials page `. Ultimately, the -ASIMplified code will work with any element and , any calculator and any -environment and be fully parallelized job submission in just a few steps! - -**Use toy example** To make things easier, it is best to first replace the -expensive parts of your workflow with toy examples e.g. use an empirical -calculator like EAM instead of DFT, use a smaller/simpler structure, loop -over fewer cases etc. - -The ASE example already uses a simple EMT calculator and structure which is -provided here for reference: - -.. code-block:: - - from ase.build import bulk - from ase.calculators.emt import EMT - from ase.eos import calculate_eos - from ase.db import connect - - db = connect('bulk.db') - for symb in ['Al', 'Ni', 'Cu', 'Pd', 'Ag', 'Pt', 'Au']: - atoms = bulk(symb, 'fcc') - atoms.calc = EMT() - eos = calculate_eos(atoms) - v, e, B = eos.fit() # find minimum - # Do one more calculation at the minimu and write to database: - atoms.cell *= (v / atoms.get_volume())**(1 / 3) - atoms.get_potential_energy() - db.write(atoms, bm=B) - - -**Wrap in a function** The workhorse of ASIMTools is the function, any code -wrapped in a function that returns a dictionary can be run within the -ASIMTools framework. The easiest thing to do would be to take the code and -copy and paste it in a function which is defined inside a script with the -same name - - -.. code-block:: - - from ase.build import bulk - from ase.calculators.emt import EMT - from ase.eos import calculate_eos - from ase.db import connect - - def ase_eos(): - db = connect('bulk.db') - for symb in ['Al', 'Ni', 'Cu', 'Pd', 'Ag', 'Pt', 'Au']: - atoms = bulk(symb, 'fcc') - atoms.calc = EMT() - eos = calculate_eos(atoms) - v, e, B = eos.fit() # find minimum - # Do one more calculation at the minimu and write to database: - atoms.cell *= (v / atoms.get_volume())**(1 / 3) - atoms.get_potential_energy() - db.write(atoms, bm=B) - - return {} - -Immediately as it is, this script can be run in ASIMTools. You can run it with -the following sim_input.yaml - -.. code-block:: yaml - - script: /path/to/ase_eos.py - env_id: inline - workdir: results - -then call - -.. code-block:: console - - asim-execute sim_input.yaml - -Only a little bit more complicated than calling ``python ase_eos.py`` -This script however still depends on specific structures and a specific -calculator. Let's do the easy thing first, let's make the script work with any -calculator using a simple change. - -.. code-block:: - - from ase.build import bulk - from ase.eos import calculate_eos - from ase.db import connect - from asimtools.calculators import load_calc - - def ase_eos( - calc_id, - ): - calc = load_calc(calc_id) - db = connect('bulk.db') - for symb in ['Al', 'Ni', 'Cu', 'Pd', 'Ag', 'Pt', 'Au']: - atoms = bulk(symb, 'fcc') - atoms.calc = calc - eos = calculate_eos(atoms) - v, e, B = eos.fit() # find minimum - # Do one more calculation at the minimu and write to database: - atoms.cell *= (v / atoms.get_volume())**(1 / 3) - atoms.get_potential_energy() - db.write(atoms, bm=B) - - return {} - -Just like that we can now run the script with any correctly configure -calculator for the all the structures! We can even now run ``calc_array`` to -iterate getting the results using different calculators. - -The final change we will make is to parallelize over structures as below - -.. code-block:: - - from ase.build import bulk - from ase.eos import calculate_eos - from ase.db import connect - from asimtools.calculators import load_calc - - def ase_eos( - image, - calc_id, - ): - calc = load_calc(calc_id) - db = connect('bulk.db') - atoms = get_atoms(**image) - atoms.calc = calc - eos = calculate_eos(atoms) - v, e, B = eos.fit() # find minimum - # Do one more calculation at the minimu and write to database: - atoms.cell *= (v / atoms.get_volume())**(1 / 3) - atoms.get_potential_energy() - db.write(atoms, bm=B) - - return {} - -Easy-peasy. We now have a script that works with arbitrary environment, -arbitrary calculator and arbitrary input structure (Of course the simulation -will fail if we give a bad structure for example) - -We can do some final cleanup of the script so that it sends outputs to -``output.yaml`` and logs some checkpoints. Additionally, any scripts added to -the repository will need clear syntax highlighting and documentation. - -.. code-block:: - - from typing import Dict - import logging - from ase.eos import calculate_eos - from ase.db import connect - from asimtools.calculators import load_calc - from asimtools.utils import get_atoms - - def ase_eos( - image: Dict, - calc_id: str, - db_file: 'bulk.db' - ) -> Dict: - calc = load_calc(calc_id) - db = connect(db_file) - atoms = get_atoms(**image) - atoms.calc = calc - eos = calculate_eos(atoms) - v, e, B = eos.fit() # find minimum - logging.info('Successfully fit EOS') - # Do one more calculation at the minimu and write to database: - atoms.cell *= (v / atoms.get_volume())**(1 / 3) - atoms.get_potential_energy() - db.write(atoms, bm=B) - - results = {'v': float(v), 'e': float(e), 'B': float(B)} - return results - -To run this script on an arbitrary structure say Argon with say the -LennardJones calculator, in a slurm job we can now use the following input -files. - -sim_input.yaml: - -.. code-block:: yaml - - script: /path/to/ase_eos.py - env_id: batch - workdir: results - args: - image: - builder: bulk - name: Ar - calc_id: gpaw - -calc_input.yaml: - -.. code-block:: yaml - - lj: - name: LennardJones - module: ase.calculators.lj - args: - sigma: 3.54 - epsilon: 0.00802236 - emt: - name: EMT - module: ase.calculators.emt - args: {} - -env_input.yaml: - -.. code-block:: yaml - - batch: - mode: - use_slurm: true - interactive: false - slurm: - flags: - - -n 2 - precommands: - - source ~/.bashrc - - conda activate asimtools - inline: - mode: - use_slurm: false - interactive: true - -Going back to the original problem, we wanted to run the simulation of mulitple -different elements with the EMT calculator. To achieve that in parallel, we can -nest the ``ase_eos`` script in a :func:`asimtools.scripts.sim_array.sim_array` script as follows - -sim_input.yaml: - -.. code-block:: yaml - - script: sim_array - workdir: results - args: - key_sequence: ['args', 'image', 'name'] - array_values: ['Al', 'Ni', 'Cu', 'Pd', 'Ag', 'Pt', 'Au'] - env_ids: 'batch' - template_sim_input: - script: /path/to/ase_eos.py - args: - calc_id: emt - image: - builder: bulk - crystalstructure: 'fcc' - -To make the script easier to access without having to use the full path, you -can set the environment variable - -.. code-block:: console - - export ASIMTOOLS_SCRIPT_DIR=/path/to/my/script/dir/ - -You can then move the ``ase_eos.py`` script into a directory called ``ase_eos`` -and place it in the script directory. This allows you to refer to scripts -prepended with the script dir as below - -.. code-block:: yaml - - script: sim_array - workdir: results - args: - key_sequence: ['args', 'image', 'name'] - array_values: ['Al', 'Ni', 'Cu', 'Pd', 'Ag', 'Pt', 'Au'] - env_ids: 'batch' - template_sim_input: - script: ase_eos/ase_eos.py - args: - calc_id: emt - image: - builder: bulk - crystalstructure: 'fcc' diff --git a/build/_sources/asimtools.rst.txt b/build/_sources/asimtools.rst.txt deleted file mode 100644 index f555217..0000000 --- a/build/_sources/asimtools.rst.txt +++ /dev/null @@ -1,43 +0,0 @@ -asimtools package -================= - - - -.. toctree:: - :maxdepth: 4 - - asimtools.scripts - - - -asimtools.calculators module ----------------------------- - -.. automodule:: asimtools.calculators - :members: - :undoc-members: - :show-inheritance: - -asimtools.job module --------------------- - -.. automodule:: asimtools.job - :members: - :undoc-members: - :show-inheritance: - -asimtools.utils module ----------------------- - -.. automodule:: asimtools.utils - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: asimtools - :members: - :undoc-members: - :show-inheritance: diff --git a/build/_sources/asimtools.scripts.eos.rst.txt b/build/_sources/asimtools.scripts.eos.rst.txt deleted file mode 100644 index 8aba0f2..0000000 --- a/build/_sources/asimtools.scripts.eos.rst.txt +++ /dev/null @@ -1,36 +0,0 @@ -asimtools.scripts.eos package -============================= - - - -asimtools.scripts.eos.eos module --------------------------------- - -.. automodule:: asimtools.scripts.eos.eos - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.eos.postprocess module ----------------------------------------- - -.. automodule:: asimtools.scripts.eos.postprocess - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.eos.preprocess module ---------------------------------------- - -.. automodule:: asimtools.scripts.eos.preprocess - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: asimtools.scripts.eos - :members: - :undoc-members: - :show-inheritance: diff --git a/build/_sources/asimtools.scripts.rst.txt b/build/_sources/asimtools.scripts.rst.txt deleted file mode 100644 index 2cfbf33..0000000 --- a/build/_sources/asimtools.scripts.rst.txt +++ /dev/null @@ -1,147 +0,0 @@ -asimtools.scripts package -========================= - - - -.. toctree:: - :maxdepth: 4 - - asimtools.scripts.eos - - - -asimtools.scripts.asim\_check module ------------------------------------- - -.. automodule:: asimtools.scripts.asim_check - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.asim\_execute module --------------------------------------- - -.. automodule:: asimtools.scripts.asim_execute - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.asim\_run module ----------------------------------- - -.. automodule:: asimtools.scripts.asim_run - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.atom\_relax module ------------------------------------- - -.. automodule:: asimtools.scripts.atom_relax - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.calc\_array module ------------------------------------- - -.. automodule:: asimtools.scripts.calc_array - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.cell\_relax module ------------------------------------- - -.. automodule:: asimtools.scripts.cell_relax - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.chained module --------------------------------- - -.. automodule:: asimtools.scripts.chained - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.distributed module ------------------------------------- - -.. automodule:: asimtools.scripts.distributed - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.do\_nothing module ------------------------------------- - -.. automodule:: asimtools.scripts.do_nothing - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.env\_array module ------------------------------------ - -.. automodule:: asimtools.scripts.env_array - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.image\_array module -------------------------------------- - -.. automodule:: asimtools.scripts.image_array - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.lammps module -------------------------------- - -.. automodule:: asimtools.scripts.lammps - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.parity module -------------------------------- - -.. automodule:: asimtools.scripts.parity - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.sim\_array module ------------------------------------ - -.. automodule:: asimtools.scripts.sim_array - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.singlepoint module ------------------------------------- - -.. automodule:: asimtools.scripts.singlepoint - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.template module ---------------------------------- - -.. automodule:: asimtools.scripts.template - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: asimtools.scripts - :members: - :undoc-members: - :show-inheritance: diff --git a/build/_sources/include_contributing.rst.txt b/build/_sources/include_contributing.rst.txt deleted file mode 100644 index 78dbb13..0000000 --- a/build/_sources/include_contributing.rst.txt +++ /dev/null @@ -1,5 +0,0 @@ -Contributing to ASIMTools -========================= - -.. include:: CONTRIBUTING.md - :parser: myst_parser.sphinx_ diff --git a/build/_sources/include_readme.rst.txt b/build/_sources/include_readme.rst.txt deleted file mode 100644 index f2253cd..0000000 --- a/build/_sources/include_readme.rst.txt +++ /dev/null @@ -1,5 +0,0 @@ -README -====== - -.. include:: README.md - :parser: myst_parser.sphinx_ diff --git a/build/_sources/index.rst.txt b/build/_sources/index.rst.txt deleted file mode 100644 index 06cd649..0000000 --- a/build/_sources/index.rst.txt +++ /dev/null @@ -1,28 +0,0 @@ -.. asimtools documentation master file, created by - sphinx-quickstart on Sat Jul 1 18:22:31 2023. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to asimtools's documentation! -===================================== - -.. toctree:: - :maxdepth: 12 - :caption: Contents: - - README - Running Scripts - Custom Scripts - Workflows - API Docs - Contributing - - - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/build/_sources/modules.rst.txt b/build/_sources/modules.rst.txt deleted file mode 100644 index f4e9fc8..0000000 --- a/build/_sources/modules.rst.txt +++ /dev/null @@ -1,7 +0,0 @@ -asimtools -========= - -.. toctree:: - :maxdepth: 4 - - asimtools diff --git a/build/_sources/usage.rst.txt b/build/_sources/usage.rst.txt deleted file mode 100644 index 6168305..0000000 --- a/build/_sources/usage.rst.txt +++ /dev/null @@ -1,443 +0,0 @@ -Running an existing script -========================== - -This section will guide you through running an already existing script whether -it be one of the scripts provided in the main repository, from the script -repository or a custom script you have written yourself. - -To run a simulation, all you need to do is run either of the following -commands: - -.. code-block:: console - - asim-execute sim_input.yaml -c calc_input.yaml -e env_input.yaml - -or - -.. code-block:: console - - asim-run sim_input.yaml -c calc_input.yaml - -In either case, providing ``calc_input.yaml`` or ``env_input.yaml`` is optional. If -not provided, the globally configured files will be used. See :ref:`envinput`. For most -cases, you will only ever use ``asim-execute``. The differences between -``asim-execute`` and ``asim-run`` are explained in :ref:`asimrunvsasimexecute`. - -.. _inputs: - -Input files -*********** - -.. _siminput: - -sim_input.yaml --------------- - -The minimal requirement to run a script is to provide a ``sim_input.yaml`` file. -An example of a ``sim_input.yaml`` is shown below: - -.. code-block:: yaml - - script: singlepoint - env_id: inline - overwrite: false - submit: true - workdir: results - precommands: - - export MY_ENV_VAR=3 - args: - arg1: value_1 - arg2: value_2 - ... - -The parameters are: - -- **script**: (str) name of core script or /path/to/my/script.py -- **env_id**: (str, optional) Environment/context in which to run script - configured in env_input.yaml, defaults to running in the current console -- **overwrite**: (bool, optional) (bool) whether or not to overwrite work - directories if they exist, defaults to false -- **submit**: (bool, optional) whether or not to run the script or instead just - write the files to run the script without running the script, defaults to true -- **workdir**: (str, optional) The directory in which the script will be run, - `asim-execute` will create the directory whereas `asim-run` ignores this - parameter, defaults to './' -- **precommands**: (list, optional) a list of commands to run in the console - before running the script, defaults to empty list -- **postcommands**: (list, optional) a list of commands to run in the console - after running the script, defaults to empty list -- **args**: (dict) The arguments of the function being called in the script as - key-value pairs. These are specific to the script being run. - -All ASIMTools generated files are named ``sim_input.yaml`` but you can name -user defined files as whatever you like - -.. _envinput: - -env_input.yaml --------------- -Additionally, one can provide an ``env_input.yaml`` file that details the kind of -environments in which scripts can be run. This file can be provided with the -asim-execute command using the ``-e`` flag or set globally. An example of an -env_input.yaml file is given below - -.. code-block:: yaml - - # template - env_id: - mode: - use_slurm: true - interactive: false - run_prefix: ... - run_postfix: ... - slurm: - flags: [flag1, flag2, ...] - precommands: [precommand1, precommand2, ...] - postcommands: [postcommand1, postcommand2, ...] - - # Concrete examples below - inline: # Run the script directly in the commandline - mode: - use_slurm: false - interactive: true - - batch_job: # Submit a batch job using slurm with 2 tasks - mode: - use_slurm: true - interactive: false - slurm: - flags: - - -n 2 - precommands: - - source ~/.bashrc - - conda activate asimtools - postcommands: - - conda deactivate asimtools - - interactive_job: # Submit an interactive job using slurm - mode: - use_slurm: true - interactive: true - slurm: - flags: - - -n 2 - - --gres:gpu=2 - precommands: - - module load lammps - -The highest level key is the ``env_id`` which is specified in the -``sim_input.yaml``. An ``env_input.yaml`` can have any number of ``env_id`` s. That -way you can specify one global file if you use the same environments -repeatedly. In particular, you can configure a global config file by setting -the environment variable. - -.. code-block:: console - - export ASIMTOOLS_ENV_INPUT=/path/to/my/global/env_input.yaml - -The parameters, required, shown in the template section are are described below - -- **env_id**: (str) unique key for identifying the environment, ``env_id`` in - ``sim_input.yaml`` must match one of the ``env_id`` s defined in the provided - ``env_input.yaml`` -- **env_id.mode.use_slurm**: (bool) whether or not to request a slurm - allocation to run the script -- **env_id.mode.interactive**: (bool) whether or not to request a slurm - allocation to run the script directly in the terminal (using ``salloc``) or - to submita batch job (using ``sbatch``) -- **env_id.mode.run_prefix**: (str) string to append before running the script - e.g. if ``run_prefix=mpirun`` the script will be inkoked with the equivalent - of ``mpirun python my_script.py``. ``run_prefix`` in ``env_input.yaml`` is - always prepended before the one provided by ``calc_input.yaml``. -- **env_id.mode.run_postfix**: (str) string to append after running the script - e.g. if ``run_postfix=' &> out.txt'`` the script will be inkoked with the - equivalent of ``python my_script.py &> out.txt``. ``run_postfix`` in - ``env_input.yaml`` is always appended after the one provided by - ``calc_input.yaml``. -- **env_id.slurm.flags**: (list, optional) The slurm flags for the allocation - as a list of flags e.g. ``[-n 4, -N 1]`` -- **env_id.slurm.precommands**: (list, optional) Commands to be run/added to - the job script before running the script. A common use cas is loading a - module or activating an environment -- **env_id.slurm.postcommands**: (list, optional) Commands to be run/added to - the job script after running the script. - -.. _calcinput: - -calc_input.yaml ---------------- -Lastly the ``calc_input.yaml`` is used to configure an ASE calculator. As -above, a global configuration file can be set using - -.. code-block:: console - - export ASIMTOOLS_CALC_INPUT=/path/to/my/global/calc_input.yaml - -or provided to asim-execute at run time. Note that if you launch a chained -workflow with ``asim-run`` instead of ``asim-execute``, scripts farther down -the chain will use the global ``calc_input.yaml`` - - -.. code-block:: yaml - - # Template - calc_id: - name: ... - module: ... - precommands: [precommand1, ...] - postcommands: [postcommand1, ...] - run_prefix: ... - run_postfix: ... - args: - arg1: value_1 - ... - - # Concrete examples - lj: - name: LennardJones - module: ase.calculators.lj - args: - sigma: 3.54 - epsilon: 0.00802236 - - # GPAW needs a run_prefix to work in parallel using mpirun - gpaw: - name: GPAW - module: gpaw.calculator - run_prefix: mpirun - args: - kpts: [2,2,2] - h: 0.1 - xc: PBE - txt: gpaw_output.txt - - -- **calc_id**: (str) unique key for identifying the calculator, ``calc_id`` in - ``sim_input.yaml`` must match one of the ``calc_id`` s defined in the - provided ``calc_input.yaml`` -- **calc_id.name**: (str) Either the name of the class or the reference to one - of the provided BBBB external calculators. -- **calc_id.module**: (str) The module from which the calculator class is - imported. e.g. if ``name=LennardJones`` and ``module=ase.calculators.lj``, - then the calculator object is imported as ``from ase.calculators.lj import - LennardJones``. This works if the calculator is available in ASE or follows - ASE format for initialization such as GPAW. Any other ASE calculator will - need to have the instantiation defined in :ref:calculators.py -- **calc_id.mode.run_prefix**: (str) string to append before running the script - e.g. if ``run_prefix=mpirun`` the script will be inkoked with the equivalent - of ``mpirun python my_script.py``. ``run_prefix`` in ``env_input.yaml`` is - always prepended before the one provided by ``calc_input.yaml``. -- **calc_id.mode.run_postfix**: (str) string to append after running the script - e.g. if ``run_postfix=' &> out.txt'`` the script will be inkoked with the - equivalent of ``python my_script.py &> out.txt``. ``run_postfix`` in - ``env_input.yaml`` is always appended after the one provided by - ``calc_input.yaml``. -- **calc_id.precommands**: (list, optional) Commands to be run/added to the job - script before running the script. A common use cas is loading a module or - activating an environment -- **calc_id.postcommands**: (list, optional) Commands to be run/added to the - job script after running the script. -- **calc_id.args**: (dict) key-value pairs to be passed as arguments for the - initialization of the calculator class. e.g. if the class is LennardJones, - the arguments are passed as ``calc = LennardJones(**{'sigma':3.2, - 'epsilon':3})`` - -.. _specifyingimages: - -Specifying Images/Atoms ------------------------ - -One of the most useful applications of ASIMTools is the unification of methods -for setting up ASE atoms objects using the same interface. If a script requires -a single or multiple atoms objects as input, they are provided as either an -``image`` dictionary for a single Atoms object or ``images`` for a list of -Atoms objects as part of the ``args`` section. Below are all the different ways -to get an atoms object. Downloading images from MaterialsProject and Generating -them from Pymatgen will implemented in future. - -For a detailed description of the API, see :func:`asimtools.utils.get_atoms` - -.. code-block:: yaml - - # Reading a specific image from a structure file using ase.io.read - image: - image_file: /path/to/my/ASE-readable/image/file.xyz - # Optional keyword argument passed to ase.io.read - index: 3 - - # Building a bulk crystal using ase.build.bulk - image: - builder: bulk - # Optional keyword arguments passed to the builder, must match ASE exactly - name: Li - crystalstructure: bcc - a: 4.3 - cubic: True - - # Building a surface using ase.build.fcc100 - image: - builder: fcc100 - # Optional keyword arguments passed to the builder, must match ASE exactly - symbol: Fe - vacuum: 8 - periodic: False - - # Building a 3x3x3 supercell of Ar using ase.build.bulk then - # Atoms.repeat(repeat) and then applying Atoms.rattle(stdev=rattle_stdev) - image: - name: Ar - repeat: [3,3,3] - rattle_stdev: 0.01 - - # You can even supply an atoms object directly so that the interface is - # universal. This is most useful in the script code itself. - image: - atoms: Atoms - -Similarly, if the script requires multiple image inputs, there exists a -universal interface. The keyword is strictly specified as ``images`` This is especially useful for distributing simulations -across multiple structures or reading structures from multiple previous -simulations. - -For a detailed description of the API, see :func:`asimtools.utils.get_images` - -.. code-block:: yaml - - # Reading specific images from a structure file using ase.io.read - images: - image_file: /path/to/my/ASE-readable/image/file.xyz - # Optional keyword arguments passed to ase.io.read - index: '3:8' - format: extxyz - - # You can read all files matching a certain pattern using a wildcard - images: - pattern: /path/to/my/structure/files/*.cif - # Optional keyword argument passed to ase.io.read - index: -1 - - # You can read all files matching a certain pattern using a wildcard - images: - patterns: - - /path/to/my/structure/files/*.cif - - /path/to/my/other/structure/files/*.cfg - - # You can even supply a list of atoms objects directly so that the interface - # is universal. This is most useful in the script code itself. - images: - images: [Atoms1, Atoms2, ...] - -.. _asimrunvsasimexecute: - -Usage of asim-execute and asim-run -********************************** -The major difference between ``asim-execute`` and ``asim-run`` is that, -``asim-execute`` takes into account the ``workdir`` and the ``env_id``. -``asim-run`` will the script in the current directory and in the current -console. In fact, ``asim-execute`` will create the ``workdir`` and then run -``asim-run`` in the correct environment/batch job. You can always for example, -request a slurm allocation, go to the directory where you want the script to be -run and call ``asim-run`` from there if you would like more control or to debug. -If you want verbose logs for debugging, you can run with the ``-d`` or -``--debug`` flag. - -.. _outputs: - -Output files -**************** -A job or script run through ASIMTools will always produce a standard set of -output files in addition to whatever outputs the script produces. In particular -the most important output are the ``output.yaml`` and the ``job.log`` file. - -#. \``output.yaml`` contains the status of the job being run in the current - directory which can be one of ``clean, started, complete, failed, discard``. - The statuses are self-explanatory, the ``discard`` status is never written - by ASIMTools but a user can edit an ``output.yaml`` file and change it's - status to ``discard`` to tell ASIMTools to ignore that job in any workflows. - This is common for example if you launch multiple jobs and one of them fails - irredemably. Deleting the directory for that job is also ok if nothing - depends on it downstream. Importantly, any results returned by the function - defined in the script are found in ``output.yaml``. Script main functions - should always return a dictionary for this purpose. - - An example of an ``output.yaml`` file is shown below - -.. code-block:: yaml - - # Successful output for singlepoint script - end_time: 2023-08-28 21:50:51.368025 - energy: 13.77302319846367 #This was added by the scinglepoint script - files: - image: image_output.xyz - job_ids: '372919' - start_time: 2023-08-28 21:50:46.188300 - status: complete - - # Failed output - start_time: 14:29:55, 10/06/23 - status: failed - -#. ``job.log`` captures the logged output of ``asim-run`` or scripts - that use logging. It is extremely useful for debugging as following the logs - starting from the base directory will usually lead you to the correct - traceback that caused the failure. - -#. ``stderr.txt`` captures errors and backtraces from running scripts. This is - usually the most informative file for debugging. You can be directed to the - correct one by noting errors in ``job.log`` - -#. ``stdout.txt`` captures any stdout from running scripts. It is mostly a - safety measure for catching anything that prints to stdout and rarely has - useful information unless you write a script that uses ``print`` statements. - -#. ``input_image.xyz`` and ``input_images.xyz`` capture the images input into - the script. This makes sure there is a concrete artifact for the structure - used in the script for the purposes of visualization and debugging. They are - always in ``extxyz`` format as a flexible standar format - -#. ``slurm*`` are slurm job files which can be named according to flags - specified in ``env_input.yaml`` otherwise are named ``slurm_[job_id].out`` - - -.. _restarting: - -Checking job status and Restarting failed jobs -************************************************** -To check the status of jobs, even complicated chains and distributed jobs, we -provide the ``asim-check`` utility which can be run using: - -.. code-block:: console - - asim-check /path/to/sim_input.yaml - -This will print the job tree, including statuses and work directories of the -jobs whose root directory is specified as ``workdir`` in ``sim_input.yaml``. - -In many cases, there may be mistakes in one of your configuration files leading to -a failed workflow. In these cases there are a few ways you could resolve the -issue: - -* Delete the workdirectory and restart the workflow. This is why it is - recommended that the base ``sim_input.yaml`` has ``workdir`` set to a new - directory that only has the results of the workflow. -* Modify the ASIMTools generated ``sim_input.yaml`` to fix the problem. If - there are downstream ``sim_input.yaml`` files in a chain, they will have to - be deleted or set ``overwrite=True``. Deleting is recommended for safety - purposes. -* Simply rerun ``asim-execute``. This will rerun the jobs, skipping any jobs - with a status of ``complete`` or ``discard``. Note that error files are not - deleted so you will have to clear those manually - -Importing functions from scripts -******************************** - -Because scripts contain what are merely Python functions, you can always import -them and use them in any other code for example, you can import -:func:`asimtools.scripts.singlepoint` and use it as below. - -.. code-block:: python - - from asimtools.scripts.singlepoint import singlepoint - - results = singlepoint(image={'name': 'Ar'}, calc_id='lj') - print(results) diff --git a/build/_sources/workflows.rst.txt b/build/_sources/workflows.rst.txt deleted file mode 100644 index 7e12001..0000000 --- a/build/_sources/workflows.rst.txt +++ /dev/null @@ -1,26 +0,0 @@ -Using built-in workflow tools -============================= - -ASIMTools offers some standard tools for performing common workflows. These -are: - -#. :func:`asimtools.scripts.sim_array.sim_array` - Run the same script with one - specified argument of the script iterated over. This is the most useful - script and captures all the others except ``chained`` - -#. :func:`asimtools.scripts.image_array.image_array` - Run the same script - on multiple structures - -#. :func:`asimtools.scripts.calc_array.calc_array` - Run the same script - using different calculators - -#. :func:`asimtools.scripts.distributed.distributed` - Run multiple - sim_inputs in parallel - -#. :func:`asimtools.scripts.chained.chained` - Run scripts one after the - other, e.g. if step 2 results depend on step 1 etc. - -Examples for each type of workflow are given in the examples directory and -documentation can be found in :mod:`asimtools.scripts`. They also serve as templates for -you to build your own workflows directly using :func:`asimtools.job.Job` -objects as an advanced user. diff --git a/build/_static/_sphinx_javascript_frameworks_compat.js b/build/_static/_sphinx_javascript_frameworks_compat.js deleted file mode 100644 index 8141580..0000000 --- a/build/_static/_sphinx_javascript_frameworks_compat.js +++ /dev/null @@ -1,123 +0,0 @@ -/* Compatability shim for jQuery and underscores.js. - * - * Copyright Sphinx contributors - * Released under the two clause BSD licence - */ - -/** - * small helper function to urldecode strings - * - * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL - */ -jQuery.urldecode = function(x) { - if (!x) { - return x - } - return decodeURIComponent(x.replace(/\+/g, ' ')); -}; - -/** - * small helper function to urlencode strings - */ -jQuery.urlencode = encodeURIComponent; - -/** - * This function returns the parsed url parameters of the - * current request. Multiple values per key are supported, - * it will always return arrays of strings for the value parts. - */ -jQuery.getQueryParameters = function(s) { - if (typeof s === 'undefined') - s = document.location.search; - var parts = s.substr(s.indexOf('?') + 1).split('&'); - var result = {}; - for (var i = 0; i < parts.length; i++) { - var tmp = parts[i].split('=', 2); - var key = jQuery.urldecode(tmp[0]); - var value = jQuery.urldecode(tmp[1]); - if (key in result) - result[key].push(value); - else - result[key] = [value]; - } - return result; -}; - -/** - * highlight a given string on a jquery object by wrapping it in - * span elements with the given class name. - */ -jQuery.fn.highlightText = function(text, className) { - function highlight(node, addItems) { - if (node.nodeType === 3) { - var val = node.nodeValue; - var pos = val.toLowerCase().indexOf(text); - if (pos >= 0 && - !jQuery(node.parentNode).hasClass(className) && - !jQuery(node.parentNode).hasClass("nohighlight")) { - var span; - var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); - if (isInSVG) { - span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); - } else { - span = document.createElement("span"); - span.className = className; - } - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - node.parentNode.insertBefore(span, node.parentNode.insertBefore( - document.createTextNode(val.substr(pos + text.length)), - node.nextSibling)); - node.nodeValue = val.substr(0, pos); - if (isInSVG) { - var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - var bbox = node.parentElement.getBBox(); - rect.x.baseVal.value = bbox.x; - rect.y.baseVal.value = bbox.y; - rect.width.baseVal.value = bbox.width; - rect.height.baseVal.value = bbox.height; - rect.setAttribute('class', className); - addItems.push({ - "parent": node.parentNode, - "target": rect}); - } - } - } - else if (!jQuery(node).is("button, select, textarea")) { - jQuery.each(node.childNodes, function() { - highlight(this, addItems); - }); - } - } - var addItems = []; - var result = this.each(function() { - highlight(this, addItems); - }); - for (var i = 0; i < addItems.length; ++i) { - jQuery(addItems[i].parent).before(addItems[i].target); - } - return result; -}; - -/* - * backward compatibility for jQuery.browser - * This will be supported until firefox bug is fixed. - */ -if (!jQuery.browser) { - jQuery.uaMatch = function(ua) { - ua = ua.toLowerCase(); - - var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || - /(webkit)[ \/]([\w.]+)/.exec(ua) || - /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || - /(msie) ([\w.]+)/.exec(ua) || - ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || - []; - - return { - browser: match[ 1 ] || "", - version: match[ 2 ] || "0" - }; - }; - jQuery.browser = {}; - jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; -} diff --git a/build/_static/basic.css b/build/_static/basic.css deleted file mode 100644 index 7577acb..0000000 --- a/build/_static/basic.css +++ /dev/null @@ -1,903 +0,0 @@ -/* - * basic.css - * ~~~~~~~~~ - * - * Sphinx stylesheet -- basic theme. - * - * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -div.section::after { - display: block; - content: ''; - clear: left; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; - word-wrap: break-word; - overflow-wrap : break-word; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox form.search { - overflow: hidden; -} - -div.sphinxsidebar #searchbox input[type="text"] { - float: left; - width: 80%; - padding: 0.25em; - box-sizing: border-box; -} - -div.sphinxsidebar #searchbox input[type="submit"] { - float: left; - width: 20%; - border-left: none; - padding: 0.25em; - box-sizing: border-box; -} - - -img { - border: 0; - max-width: 100%; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li p.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; - margin-left: auto; - margin-right: auto; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable { - width: 100%; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable ul { - margin-top: 0; - margin-bottom: 0; - list-style-type: none; -} - -table.indextable > tbody > tr > td > ul { - padding-left: 0em; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -div.modindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -div.genindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -/* -- domain module index --------------------------------------------------- */ - -table.modindextable td { - padding: 2px; - border-collapse: collapse; -} - -/* -- general body styles --------------------------------------------------- */ - -div.body { - min-width: 360px; - max-width: 800px; -} - -div.body p, div.body dd, div.body li, div.body blockquote { - -moz-hyphens: auto; - -ms-hyphens: auto; - -webkit-hyphens: auto; - hyphens: auto; -} - -a.headerlink { - visibility: hidden; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink, -caption:hover > a.headerlink, -p.caption:hover > a.headerlink, -div.code-block-caption:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.first { - margin-top: 0 !important; -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -img.align-left, figure.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; -} - -img.align-right, figure.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; -} - -img.align-center, figure.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - -img.align-default, figure.align-default, .figure.align-default { - display: block; - margin-left: auto; - margin-right: auto; -} - -.align-left { - text-align: left; -} - -.align-center { - text-align: center; -} - -.align-default { - text-align: center; -} - -.align-right { - text-align: right; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar, -aside.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px; - background-color: #ffe; - width: 40%; - float: right; - clear: right; - overflow-x: auto; -} - -p.sidebar-title { - font-weight: bold; -} - -nav.contents, -aside.topic, -div.admonition, div.topic, blockquote { - clear: left; -} - -/* -- topics ---------------------------------------------------------------- */ - -nav.contents, -aside.topic, -div.topic { - border: 1px solid #ccc; - padding: 7px; - margin: 10px 0 10px 0; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; -} - -div.admonition dt { - font-weight: bold; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- content of sidebars/topics/admonitions -------------------------------- */ - -div.sidebar > :last-child, -aside.sidebar > :last-child, -nav.contents > :last-child, -aside.topic > :last-child, -div.topic > :last-child, -div.admonition > :last-child { - margin-bottom: 0; -} - -div.sidebar::after, -aside.sidebar::after, -nav.contents::after, -aside.topic::after, -div.topic::after, -div.admonition::after, -blockquote::after { - display: block; - content: ''; - clear: both; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - margin-top: 10px; - margin-bottom: 10px; - border: 0; - border-collapse: collapse; -} - -table.align-center { - margin-left: auto; - margin-right: auto; -} - -table.align-default { - margin-left: auto; - margin-right: auto; -} - -table caption span.caption-number { - font-style: italic; -} - -table caption span.caption-text { -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 5px; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -th { - text-align: left; - padding-right: 5px; -} - -table.citation { - border-left: solid 1px gray; - margin-left: 1px; -} - -table.citation td { - border-bottom: none; -} - -th > :first-child, -td > :first-child { - margin-top: 0px; -} - -th > :last-child, -td > :last-child { - margin-bottom: 0px; -} - -/* -- figures --------------------------------------------------------------- */ - -div.figure, figure { - margin: 0.5em; - padding: 0.5em; -} - -div.figure p.caption, figcaption { - padding: 0.3em; -} - -div.figure p.caption span.caption-number, -figcaption span.caption-number { - font-style: italic; -} - -div.figure p.caption span.caption-text, -figcaption span.caption-text { -} - -/* -- field list styles ----------------------------------------------------- */ - -table.field-list td, table.field-list th { - border: 0 !important; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -/* -- hlist styles ---------------------------------------------------------- */ - -table.hlist { - margin: 1em 0; -} - -table.hlist td { - vertical-align: top; -} - -/* -- object description styles --------------------------------------------- */ - -.sig { - font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; -} - -.sig-name, code.descname { - background-color: transparent; - font-weight: bold; -} - -.sig-name { - font-size: 1.1em; -} - -code.descname { - font-size: 1.2em; -} - -.sig-prename, code.descclassname { - background-color: transparent; -} - -.optional { - font-size: 1.3em; -} - -.sig-paren { - font-size: larger; -} - -.sig-param.n { - font-style: italic; -} - -/* C++ specific styling */ - -.sig-inline.c-texpr, -.sig-inline.cpp-texpr { - font-family: unset; -} - -.sig.c .k, .sig.c .kt, -.sig.cpp .k, .sig.cpp .kt { - color: #0033B3; -} - -.sig.c .m, -.sig.cpp .m { - color: #1750EB; -} - -.sig.c .s, .sig.c .sc, -.sig.cpp .s, .sig.cpp .sc { - color: #067D17; -} - - -/* -- other body styles ----------------------------------------------------- */ - -ol.arabic { - list-style: decimal; -} - -ol.loweralpha { - list-style: lower-alpha; -} - -ol.upperalpha { - list-style: upper-alpha; -} - -ol.lowerroman { - list-style: lower-roman; -} - -ol.upperroman { - list-style: upper-roman; -} - -:not(li) > ol > li:first-child > :first-child, -:not(li) > ul > li:first-child > :first-child { - margin-top: 0px; -} - -:not(li) > ol > li:last-child > :last-child, -:not(li) > ul > li:last-child > :last-child { - margin-bottom: 0px; -} - -ol.simple ol p, -ol.simple ul p, -ul.simple ol p, -ul.simple ul p { - margin-top: 0; -} - -ol.simple > li:not(:first-child) > p, -ul.simple > li:not(:first-child) > p { - margin-top: 0; -} - -ol.simple p, -ul.simple p { - margin-bottom: 0; -} - -aside.footnote > span, -div.citation > span { - float: left; -} -aside.footnote > span:last-of-type, -div.citation > span:last-of-type { - padding-right: 0.5em; -} -aside.footnote > p { - margin-left: 2em; -} -div.citation > p { - margin-left: 4em; -} -aside.footnote > p:last-of-type, -div.citation > p:last-of-type { - margin-bottom: 0em; -} -aside.footnote > p:last-of-type:after, -div.citation > p:last-of-type:after { - content: ""; - clear: both; -} - -dl.field-list { - display: grid; - grid-template-columns: fit-content(30%) auto; -} - -dl.field-list > dt { - font-weight: bold; - word-break: break-word; - padding-left: 0.5em; - padding-right: 5px; -} - -dl.field-list > dd { - padding-left: 0.5em; - margin-top: 0em; - margin-left: 0em; - margin-bottom: 0em; -} - -dl { - margin-bottom: 15px; -} - -dd > :first-child { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -dl > dd:last-child, -dl > dd:last-child > :last-child { - margin-bottom: 0; -} - -dt:target, span.highlighted { - background-color: #fbe54e; -} - -rect.highlighted { - fill: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa; -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -.guilabel, .menuselection { - font-family: sans-serif; -} - -.accelerator { - text-decoration: underline; -} - -.classifier { - font-style: oblique; -} - -.classifier:before { - font-style: normal; - margin: 0 0.5em; - content: ":"; - display: inline-block; -} - -abbr, acronym { - border-bottom: dotted 1px; - cursor: help; -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; - overflow-y: hidden; /* fixes display issues on Chrome browsers */ -} - -pre, div[class*="highlight-"] { - clear: both; -} - -span.pre { - -moz-hyphens: none; - -ms-hyphens: none; - -webkit-hyphens: none; - hyphens: none; - white-space: nowrap; -} - -div[class*="highlight-"] { - margin: 1em 0; -} - -td.linenos pre { - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - display: block; -} - -table.highlighttable tbody { - display: block; -} - -table.highlighttable tr { - display: flex; -} - -table.highlighttable td { - margin: 0; - padding: 0; -} - -table.highlighttable td.linenos { - padding-right: 0.5em; -} - -table.highlighttable td.code { - flex: 1; - overflow: hidden; -} - -.highlight .hll { - display: block; -} - -div.highlight pre, -table.highlighttable pre { - margin: 0; -} - -div.code-block-caption + div { - margin-top: 0; -} - -div.code-block-caption { - margin-top: 1em; - padding: 2px 5px; - font-size: small; -} - -div.code-block-caption code { - background-color: transparent; -} - -table.highlighttable td.linenos, -span.linenos, -div.highlight span.gp { /* gp: Generic.Prompt */ - user-select: none; - -webkit-user-select: text; /* Safari fallback only */ - -webkit-user-select: none; /* Chrome/Safari */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* IE10+ */ -} - -div.code-block-caption span.caption-number { - padding: 0.1em 0.3em; - font-style: italic; -} - -div.code-block-caption span.caption-text { -} - -div.literal-block-wrapper { - margin: 1em 0; -} - -code.xref, a code { - background-color: transparent; - font-weight: bold; -} - -h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { - background-color: transparent; -} - -.viewcode-link { - float: right; -} - -.viewcode-back { - float: right; - font-family: sans-serif; -} - -div.viewcode-block:target { - margin: -1px -10px; - padding: 0 10px; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -span.eqno a.headerlink { - position: absolute; - z-index: 1; -} - -div.math:hover a.headerlink { - visibility: visible; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -} \ No newline at end of file diff --git a/build/_static/css/badge_only.css b/build/_static/css/badge_only.css deleted file mode 100644 index c718cee..0000000 --- a/build/_static/css/badge_only.css +++ /dev/null @@ -1 +0,0 @@ -.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} \ No newline at end of file diff --git a/build/_static/css/fonts/Roboto-Slab-Bold.woff b/build/_static/css/fonts/Roboto-Slab-Bold.woff deleted file mode 100644 index 6cb6000..0000000 Binary files a/build/_static/css/fonts/Roboto-Slab-Bold.woff and /dev/null differ diff --git a/build/_static/css/fonts/Roboto-Slab-Bold.woff2 b/build/_static/css/fonts/Roboto-Slab-Bold.woff2 deleted file mode 100644 index 7059e23..0000000 Binary files a/build/_static/css/fonts/Roboto-Slab-Bold.woff2 and /dev/null differ diff --git a/build/_static/css/fonts/Roboto-Slab-Regular.woff b/build/_static/css/fonts/Roboto-Slab-Regular.woff deleted file mode 100644 index f815f63..0000000 Binary files a/build/_static/css/fonts/Roboto-Slab-Regular.woff and /dev/null differ diff --git a/build/_static/css/fonts/Roboto-Slab-Regular.woff2 b/build/_static/css/fonts/Roboto-Slab-Regular.woff2 deleted file mode 100644 index f2c76e5..0000000 Binary files a/build/_static/css/fonts/Roboto-Slab-Regular.woff2 and /dev/null differ diff --git a/build/_static/css/fonts/fontawesome-webfont.eot b/build/_static/css/fonts/fontawesome-webfont.eot deleted file mode 100644 index e9f60ca..0000000 Binary files a/build/_static/css/fonts/fontawesome-webfont.eot and /dev/null differ diff --git a/build/_static/css/fonts/fontawesome-webfont.svg b/build/_static/css/fonts/fontawesome-webfont.svg deleted file mode 100644 index 855c845..0000000 --- a/build/_static/css/fonts/fontawesome-webfont.svg +++ /dev/null @@ -1,2671 +0,0 @@ - - - - -Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 - By ,,, -Copyright Dave Gandy 2016. All rights reserved. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/_static/css/fonts/fontawesome-webfont.ttf b/build/_static/css/fonts/fontawesome-webfont.ttf deleted file mode 100644 index 35acda2..0000000 Binary files a/build/_static/css/fonts/fontawesome-webfont.ttf and /dev/null differ diff --git a/build/_static/css/fonts/fontawesome-webfont.woff b/build/_static/css/fonts/fontawesome-webfont.woff deleted file mode 100644 index 400014a..0000000 Binary files a/build/_static/css/fonts/fontawesome-webfont.woff and /dev/null differ diff --git a/build/_static/css/fonts/fontawesome-webfont.woff2 b/build/_static/css/fonts/fontawesome-webfont.woff2 deleted file mode 100644 index 4d13fc6..0000000 Binary files a/build/_static/css/fonts/fontawesome-webfont.woff2 and /dev/null differ diff --git a/build/_static/css/fonts/lato-bold-italic.woff b/build/_static/css/fonts/lato-bold-italic.woff deleted file mode 100644 index 88ad05b..0000000 Binary files a/build/_static/css/fonts/lato-bold-italic.woff and /dev/null differ diff --git a/build/_static/css/fonts/lato-bold-italic.woff2 b/build/_static/css/fonts/lato-bold-italic.woff2 deleted file mode 100644 index c4e3d80..0000000 Binary files a/build/_static/css/fonts/lato-bold-italic.woff2 and /dev/null differ diff --git a/build/_static/css/fonts/lato-bold.woff b/build/_static/css/fonts/lato-bold.woff deleted file mode 100644 index c6dff51..0000000 Binary files a/build/_static/css/fonts/lato-bold.woff and /dev/null differ diff --git a/build/_static/css/fonts/lato-bold.woff2 b/build/_static/css/fonts/lato-bold.woff2 deleted file mode 100644 index bb19504..0000000 Binary files a/build/_static/css/fonts/lato-bold.woff2 and /dev/null differ diff --git a/build/_static/css/fonts/lato-normal-italic.woff b/build/_static/css/fonts/lato-normal-italic.woff deleted file mode 100644 index 76114bc..0000000 Binary files a/build/_static/css/fonts/lato-normal-italic.woff and /dev/null differ diff --git a/build/_static/css/fonts/lato-normal-italic.woff2 b/build/_static/css/fonts/lato-normal-italic.woff2 deleted file mode 100644 index 3404f37..0000000 Binary files a/build/_static/css/fonts/lato-normal-italic.woff2 and /dev/null differ diff --git a/build/_static/css/fonts/lato-normal.woff b/build/_static/css/fonts/lato-normal.woff deleted file mode 100644 index ae1307f..0000000 Binary files a/build/_static/css/fonts/lato-normal.woff and /dev/null differ diff --git a/build/_static/css/fonts/lato-normal.woff2 b/build/_static/css/fonts/lato-normal.woff2 deleted file mode 100644 index 3bf9843..0000000 Binary files a/build/_static/css/fonts/lato-normal.woff2 and /dev/null differ diff --git a/build/_static/css/theme.css b/build/_static/css/theme.css deleted file mode 100644 index 19a446a..0000000 --- a/build/_static/css/theme.css +++ /dev/null @@ -1,4 +0,0 @@ -html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search>a:hover{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .citation-reference>span.fn-bracket,.rst-content .footnote-reference>span.fn-bracket{display:none}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:auto minmax(80%,95%)}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{display:inline-grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{display:grid;grid-template-columns:auto auto minmax(.65rem,auto) minmax(40%,95%)}html.writer-html5 .rst-content aside.citation>span.label,html.writer-html5 .rst-content aside.footnote>span.label,html.writer-html5 .rst-content div.citation>span.label{grid-column-start:1;grid-column-end:2}html.writer-html5 .rst-content aside.citation>span.backrefs,html.writer-html5 .rst-content aside.footnote>span.backrefs,html.writer-html5 .rst-content div.citation>span.backrefs{grid-column-start:2;grid-column-end:3;grid-row-start:1;grid-row-end:3}html.writer-html5 .rst-content aside.citation>p,html.writer-html5 .rst-content aside.footnote>p,html.writer-html5 .rst-content div.citation>p{grid-column-start:4;grid-column-end:5}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{margin-bottom:24px}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a{word-break:keep-all}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{padding-left:1rem;padding-right:1rem;font-size:.9rem;line-height:1.2rem}html.writer-html5 .rst-content aside.citation p,html.writer-html5 .rst-content aside.footnote p,html.writer-html5 .rst-content div.citation p{font-size:.9rem;line-height:1.2rem;margin-bottom:12px}html.writer-html5 .rst-content aside.citation span.backrefs,html.writer-html5 .rst-content aside.footnote span.backrefs,html.writer-html5 .rst-content div.citation span.backrefs{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content aside.citation span.backrefs>a,html.writer-html5 .rst-content aside.footnote span.backrefs>a,html.writer-html5 .rst-content div.citation span.backrefs>a{word-break:keep-all}html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content aside.citation span.label,html.writer-html5 .rst-content aside.footnote span.label,html.writer-html5 .rst-content div.citation span.label{line-height:1.2rem}html.writer-html5 .rst-content aside.citation-list,html.writer-html5 .rst-content aside.footnote-list,html.writer-html5 .rst-content div.citation-list{margin-bottom:24px}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content aside.footnote-list aside.footnote,html.writer-html5 .rst-content div.citation-list>div.citation,html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content aside.footnote-list aside.footnote code,html.writer-html5 .rst-content aside.footnote-list aside.footnote tt,html.writer-html5 .rst-content aside.footnote code,html.writer-html5 .rst-content aside.footnote tt,html.writer-html5 .rst-content div.citation-list>div.citation code,html.writer-html5 .rst-content div.citation-list>div.citation tt,html.writer-html5 .rst-content dl.citation code,html.writer-html5 .rst-content dl.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel,.rst-content .menuselection{font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .guilabel,.rst-content .menuselection{border:1px solid #7fbbe3;background:#e7f2fa}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} \ No newline at end of file diff --git a/build/_static/doctools.js b/build/_static/doctools.js deleted file mode 100644 index d06a71d..0000000 --- a/build/_static/doctools.js +++ /dev/null @@ -1,156 +0,0 @@ -/* - * doctools.js - * ~~~~~~~~~~~ - * - * Base JavaScript utilities for all Sphinx HTML documentation. - * - * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ -"use strict"; - -const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ - "TEXTAREA", - "INPUT", - "SELECT", - "BUTTON", -]); - -const _ready = (callback) => { - if (document.readyState !== "loading") { - callback(); - } else { - document.addEventListener("DOMContentLoaded", callback); - } -}; - -/** - * Small JavaScript module for the documentation. - */ -const Documentation = { - init: () => { - Documentation.initDomainIndexTable(); - Documentation.initOnKeyListeners(); - }, - - /** - * i18n support - */ - TRANSLATIONS: {}, - PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), - LOCALE: "unknown", - - // gettext and ngettext don't access this so that the functions - // can safely bound to a different name (_ = Documentation.gettext) - gettext: (string) => { - const translated = Documentation.TRANSLATIONS[string]; - switch (typeof translated) { - case "undefined": - return string; // no translation - case "string": - return translated; // translation exists - default: - return translated[0]; // (singular, plural) translation tuple exists - } - }, - - ngettext: (singular, plural, n) => { - const translated = Documentation.TRANSLATIONS[singular]; - if (typeof translated !== "undefined") - return translated[Documentation.PLURAL_EXPR(n)]; - return n === 1 ? singular : plural; - }, - - addTranslations: (catalog) => { - Object.assign(Documentation.TRANSLATIONS, catalog.messages); - Documentation.PLURAL_EXPR = new Function( - "n", - `return (${catalog.plural_expr})` - ); - Documentation.LOCALE = catalog.locale; - }, - - /** - * helper function to focus on search bar - */ - focusSearchBar: () => { - document.querySelectorAll("input[name=q]")[0]?.focus(); - }, - - /** - * Initialise the domain index toggle buttons - */ - initDomainIndexTable: () => { - const toggler = (el) => { - const idNumber = el.id.substr(7); - const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); - if (el.src.substr(-9) === "minus.png") { - el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; - toggledRows.forEach((el) => (el.style.display = "none")); - } else { - el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; - toggledRows.forEach((el) => (el.style.display = "")); - } - }; - - const togglerElements = document.querySelectorAll("img.toggler"); - togglerElements.forEach((el) => - el.addEventListener("click", (event) => toggler(event.currentTarget)) - ); - togglerElements.forEach((el) => (el.style.display = "")); - if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); - }, - - initOnKeyListeners: () => { - // only install a listener if it is really needed - if ( - !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && - !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS - ) - return; - - document.addEventListener("keydown", (event) => { - // bail for input elements - if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; - // bail with special keys - if (event.altKey || event.ctrlKey || event.metaKey) return; - - if (!event.shiftKey) { - switch (event.key) { - case "ArrowLeft": - if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; - - const prevLink = document.querySelector('link[rel="prev"]'); - if (prevLink && prevLink.href) { - window.location.href = prevLink.href; - event.preventDefault(); - } - break; - case "ArrowRight": - if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; - - const nextLink = document.querySelector('link[rel="next"]'); - if (nextLink && nextLink.href) { - window.location.href = nextLink.href; - event.preventDefault(); - } - break; - } - } - - // some keyboard layouts may need Shift to get / - switch (event.key) { - case "/": - if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; - Documentation.focusSearchBar(); - event.preventDefault(); - } - }); - }, -}; - -// quick alias for translations -const _ = Documentation.gettext; - -_ready(Documentation.init); diff --git a/build/_static/documentation_options.js b/build/_static/documentation_options.js deleted file mode 100644 index ec8cedd..0000000 --- a/build/_static/documentation_options.js +++ /dev/null @@ -1,14 +0,0 @@ -var DOCUMENTATION_OPTIONS = { - URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), - VERSION: '0.0.0', - LANGUAGE: 'en', - COLLAPSE_INDEX: false, - BUILDER: 'html', - FILE_SUFFIX: '.html', - LINK_SUFFIX: '.html', - HAS_SOURCE: true, - SOURCELINK_SUFFIX: '.txt', - NAVIGATION_WITH_KEYS: false, - SHOW_SEARCH_SUMMARY: true, - ENABLE_SEARCH_SHORTCUTS: true, -}; \ No newline at end of file diff --git a/build/_static/file.png b/build/_static/file.png deleted file mode 100644 index a858a41..0000000 Binary files a/build/_static/file.png and /dev/null differ diff --git a/build/_static/jquery.js b/build/_static/jquery.js deleted file mode 100644 index c4c6022..0000000 --- a/build/_static/jquery.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/build/_static/js/html5shiv.min.js b/build/_static/js/html5shiv.min.js deleted file mode 100644 index cd1c674..0000000 --- a/build/_static/js/html5shiv.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/** -* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed -*/ -!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/build/_static/js/theme.js b/build/_static/js/theme.js deleted file mode 100644 index 1fddb6e..0000000 --- a/build/_static/js/theme.js +++ /dev/null @@ -1 +0,0 @@ -!function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0 - var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 - var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 - var s_v = "^(" + C + ")?" + v; // vowel in stem - - this.stemWord = function (w) { - var stem; - var suffix; - var firstch; - var origword = w; - - if (w.length < 3) - return w; - - var re; - var re2; - var re3; - var re4; - - firstch = w.substr(0,1); - if (firstch == "y") - w = firstch.toUpperCase() + w.substr(1); - - // Step 1a - re = /^(.+?)(ss|i)es$/; - re2 = /^(.+?)([^s])s$/; - - if (re.test(w)) - w = w.replace(re,"$1$2"); - else if (re2.test(w)) - w = w.replace(re2,"$1$2"); - - // Step 1b - re = /^(.+?)eed$/; - re2 = /^(.+?)(ed|ing)$/; - if (re.test(w)) { - var fp = re.exec(w); - re = new RegExp(mgr0); - if (re.test(fp[1])) { - re = /.$/; - w = w.replace(re,""); - } - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1]; - re2 = new RegExp(s_v); - if (re2.test(stem)) { - w = stem; - re2 = /(at|bl|iz)$/; - re3 = new RegExp("([^aeiouylsz])\\1$"); - re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re2.test(w)) - w = w + "e"; - else if (re3.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - else if (re4.test(w)) - w = w + "e"; - } - } - - // Step 1c - re = /^(.+?)y$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(s_v); - if (re.test(stem)) - w = stem + "i"; - } - - // Step 2 - re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step2list[suffix]; - } - - // Step 3 - re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step3list[suffix]; - } - - // Step 4 - re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; - re2 = /^(.+?)(s|t)(ion)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - if (re.test(stem)) - w = stem; - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1] + fp[2]; - re2 = new RegExp(mgr1); - if (re2.test(stem)) - w = stem; - } - - // Step 5 - re = /^(.+?)e$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - re2 = new RegExp(meq1); - re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) - w = stem; - } - re = /ll$/; - re2 = new RegExp(mgr1); - if (re.test(w) && re2.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - - // and turn initial Y back to y - if (firstch == "y") - w = firstch.toLowerCase() + w.substr(1); - return w; - } -} - diff --git a/build/_static/minus.png b/build/_static/minus.png deleted file mode 100644 index d96755f..0000000 Binary files a/build/_static/minus.png and /dev/null differ diff --git a/build/_static/plus.png b/build/_static/plus.png deleted file mode 100644 index 7107cec..0000000 Binary files a/build/_static/plus.png and /dev/null differ diff --git a/build/_static/pygments.css b/build/_static/pygments.css deleted file mode 100644 index 08bec68..0000000 --- a/build/_static/pygments.css +++ /dev/null @@ -1,74 +0,0 @@ -pre { line-height: 125%; } -td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -.highlight .hll { background-color: #ffffcc } -.highlight { background: #f8f8f8; } -.highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ -.highlight .err { border: 1px solid #FF0000 } /* Error */ -.highlight .k { color: #008000; font-weight: bold } /* Keyword */ -.highlight .o { color: #666666 } /* Operator */ -.highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ -.highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #9C6500 } /* Comment.Preproc */ -.highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ -.highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ -.highlight .gd { color: #A00000 } /* Generic.Deleted */ -.highlight .ge { font-style: italic } /* Generic.Emph */ -.highlight .gr { color: #E40000 } /* Generic.Error */ -.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.highlight .gi { color: #008400 } /* Generic.Inserted */ -.highlight .go { color: #717171 } /* Generic.Output */ -.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ -.highlight .gs { font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.highlight .gt { color: #0044DD } /* Generic.Traceback */ -.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ -.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { color: #008000 } /* Keyword.Pseudo */ -.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #B00040 } /* Keyword.Type */ -.highlight .m { color: #666666 } /* Literal.Number */ -.highlight .s { color: #BA2121 } /* Literal.String */ -.highlight .na { color: #687822 } /* Name.Attribute */ -.highlight .nb { color: #008000 } /* Name.Builtin */ -.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ -.highlight .no { color: #880000 } /* Name.Constant */ -.highlight .nd { color: #AA22FF } /* Name.Decorator */ -.highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ -.highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ -.highlight .nf { color: #0000FF } /* Name.Function */ -.highlight .nl { color: #767600 } /* Name.Label */ -.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ -.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ -.highlight .nv { color: #19177C } /* Name.Variable */ -.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ -.highlight .w { color: #bbbbbb } /* Text.Whitespace */ -.highlight .mb { color: #666666 } /* Literal.Number.Bin */ -.highlight .mf { color: #666666 } /* Literal.Number.Float */ -.highlight .mh { color: #666666 } /* Literal.Number.Hex */ -.highlight .mi { color: #666666 } /* Literal.Number.Integer */ -.highlight .mo { color: #666666 } /* Literal.Number.Oct */ -.highlight .sa { color: #BA2121 } /* Literal.String.Affix */ -.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ -.highlight .sc { color: #BA2121 } /* Literal.String.Char */ -.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ -.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ -.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ -.highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ -.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ -.highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ -.highlight .sx { color: #008000 } /* Literal.String.Other */ -.highlight .sr { color: #A45A77 } /* Literal.String.Regex */ -.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ -.highlight .ss { color: #19177C } /* Literal.String.Symbol */ -.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ -.highlight .fm { color: #0000FF } /* Name.Function.Magic */ -.highlight .vc { color: #19177C } /* Name.Variable.Class */ -.highlight .vg { color: #19177C } /* Name.Variable.Global */ -.highlight .vi { color: #19177C } /* Name.Variable.Instance */ -.highlight .vm { color: #19177C } /* Name.Variable.Magic */ -.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/build/_static/searchtools.js b/build/_static/searchtools.js deleted file mode 100644 index 97d56a7..0000000 --- a/build/_static/searchtools.js +++ /dev/null @@ -1,566 +0,0 @@ -/* - * searchtools.js - * ~~~~~~~~~~~~~~~~ - * - * Sphinx JavaScript utilities for the full-text search. - * - * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ -"use strict"; - -/** - * Simple result scoring code. - */ -if (typeof Scorer === "undefined") { - var Scorer = { - // Implement the following function to further tweak the score for each result - // The function takes a result array [docname, title, anchor, descr, score, filename] - // and returns the new score. - /* - score: result => { - const [docname, title, anchor, descr, score, filename] = result - return score - }, - */ - - // query matches the full name of an object - objNameMatch: 11, - // or matches in the last dotted part of the object name - objPartialMatch: 6, - // Additive scores depending on the priority of the object - objPrio: { - 0: 15, // used to be importantResults - 1: 5, // used to be objectResults - 2: -5, // used to be unimportantResults - }, - // Used when the priority is not in the mapping. - objPrioDefault: 0, - - // query found in title - title: 15, - partialTitle: 7, - // query found in terms - term: 5, - partialTerm: 2, - }; -} - -const _removeChildren = (element) => { - while (element && element.lastChild) element.removeChild(element.lastChild); -}; - -/** - * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping - */ -const _escapeRegExp = (string) => - string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string - -const _displayItem = (item, searchTerms) => { - const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; - const docUrlRoot = DOCUMENTATION_OPTIONS.URL_ROOT; - const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; - const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; - const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; - - const [docName, title, anchor, descr, score, _filename] = item; - - let listItem = document.createElement("li"); - let requestUrl; - let linkUrl; - if (docBuilder === "dirhtml") { - // dirhtml builder - let dirname = docName + "/"; - if (dirname.match(/\/index\/$/)) - dirname = dirname.substring(0, dirname.length - 6); - else if (dirname === "index/") dirname = ""; - requestUrl = docUrlRoot + dirname; - linkUrl = requestUrl; - } else { - // normal html builders - requestUrl = docUrlRoot + docName + docFileSuffix; - linkUrl = docName + docLinkSuffix; - } - let linkEl = listItem.appendChild(document.createElement("a")); - linkEl.href = linkUrl + anchor; - linkEl.dataset.score = score; - linkEl.innerHTML = title; - if (descr) - listItem.appendChild(document.createElement("span")).innerHTML = - " (" + descr + ")"; - else if (showSearchSummary) - fetch(requestUrl) - .then((responseData) => responseData.text()) - .then((data) => { - if (data) - listItem.appendChild( - Search.makeSearchSummary(data, searchTerms) - ); - }); - Search.output.appendChild(listItem); -}; -const _finishSearch = (resultCount) => { - Search.stopPulse(); - Search.title.innerText = _("Search Results"); - if (!resultCount) - Search.status.innerText = Documentation.gettext( - "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." - ); - else - Search.status.innerText = _( - `Search finished, found ${resultCount} page(s) matching the search query.` - ); -}; -const _displayNextItem = ( - results, - resultCount, - searchTerms -) => { - // results left, load the summary and display it - // this is intended to be dynamic (don't sub resultsCount) - if (results.length) { - _displayItem(results.pop(), searchTerms); - setTimeout( - () => _displayNextItem(results, resultCount, searchTerms), - 5 - ); - } - // search finished, update title and status message - else _finishSearch(resultCount); -}; - -/** - * Default splitQuery function. Can be overridden in ``sphinx.search`` with a - * custom function per language. - * - * The regular expression works by splitting the string on consecutive characters - * that are not Unicode letters, numbers, underscores, or emoji characters. - * This is the same as ``\W+`` in Python, preserving the surrogate pair area. - */ -if (typeof splitQuery === "undefined") { - var splitQuery = (query) => query - .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) - .filter(term => term) // remove remaining empty strings -} - -/** - * Search Module - */ -const Search = { - _index: null, - _queued_query: null, - _pulse_status: -1, - - htmlToText: (htmlString) => { - const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); - htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() }); - const docContent = htmlElement.querySelector('[role="main"]'); - if (docContent !== undefined) return docContent.textContent; - console.warn( - "Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template." - ); - return ""; - }, - - init: () => { - const query = new URLSearchParams(window.location.search).get("q"); - document - .querySelectorAll('input[name="q"]') - .forEach((el) => (el.value = query)); - if (query) Search.performSearch(query); - }, - - loadIndex: (url) => - (document.body.appendChild(document.createElement("script")).src = url), - - setIndex: (index) => { - Search._index = index; - if (Search._queued_query !== null) { - const query = Search._queued_query; - Search._queued_query = null; - Search.query(query); - } - }, - - hasIndex: () => Search._index !== null, - - deferQuery: (query) => (Search._queued_query = query), - - stopPulse: () => (Search._pulse_status = -1), - - startPulse: () => { - if (Search._pulse_status >= 0) return; - - const pulse = () => { - Search._pulse_status = (Search._pulse_status + 1) % 4; - Search.dots.innerText = ".".repeat(Search._pulse_status); - if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); - }; - pulse(); - }, - - /** - * perform a search for something (or wait until index is loaded) - */ - performSearch: (query) => { - // create the required interface elements - const searchText = document.createElement("h2"); - searchText.textContent = _("Searching"); - const searchSummary = document.createElement("p"); - searchSummary.classList.add("search-summary"); - searchSummary.innerText = ""; - const searchList = document.createElement("ul"); - searchList.classList.add("search"); - - const out = document.getElementById("search-results"); - Search.title = out.appendChild(searchText); - Search.dots = Search.title.appendChild(document.createElement("span")); - Search.status = out.appendChild(searchSummary); - Search.output = out.appendChild(searchList); - - const searchProgress = document.getElementById("search-progress"); - // Some themes don't use the search progress node - if (searchProgress) { - searchProgress.innerText = _("Preparing search..."); - } - Search.startPulse(); - - // index already loaded, the browser was quick! - if (Search.hasIndex()) Search.query(query); - else Search.deferQuery(query); - }, - - /** - * execute search (requires search index to be loaded) - */ - query: (query) => { - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const titles = Search._index.titles; - const allTitles = Search._index.alltitles; - const indexEntries = Search._index.indexentries; - - // stem the search terms and add them to the correct list - const stemmer = new Stemmer(); - const searchTerms = new Set(); - const excludedTerms = new Set(); - const highlightTerms = new Set(); - const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); - splitQuery(query.trim()).forEach((queryTerm) => { - const queryTermLower = queryTerm.toLowerCase(); - - // maybe skip this "word" - // stopwords array is from language_data.js - if ( - stopwords.indexOf(queryTermLower) !== -1 || - queryTerm.match(/^\d+$/) - ) - return; - - // stem the word - let word = stemmer.stemWord(queryTermLower); - // select the correct list - if (word[0] === "-") excludedTerms.add(word.substr(1)); - else { - searchTerms.add(word); - highlightTerms.add(queryTermLower); - } - }); - - if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js - localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) - } - - // console.debug("SEARCH: searching for:"); - // console.info("required: ", [...searchTerms]); - // console.info("excluded: ", [...excludedTerms]); - - // array of [docname, title, anchor, descr, score, filename] - let results = []; - _removeChildren(document.getElementById("search-progress")); - - const queryLower = query.toLowerCase(); - for (const [title, foundTitles] of Object.entries(allTitles)) { - if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) { - for (const [file, id] of foundTitles) { - let score = Math.round(100 * queryLower.length / title.length) - results.push([ - docNames[file], - titles[file] !== title ? `${titles[file]} > ${title}` : title, - id !== null ? "#" + id : "", - null, - score, - filenames[file], - ]); - } - } - } - - // search for explicit entries in index directives - for (const [entry, foundEntries] of Object.entries(indexEntries)) { - if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { - for (const [file, id] of foundEntries) { - let score = Math.round(100 * queryLower.length / entry.length) - results.push([ - docNames[file], - titles[file], - id ? "#" + id : "", - null, - score, - filenames[file], - ]); - } - } - } - - // lookup as object - objectTerms.forEach((term) => - results.push(...Search.performObjectSearch(term, objectTerms)) - ); - - // lookup as search terms in fulltext - results.push(...Search.performTermsSearch(searchTerms, excludedTerms)); - - // let the scorer override scores with a custom scoring function - if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item))); - - // now sort the results by score (in opposite order of appearance, since the - // display function below uses pop() to retrieve items) and then - // alphabetically - results.sort((a, b) => { - const leftScore = a[4]; - const rightScore = b[4]; - if (leftScore === rightScore) { - // same score: sort alphabetically - const leftTitle = a[1].toLowerCase(); - const rightTitle = b[1].toLowerCase(); - if (leftTitle === rightTitle) return 0; - return leftTitle > rightTitle ? -1 : 1; // inverted is intentional - } - return leftScore > rightScore ? 1 : -1; - }); - - // remove duplicate search results - // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept - let seen = new Set(); - results = results.reverse().reduce((acc, result) => { - let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); - if (!seen.has(resultStr)) { - acc.push(result); - seen.add(resultStr); - } - return acc; - }, []); - - results = results.reverse(); - - // for debugging - //Search.lastresults = results.slice(); // a copy - // console.info("search results:", Search.lastresults); - - // print the results - _displayNextItem(results, results.length, searchTerms); - }, - - /** - * search for object names - */ - performObjectSearch: (object, objectTerms) => { - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const objects = Search._index.objects; - const objNames = Search._index.objnames; - const titles = Search._index.titles; - - const results = []; - - const objectSearchCallback = (prefix, match) => { - const name = match[4] - const fullname = (prefix ? prefix + "." : "") + name; - const fullnameLower = fullname.toLowerCase(); - if (fullnameLower.indexOf(object) < 0) return; - - let score = 0; - const parts = fullnameLower.split("."); - - // check for different match types: exact matches of full name or - // "last name" (i.e. last dotted part) - if (fullnameLower === object || parts.slice(-1)[0] === object) - score += Scorer.objNameMatch; - else if (parts.slice(-1)[0].indexOf(object) > -1) - score += Scorer.objPartialMatch; // matches in last name - - const objName = objNames[match[1]][2]; - const title = titles[match[0]]; - - // If more than one term searched for, we require other words to be - // found in the name/title/description - const otherTerms = new Set(objectTerms); - otherTerms.delete(object); - if (otherTerms.size > 0) { - const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); - if ( - [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) - ) - return; - } - - let anchor = match[3]; - if (anchor === "") anchor = fullname; - else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; - - const descr = objName + _(", in ") + title; - - // add custom score for some objects according to scorer - if (Scorer.objPrio.hasOwnProperty(match[2])) - score += Scorer.objPrio[match[2]]; - else score += Scorer.objPrioDefault; - - results.push([ - docNames[match[0]], - fullname, - "#" + anchor, - descr, - score, - filenames[match[0]], - ]); - }; - Object.keys(objects).forEach((prefix) => - objects[prefix].forEach((array) => - objectSearchCallback(prefix, array) - ) - ); - return results; - }, - - /** - * search for full-text terms in the index - */ - performTermsSearch: (searchTerms, excludedTerms) => { - // prepare search - const terms = Search._index.terms; - const titleTerms = Search._index.titleterms; - const filenames = Search._index.filenames; - const docNames = Search._index.docnames; - const titles = Search._index.titles; - - const scoreMap = new Map(); - const fileMap = new Map(); - - // perform the search on the required terms - searchTerms.forEach((word) => { - const files = []; - const arr = [ - { files: terms[word], score: Scorer.term }, - { files: titleTerms[word], score: Scorer.title }, - ]; - // add support for partial matches - if (word.length > 2) { - const escapedWord = _escapeRegExp(word); - Object.keys(terms).forEach((term) => { - if (term.match(escapedWord) && !terms[word]) - arr.push({ files: terms[term], score: Scorer.partialTerm }); - }); - Object.keys(titleTerms).forEach((term) => { - if (term.match(escapedWord) && !titleTerms[word]) - arr.push({ files: titleTerms[word], score: Scorer.partialTitle }); - }); - } - - // no match but word was a required one - if (arr.every((record) => record.files === undefined)) return; - - // found search word in contents - arr.forEach((record) => { - if (record.files === undefined) return; - - let recordFiles = record.files; - if (recordFiles.length === undefined) recordFiles = [recordFiles]; - files.push(...recordFiles); - - // set score for the word in each file - recordFiles.forEach((file) => { - if (!scoreMap.has(file)) scoreMap.set(file, {}); - scoreMap.get(file)[word] = record.score; - }); - }); - - // create the mapping - files.forEach((file) => { - if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1) - fileMap.get(file).push(word); - else fileMap.set(file, [word]); - }); - }); - - // now check if the files don't contain excluded terms - const results = []; - for (const [file, wordList] of fileMap) { - // check if all requirements are matched - - // as search terms with length < 3 are discarded - const filteredTermCount = [...searchTerms].filter( - (term) => term.length > 2 - ).length; - if ( - wordList.length !== searchTerms.size && - wordList.length !== filteredTermCount - ) - continue; - - // ensure that none of the excluded terms is in the search result - if ( - [...excludedTerms].some( - (term) => - terms[term] === file || - titleTerms[term] === file || - (terms[term] || []).includes(file) || - (titleTerms[term] || []).includes(file) - ) - ) - break; - - // select one (max) score for the file. - const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); - // add result to the result list - results.push([ - docNames[file], - titles[file], - "", - null, - score, - filenames[file], - ]); - } - return results; - }, - - /** - * helper function to return a node containing the - * search summary for a given text. keywords is a list - * of stemmed words. - */ - makeSearchSummary: (htmlText, keywords) => { - const text = Search.htmlToText(htmlText); - if (text === "") return null; - - const textLower = text.toLowerCase(); - const actualStartPosition = [...keywords] - .map((k) => textLower.indexOf(k.toLowerCase())) - .filter((i) => i > -1) - .slice(-1)[0]; - const startWithContext = Math.max(actualStartPosition - 120, 0); - - const top = startWithContext === 0 ? "" : "..."; - const tail = startWithContext + 240 < text.length ? "..." : ""; - - let summary = document.createElement("p"); - summary.classList.add("context"); - summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; - - return summary; - }, -}; - -_ready(Search.init); diff --git a/build/_static/sphinx_highlight.js b/build/_static/sphinx_highlight.js deleted file mode 100644 index aae669d..0000000 --- a/build/_static/sphinx_highlight.js +++ /dev/null @@ -1,144 +0,0 @@ -/* Highlighting utilities for Sphinx HTML documentation. */ -"use strict"; - -const SPHINX_HIGHLIGHT_ENABLED = true - -/** - * highlight a given string on a node by wrapping it in - * span elements with the given class name. - */ -const _highlight = (node, addItems, text, className) => { - if (node.nodeType === Node.TEXT_NODE) { - const val = node.nodeValue; - const parent = node.parentNode; - const pos = val.toLowerCase().indexOf(text); - if ( - pos >= 0 && - !parent.classList.contains(className) && - !parent.classList.contains("nohighlight") - ) { - let span; - - const closestNode = parent.closest("body, svg, foreignObject"); - const isInSVG = closestNode && closestNode.matches("svg"); - if (isInSVG) { - span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); - } else { - span = document.createElement("span"); - span.classList.add(className); - } - - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - parent.insertBefore( - span, - parent.insertBefore( - document.createTextNode(val.substr(pos + text.length)), - node.nextSibling - ) - ); - node.nodeValue = val.substr(0, pos); - - if (isInSVG) { - const rect = document.createElementNS( - "http://www.w3.org/2000/svg", - "rect" - ); - const bbox = parent.getBBox(); - rect.x.baseVal.value = bbox.x; - rect.y.baseVal.value = bbox.y; - rect.width.baseVal.value = bbox.width; - rect.height.baseVal.value = bbox.height; - rect.setAttribute("class", className); - addItems.push({ parent: parent, target: rect }); - } - } - } else if (node.matches && !node.matches("button, select, textarea")) { - node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); - } -}; -const _highlightText = (thisNode, text, className) => { - let addItems = []; - _highlight(thisNode, addItems, text, className); - addItems.forEach((obj) => - obj.parent.insertAdjacentElement("beforebegin", obj.target) - ); -}; - -/** - * Small JavaScript module for the documentation. - */ -const SphinxHighlight = { - - /** - * highlight the search words provided in localstorage in the text - */ - highlightSearchWords: () => { - if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight - - // get and clear terms from localstorage - const url = new URL(window.location); - const highlight = - localStorage.getItem("sphinx_highlight_terms") - || url.searchParams.get("highlight") - || ""; - localStorage.removeItem("sphinx_highlight_terms") - url.searchParams.delete("highlight"); - window.history.replaceState({}, "", url); - - // get individual terms from highlight string - const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); - if (terms.length === 0) return; // nothing to do - - // There should never be more than one element matching "div.body" - const divBody = document.querySelectorAll("div.body"); - const body = divBody.length ? divBody[0] : document.querySelector("body"); - window.setTimeout(() => { - terms.forEach((term) => _highlightText(body, term, "highlighted")); - }, 10); - - const searchBox = document.getElementById("searchbox"); - if (searchBox === null) return; - searchBox.appendChild( - document - .createRange() - .createContextualFragment( - '" - ) - ); - }, - - /** - * helper function to hide the search marks again - */ - hideSearchWords: () => { - document - .querySelectorAll("#searchbox .highlight-link") - .forEach((el) => el.remove()); - document - .querySelectorAll("span.highlighted") - .forEach((el) => el.classList.remove("highlighted")); - localStorage.removeItem("sphinx_highlight_terms") - }, - - initEscapeListener: () => { - // only install a listener if it is really needed - if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; - - document.addEventListener("keydown", (event) => { - // bail for input elements - if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; - // bail with special keys - if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; - if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { - SphinxHighlight.hideSearchWords(); - event.preventDefault(); - } - }); - }, -}; - -_ready(SphinxHighlight.highlightSearchWords); -_ready(SphinxHighlight.initEscapeListener); diff --git a/build/asimplify.html b/build/asimplify.html deleted file mode 100644 index 970ab7f..0000000 --- a/build/asimplify.html +++ /dev/null @@ -1,354 +0,0 @@ - - - - - - - Developing Custom Scripts — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

Developing Custom Scripts

-

This section will guide you through taking your own in-house simulation and -integrating it in to asimtools. The process is designed to be as -straight-forward as reasonably possible.

-

As an example, we will ASIMplify the calculation of adsorption_energies as -shown in ASE. To understand the code, visit the ASE tutorials page <https://wiki.fysik.dtu.dk/ase/tutorials/db/db.html>. Ultimately, the -ASIMplified code will work with any element and , any calculator and any -environment and be fully parallelized job submission in just a few steps!

-

Use toy example To make things easier, it is best to first replace the -expensive parts of your workflow with toy examples e.g. use an empirical -calculator like EAM instead of DFT, use a smaller/simpler structure, loop -over fewer cases etc.

-

The ASE example already uses a simple EMT calculator and structure which is -provided here for reference:

-
from ase.build import bulk
-from ase.calculators.emt import EMT
-from ase.eos import calculate_eos
-from ase.db import connect
-
-db = connect('bulk.db')
-for symb in ['Al', 'Ni', 'Cu', 'Pd', 'Ag', 'Pt', 'Au']:
-    atoms = bulk(symb, 'fcc')
-    atoms.calc = EMT()
-    eos = calculate_eos(atoms)
-    v, e, B = eos.fit()  # find minimum
-    # Do one more calculation at the minimu and write to database:
-    atoms.cell *= (v / atoms.get_volume())**(1 / 3)
-    atoms.get_potential_energy()
-    db.write(atoms, bm=B)
-
-
-

Wrap in a function The workhorse of ASIMTools is the function, any code -wrapped in a function that returns a dictionary can be run within the -ASIMTools framework. The easiest thing to do would be to take the code and -copy and paste it in a function which is defined inside a script with the -same name

-
from ase.build import bulk
-from ase.calculators.emt import EMT
-from ase.eos import calculate_eos
-from ase.db import connect
-
-def ase_eos():
-    db = connect('bulk.db')
-    for symb in ['Al', 'Ni', 'Cu', 'Pd', 'Ag', 'Pt', 'Au']:
-        atoms = bulk(symb, 'fcc')
-        atoms.calc = EMT()
-        eos = calculate_eos(atoms)
-        v, e, B = eos.fit()  # find minimum
-        # Do one more calculation at the minimu and write to database:
-        atoms.cell *= (v / atoms.get_volume())**(1 / 3)
-        atoms.get_potential_energy()
-        db.write(atoms, bm=B)
-
-    return {}
-
-
-

Immediately as it is, this script can be run in ASIMTools. You can run it with -the following sim_input.yaml

-
script: /path/to/ase_eos.py
-env_id: inline
-workdir: results
-
-
-

then call

-
asim-execute sim_input.yaml
-
-
-

Only a little bit more complicated than calling python ase_eos.py -This script however still depends on specific structures and a specific -calculator. Let’s do the easy thing first, let’s make the script work with any -calculator using a simple change.

-
from ase.build import bulk
-from ase.eos import calculate_eos
-from ase.db import connect
-from asimtools.calculators import load_calc
-
-def ase_eos(
-    calc_id,
-):
-    calc = load_calc(calc_id)
-    db = connect('bulk.db')
-    for symb in ['Al', 'Ni', 'Cu', 'Pd', 'Ag', 'Pt', 'Au']:
-        atoms = bulk(symb, 'fcc')
-        atoms.calc = calc
-        eos = calculate_eos(atoms)
-        v, e, B = eos.fit()  # find minimum
-        # Do one more calculation at the minimu and write to database:
-        atoms.cell *= (v / atoms.get_volume())**(1 / 3)
-        atoms.get_potential_energy()
-        db.write(atoms, bm=B)
-
-    return {}
-
-
-

Just like that we can now run the script with any correctly configure -calculator for the all the structures! We can even now run calc_array to -iterate getting the results using different calculators.

-

The final change we will make is to parallelize over structures as below

-
from ase.build import bulk
-from ase.eos import calculate_eos
-from ase.db import connect
-from asimtools.calculators import load_calc
-
-def ase_eos(
-    image,
-    calc_id,
-):
-    calc = load_calc(calc_id)
-    db = connect('bulk.db')
-    atoms = get_atoms(**image)
-    atoms.calc = calc
-    eos = calculate_eos(atoms)
-    v, e, B = eos.fit()  # find minimum
-    # Do one more calculation at the minimu and write to database:
-    atoms.cell *= (v / atoms.get_volume())**(1 / 3)
-    atoms.get_potential_energy()
-    db.write(atoms, bm=B)
-
-    return {}
-
-
-

Easy-peasy. We now have a script that works with arbitrary environment, -arbitrary calculator and arbitrary input structure (Of course the simulation -will fail if we give a bad structure for example)

-

We can do some final cleanup of the script so that it sends outputs to -output.yaml and logs some checkpoints. Additionally, any scripts added to -the repository will need clear syntax highlighting and documentation.

-
from typing import Dict
-import logging
-from ase.eos import calculate_eos
-from ase.db import connect
-from asimtools.calculators import load_calc
-from asimtools.utils import get_atoms
-
-def ase_eos(
-    image: Dict,
-    calc_id: str,
-    db_file: 'bulk.db'
-) -> Dict:
-    calc = load_calc(calc_id)
-    db = connect(db_file)
-    atoms = get_atoms(**image)
-    atoms.calc = calc
-    eos = calculate_eos(atoms)
-    v, e, B = eos.fit()  # find minimum
-    logging.info('Successfully fit EOS')
-    # Do one more calculation at the minimu and write to database:
-    atoms.cell *= (v / atoms.get_volume())**(1 / 3)
-    atoms.get_potential_energy()
-    db.write(atoms, bm=B)
-
-    results = {'v': float(v), 'e': float(e), 'B': float(B)}
-    return results
-
-
-

To run this script on an arbitrary structure say Argon with say the -LennardJones calculator, in a slurm job we can now use the following input -files.

-

sim_input.yaml:

-
script: /path/to/ase_eos.py
-env_id: batch
-workdir: results
-args:
-    image:
-        builder: bulk
-        name: Ar
-    calc_id: gpaw
-
-
-

calc_input.yaml:

-
lj:
-    name: LennardJones
-    module: ase.calculators.lj
-    args:
-        sigma: 3.54
-        epsilon: 0.00802236
-emt:
-    name: EMT
-    module: ase.calculators.emt
-    args: {}
-
-
-

env_input.yaml:

-
batch:
-    mode:
-        use_slurm: true
-        interactive: false
-    slurm:
-        flags:
-            - -n 2
-        precommands:
-            - source ~/.bashrc
-            - conda activate asimtools
-inline:
-    mode:
-        use_slurm: false
-        interactive: true
-
-
-

Going back to the original problem, we wanted to run the simulation of mulitple -different elements with the EMT calculator. To achieve that in parallel, we can -nest the ase_eos script in a asimtools.scripts.sim_array.sim_array() script as follows

-

sim_input.yaml:

-
script: sim_array
-workdir: results
-args:
-    key_sequence: ['args', 'image', 'name']
-    array_values: ['Al', 'Ni', 'Cu', 'Pd', 'Ag', 'Pt', 'Au']
-    env_ids: 'batch'
-    template_sim_input:
-        script: /path/to/ase_eos.py
-        args:
-            calc_id: emt
-            image:
-                builder: bulk
-                crystalstructure: 'fcc'
-
-
-

To make the script easier to access without having to use the full path, you -can set the environment variable

-
export ASIMTOOLS_SCRIPT_DIR=/path/to/my/script/dir/
-
-
-

You can then move the ase_eos.py script into a directory called ase_eos -and place it in the script directory. This allows you to refer to scripts -prepended with the script dir as below

-
script: sim_array
-workdir: results
-args:
-    key_sequence: ['args', 'image', 'name']
-    array_values: ['Al', 'Ni', 'Cu', 'Pd', 'Ag', 'Pt', 'Au']
-    env_ids: 'batch'
-    template_sim_input:
-        script: ase_eos/ase_eos.py
-        args:
-            calc_id: emt
-            image:
-                builder: bulk
-                crystalstructure: 'fcc'
-
-
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/asimtools.html b/build/asimtools.html deleted file mode 100644 index 51721c1..0000000 --- a/build/asimtools.html +++ /dev/null @@ -1,819 +0,0 @@ - - - - - - - asimtools package — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

asimtools package

- -
-

asimtools.calculators module

-

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

-
-
-asimtools.calculators.load_ase_calc(calc_params)[source]
-

Load any builtin ASE calculator

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

Loads a calculator using given calc_id or calc_input

-
-
Parameters:
-
    -
  • calc_id (str, optional) – ID/key to use to load calculator from the supplied/global calc_input file, defaults to None

  • -
  • calc_input (Optional[Dict], optional) – calc_input dictionary for a single calculator , defaults to None

  • -
-
-
Returns:
-

ASE calculator instance

-
-
Return type:
-

ase.calculators.calculators.Calculator

-
-
-
- -
-
-asimtools.calculators.load_dp(calc_params)[source]
-

Load Deep Potential Calculator

-
-
Parameters:
-

calc_params (Dict) – args to pass to loader

-
-
Returns:
-

DP calculator

-
-
Return type:
-

deepmd.calculator.DP

-
-
-
- -
-
-asimtools.calculators.load_nequip(calc_params)[source]
-

Load NequIP or Allegro calculator

-
-
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 scripts, 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) 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() None[source]
-

Write input files to working directory

-
- -
-
-submit(**kwargs) None[source]
-

Submit a job using slurm, interactively or in the terminal

-
- -
-
-submit_array(array_max=None, dependency: List[str] | None = None, **kwargs) None | List[str][source]
-

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

-
- -
-
-submit_jobs(**kwargs) None | List[str][source]
-

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

-
- -
- -
-
-class asimtools.job.Job(sim_input: Dict, env_input: Dict | None = None, calc_input: Dict | None = None)[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 was started

-
- -
-
-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='job.log', level='info')[source]
-

Get the logger

-
- -
-
-get_output() Dict[source]
-

Get values in output.yaml

-
- -
-
-get_output_yaml() Dict[source]
-

Get current output file

-
- -
-
-get_sim_input() Dict[source]
-

Get simulation input

-
- -
-
-get_status(display=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_workdir(workdir) None[source]
-

Set working directory

-
- -
-
-start() None[source]
-

Updates the output to signal that the job was started

-
- -
-
-update_calc_input(new_params) None[source]
-

Update calculator parameters

-
- -
-
-update_env_input(new_params) 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) None[source]
-

Update simulation parameters

-
- -
-
-update_status(status: str) None[source]
-

Updates job status to specificied value

-
- -
- -
-
-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() 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 | None = None) None | List[str][source]
-

Submit a job using slurm, interactively or in the terminal

-
- -
- -
-
-asimtools.job.load_job_from_directory(workdir: str)[source]
-

Loads a job from a given directory

-
- -
-
-

asimtools.utils module

-

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

-
-
-asimtools.utils.change_dict_value(d: Dict, new_value, key_sequence: Sequence, return_copy: bool = True) Dict[source]
-

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

-
-
Parameters:
-
    -
  • d (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.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.get_atoms(image_file: str | None = None, builder: str = 'bulk', atoms: Atoms | None = None, repeat: Tuple[int, int, int] | None = None, rattle_stdev: float | None = None, **kwargs) Atoms[source]
-

Return an atoms object based on specified config. This is the -recommended way to load atoms objects. There are three options to specify: -#. image_file. -#. builder, kwargs. -#. atoms.

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

  • -
  • 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(), defaults to None

  • -
-
-
Returns:
-

One ase.Atoms instance

-
-
Return type:
-

Atoms

-
-
-
- -
-
-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

-
-
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 = ':', **kwargs) List[Atoms][source]
-
-
Return a list of atoms objects based on the input arguments. Options to specify are:
    -
  1. image_file

  2. -
  3. pattern

  4. -
  5. images

  6. -
-
-
-
-
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 one image from each file as in ase.io.read() without specifying an index

  • -
  • 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 ‘:’

  • -
-
-
Returns:
-

List of ase.Atoms

-
-
Return type:
-

List[Atoms]

-
-
-
- -
-
-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.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) 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.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_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

-
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/asimtools.scripts.eos.html b/build/asimtools.scripts.eos.html deleted file mode 100644 index 9647e5a..0000000 --- a/build/asimtools.scripts.eos.html +++ /dev/null @@ -1,180 +0,0 @@ - - - - - - - asimtools.scripts.eos package — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

asimtools.scripts.eos package

-
-

asimtools.scripts.eos.eos module

-

.Xauthority

-
-
-asimtools.scripts.eos.eos.eos(image: dict, singlepoint_env_id: str, calc_id: str, preprocess_env_id: str, nimages: int = 5, scale_range: Tuple[float] = (0.95, 1.05), postprocess: bool = True) Dict[source]
-

EOS calculation

-
- -
-
-

asimtools.scripts.eos.postprocess module

-

.Xauthority

-
-
-asimtools.scripts.eos.postprocess.postprocess(images: Dict | None = None) Tuple[None, Dict][source]
-

plot things

-
- -
-
-

asimtools.scripts.eos.preprocess module

-

.Xauthority

-
-
-asimtools.scripts.eos.preprocess.preprocess(image: Dict, nimages: int = 5, scale_range: Tuple[float, float] = (0.95, 1.05)) Dict[source]
-

Prepares structures for EOS calculation

-
- -
-
-

Module contents

-
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/asimtools.scripts.html b/build/asimtools.scripts.html deleted file mode 100644 index 5ee1734..0000000 --- a/build/asimtools.scripts.html +++ /dev/null @@ -1,670 +0,0 @@ - - - - - - - asimtools.scripts package — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

asimtools.scripts package

- -
-

asimtools.scripts.asim_check module

-

Recursively check progress of jobs starting from a specified sim_input.yaml -and pring out the progress

-
-
-asimtools.scripts.asim_check.get_status_and_color(job)[source]
-

Helper to get printing colors

-
- -
-
-asimtools.scripts.asim_check.get_subjobs(workdir)[source]
-

Get all the directories with jobs in them

-
-
Parameters:
-

workdir (_type_) – _description_

-
-
Returns:
-

_description_

-
-
Return type:
-

_type_

-
-
-
- -
-
-asimtools.scripts.asim_check.load_job_tree(workdir: str = './') 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.scripts.asim_check.main(args=None) None[source]
-

Main

-
-
Parameters:
-

args (_type_, optional) – cmdline args, defaults to None

-
-
-
- -
-
-asimtools.scripts.asim_check.parse_command_line(args) Tuple[Dict, str][source]
-

Parse command line input

-
- -
-
-asimtools.scripts.asim_check.print_job_tree(job_tree: Dict, indent_str='', level=0) None[source]
-

Prints the job tree given the job_tree dictionary

-
-
Parameters:
-
    -
  • job_tree (Dict) – dictionary from load_job_tree

  • -
  • indent (int, optional) – indentation of output, defaults to 0

  • -
-
-
-
- -
-
-

asimtools.scripts.asim_execute module

-

Execute a workflow given the sim_input.yaml and optionally, -a calc_input.yaml and/or env_input.yaml. The called script will be run -in the specified workdir and env_id

-

Author: mkphuthi@github.com

-
-
-asimtools.scripts.asim_execute.main(args=None) None[source]
-

Execute a workflow given the sim_input.yaml and optinally, -a calc_input.yaml and/or env_input.yaml. The called script will be run -in the specified workdir and env_id

-
- -
-
-asimtools.scripts.asim_execute.parse_command_line(args) Tuple[Dict, Dict][source]
-

Parse command line input

-
- -
-
-

asimtools.scripts.asim_run module

-

Execute a script given the sim_input.yaml and optionally, -a calc_input.yaml. The called script will be run directly in the current -directory and environment

-
-
-asimtools.scripts.asim_run.main(args=None) None[source]
-

Main

-
- -
-
-asimtools.scripts.asim_run.parse_command_line(args) Tuple[Dict, str][source]
-

Parse command line input

-
- -
-
-

asimtools.scripts.atom_relax module

-

Relaxes the given atomic structure using ASE’s built-in structure -optimizers

-
-
-asimtools.scripts.atom_relax.atom_relax(calc_id: str, image: Dict, optimizer: str = 'GPMin', properties: Tuple[str] = ('energy', 'forces'), fmax: float = 0.02) Dict[source]
-

Relaxes the given tomic structure using ASE’s built-in structure -optimizers

-
-
Parameters:
-
    -
  • calc_id (str) – Calculator ID

  • -
  • image (Dict) – image configuration

  • -
  • prefix (str, optional) – Prefix of output files, defaults to ‘’

  • -
  • optimizer (str, optional) – ASE Optimizer class, defaults to ‘GPMin’

  • -
  • fmax (float, optional) – Force convergence threshold in optimizer, defaults to 0.02

  • -
-
-
Returns:
-

Dictionary of results

-
-
Return type:
-

Dict

-
-
-
- -
-
-

asimtools.scripts.calc_array module

-

Apply the same script using multiple calculators

-

Author: mkphuthi@github.com

-
-
-asimtools.scripts.calc_array.calc_array(subscript_input: Dict, calc_ids: Sequence[str] | None = None, template_calc_id: str | None = None, key_sequence: Sequence[str] | None = None, array_values: Sequence | None = None, env_ids: Sequence[str] | str | None = None, calc_input: Dict | None = None, env_input: Dict | None = None, ids: Sequence | None = None) Dict[source]
-

Apply the same script using different calculators and if necessary -different environments

-
-
Parameters:
-
    -
  • calc_ids (Sequence, optional) – Iterable with calc_ids, defaults to None

  • -
  • calc_input (Dictionary, optional) – Dictionary of calculator inputs

  • -
  • ids (Sequence, optional) – Iterable with custom ids for each calc, defaults to None

  • -
-
-
Returns:
-

Dictionary of results

-
-
Return type:
-

Dict

-
-
-
- -
-
-

asimtools.scripts.cell_relax module

-

Relaxes structure using ASE

-

Author: mkphuthi@github.com

-
-
-asimtools.scripts.cell_relax.cell_relax(calc_id: str, image: Dict, prefix: str = '', optimizer: str = 'BFGS', smax: float = 0.002, mask: Sequence | None = None) Dict[source]
-

Relax cell using ASE Optimizer

-
-
Parameters:
-
    -
  • calc_id (str) – Key for calculator provided in calc_input

  • -
  • image (Dict) – Image parameters, see :func: asimtools.utils.get_atoms

  • -
  • prefix (str, optional) – Prefix to output files, defaults to ‘’

  • -
  • optimizer (str, optional) – Optimizer class to use from ase.optimize, defaults to ‘BFGS’

  • -
  • smax (float, optional) – Maximum stress in eV/$AA^3$, defaults to 0.002

  • -
  • mask (Optional[Sequence], optional) – Mask to constrain cell deformation while relaxing, defaults to None

  • -
-
-
Returns:
-

Dictionary of results including, final energy, stress and output files

-
-
Return type:
-

Dict

-
-
-
- -
-
-

asimtools.scripts.chained module

-

Submits a chain of scripts in the order specified by steps -in input

-

Author: mkphuthi@github.com

-
-
-asimtools.scripts.chained.chained(steps: Dict, env_input: Dict | None = None, calc_input: Dict | None = None) Dict[source]
-

Run a workflow using the provided sim_input.yaml one after the other, -The jobs will run one after the other, if slurm is used, job dependencies -will be used. Note that if one of the subscripts internally launches another -script e.g. image_array, calc_array etc, slurm job dependencies will not -work as expected. In those cases, we recommend running the steps one by one -or interactively

-
-
Parameters:
-
    -
  • steps (Dict) – Dictionary where each key is an ID and the value is a standard sim_input

  • -
  • env_input (Optional[Dict], optional) – env_input that overrides global file, defaults to None

  • -
  • calc_input (Optional[Dict], optional) – calc_input that overrides global file, defaults to None

  • -
-
-
Returns:
-

Dictionary of results

-
-
Return type:
-

Dict

-
-
-
- -
-
-

asimtools.scripts.distributed module

-

Distributes a number of simulations at the same time if possible

-

Author: mkphuthi@github.com

-
-
-asimtools.scripts.distributed.distributed(subscripts: Dict, env_input: Dict | None = None, calc_input: Dict | None = None, array_max: int | None = None) Dict[source]
-

Distributes a set of jobs based on inpout parameter. The scnarios are -as follows: -#. use_slurm: False, interactive: True -> Runs one after the other. -#. use_slurm: True, interactive: False, same env_id -> Launches a job # array. -#. use_slurm: False, interactive: False, different env_ids -> Launches # multiple jobs.

-
-
Parameters:
-
    -
  • subscripts (Dict) – Dictionary of scripts, each key is an ID and each value is a sim_input file

  • -
  • env_input (Optional[Dict], optional) – env_input that overrides global file, defaults to None

  • -
  • calc_input (Optional[Dict], optional) – calc_input that overrides global file, defaults to None

  • -
  • array_max (Optional[int], optional) – Maximum number of jobs to run in array, defaults to None

  • -
-
-
Returns:
-

Dictionary of results

-
-
Return type:
-

Dict

-
-
-
- -
-
-

asimtools.scripts.do_nothing module

-

Script for unit tests and debugging, does nothing for specified duration

-
-
-asimtools.scripts.do_nothing.do_nothing(duration: int | None = 5) Dict[source]
-

Sleep for the specified duration

-
-
Parameters:
-

duration (int, optional) – time in seconds, defaults to 60

-
-
Returns:
-

Dictionary with duration slept for

-
-
Return type:
-

Dict

-
-
-
- -
-
-

asimtools.scripts.env_array module

-

Apply the same script using different envs

-

Author: mkphuthi@github.com

-
-
-asimtools.scripts.env_array.env_array(subscript_input: Dict, env_ids: Sequence[str], calc_input: Dict | None = None, env_input: Dict | None = None, ids: Sequence | None = None) Dict[source]
-

Run the same script using multiple env_ids

-
-
Parameters:
-
    -
  • subscript_input (Dict) – sim_input for the script to be run

  • -
  • env_ids (Sequence[str]) – Sequence of env_ids to iterate over

  • -
  • calc_input (Optional[Dict], optional) – calc_input to override global file, defaults to None

  • -
  • env_input (Optional[Dict], optional) – env_input defaults to overide global file, defaults to None

  • -
  • ids (Sequence, optional) – Sequence of custom ids for each env_id, defaults to None

  • -
-
-
Returns:
-

Dictionary of results

-
-
Return type:
-

Dict

-
-
-
- -
-
-

asimtools.scripts.image_array module

-

Apply the same script on multiple images

-

Author: mkphuthi@github.com

-
-
-asimtools.scripts.image_array.image_array(images: Dict, subscript_input: Dict, calc_input: Dict | None = None, env_input: Dict | None = None, ids: Sequence[str] | None = None, env_ids: Sequence[str] | None = None, array_max: int | None = None) Dict[source]
-

Submit the same script on multiple images and if specified, use -different env_ids

-
-
Parameters:
-
    -
  • images (Dict) – Image config, see asimtools.utils.get_atoms()

  • -
  • subscript_input (Dict) – sim_input of script to be run

  • -
  • calc_input (Optional[Dict], optional) – calc_input to override global file, defaults to None

  • -
  • env_input (Optional[Dict], optional) – env_input to override global file, defaults to None

  • -
  • ids (Sequence[str], optional) – Custom ids for each image, defaults to None

  • -
  • env_ids (Sequence[str], optional) – Sequence of envs for each image, must be the same length as the total number of images, defaults to None

  • -
  • array_max (Optional[int], optional) – Maximum jobs to run simultanteously in job array, defaults to None

  • -
-
-
Returns:
-

Dictionary of results

-
-
Return type:
-

Dict

-
-
-
- -
-
-

asimtools.scripts.lammps module

-

Runs a user defined lammps script or template

-

Author: mkphuthi@github.com

-
-
-asimtools.scripts.lammps.lammps(template: str, image: Dict | None = None, prefix: str = '', atom_style: str = 'atomic', variables: Dict | None = None, lmp_cmd: str = 'lmp') Dict[source]
-

Runs a lammps simulation based on a template lammps input script

-
- -
-
-

asimtools.scripts.parity module

-

Generates a parity plot and collects evaluation statistics comparing energy -and/or forces and/or stress to existing values in the provided dataset. This -script can work in parallel based on the number of cores specified.

-

Author: mkphuthi@github.com

-
-
-asimtools.scripts.parity.calc_parity_data(subset: List, calc_id: str, properties: Sequence = ('energy', 'forces', 'stress'), force_prob: float = 1.0) Dict[source]
-

Calculates parity data for each atoms instance in subset

-
-
Parameters:
-
    -
  • subset (List) – List of atoms instances

  • -
  • calc (Calculator) – ASE Cailculator instance

  • -
  • properties (List, optional) – Properties to evaluate, choose from “energy”, “forces” and “stress”, defaults to (‘energy’, ‘forces’, ‘stress’)

  • -
  • force_prob (float, optional) – Fraction of forces to consider, may speed up sampling large structures by subsampling forces randomly, defaults to 1.0

  • -
-
-
Returns:
-

Dictionary with reference and predicted values for each property

-
-
Return type:
-

Dict

-
-
-
- -
-
-asimtools.scripts.parity.parity(images: Dict, calc_id: str, force_prob: float = 1.0, nprocs: int = 1, unit: str = 'meV', index: str = ':', properties: Sequence = ('energy', 'forces', 'stress')) Dict[source]
-

Generates a parity plot and collects evaluation statistics comparing energy -and/or forces and/or stress to existing values in the provided dataset

-
-
Parameters:
-
    -
  • images (Dict) – Image config, see asimtools.utils.get_atoms()

  • -
  • calc_id (str) – ID of calculator provided in calc_input or global file

  • -
  • force_prob (float, optional) – Fraction of forces to consider in force parity, can be used for speeding up large structures by only subsampling randomly, defaults to 1.0

  • -
  • nprocs (int, optional) – Number of process to parallelize over, must be less than number of datapoints, defaults to 1

  • -
  • unit (str, optional) – Unit to plot, choose from meV, eV or kcal/mol, defaults to ‘meV’

  • -
  • index (int, optional) – index of data to use from full dataset, defaults to ‘:’

  • -
  • properties (Sequence, optional) – Properties to evaluate, choose from “energy”, “forces” and “stress”, defaults to (‘energy’, ‘forces’, ‘stress’)

  • -
-
-
Returns:
-

Dictionary of results

-
-
Return type:
-

Dict

-
-
-
- -
-
-asimtools.scripts.parity.rmse(yhat: Sequence, y: Sequence) float[source]
-

Calculate Root Mean Square Error between to sequences

-
-
Parameters:
-
    -
  • yhat (Sequence) – Predicted values

  • -
  • y (Sequence) – True values

  • -
-
-
Returns:
-

RMSE

-
-
Return type:
-

float

-
-
-
- -
-
-

asimtools.scripts.sim_array module

-

Runs the same script, iterating over multiple values of a specified argument -based on a sim_input template provided by the user

-

Author: mkphuthi@github.com

-
-
-asimtools.scripts.sim_array.sim_array(template_sim_input: Dict, key_sequence: Sequence[str] | None = None, array_values: Sequence | None = None, env_ids: Sequence[str] | str | None = None, calc_input: Dict | None = None, env_input: Dict | None = None, ids: Sequence | None = None) Dict[source]
-

Runs the same script, iterating over multiple values of a specified -argument based on a sim_input template provided by the user

-
-
Parameters:
-
    -
  • template_sim_input (Dict) – sim_input containing all the default parameters

  • -
  • key_sequence (Optional[Sequence[str]], optional) – Sequence of keys to access value to be iterated over, defaults to None

  • -
  • array_values (Optional[Sequence], optional) – values to be iterated over in each simulation, defaults to None

  • -
  • env_ids (Optional[Union[Sequence[str],str]], optional) – Environment(s) to be used for each simulation, must either be a list with as many env_ids as array values or a string with the env_id to be used by all simulations, defaults to None

  • -
  • calc_input (Optional[Dict], optional) – calc_input file to use, defaults to None

  • -
  • env_input (Optional[Dict], optional) – env_input to use, defaults to None

  • -
  • ids (Sequence, optional) – Custom ids to use for each simulation, defaults to None

  • -
-
-
Returns:
-

Results

-
-
Return type:
-

Dict

-
-
-
- -
-
-

asimtools.scripts.singlepoint module

-

Calculates single point energy

-

Author: mkphuthi@github.com

-
-
-asimtools.scripts.singlepoint.singlepoint(calc_id: str, image: Dict, properties: Tuple[str] = ('energy', 'forces')) Dict[source]
-

Evaluates the properties of a single image, currently implemented -properties are energy, forces and stress

-
-
Parameters:
-
    -
  • calc_id (str) – ID of calculator provided in calc_input or global file

  • -
  • image (Dict) – Image config, see asimtools.utils.get_atoms()

  • -
  • properties (Tuple[str], optional) – properties to evaluate, defaults to (‘energy’, ‘forces’)

  • -
-
-
Returns:
-

Dictionary of results

-
-
Return type:
-

Dict

-
-
-
- -
-
-

asimtools.scripts.template module

-

Describe the script briefly here. If this is a script that runs multiple steps, -describe it here using reStructuredText to generate autodocs

-

Cite the papers where the method/script was first introduced here as well

-

Author: mkphuthi@github.com

-
-
-asimtools.scripts.template.template() Dict[source]
-

Script does xyz specifically

-
- -
-
-

Module contents

-
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/genindex.html b/build/genindex.html deleted file mode 100644 index f157503..0000000 --- a/build/genindex.html +++ /dev/null @@ -1,663 +0,0 @@ - - - - - - Index — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
-
-
- - -

Index

- -
- A - | C - | D - | E - | F - | G - | I - | J - | L - | M - | N - | P - | R - | S - | T - | U - | W - -
-

A

- - - -
    -
  • add_output_files() (asimtools.job.Job method) -
  • -
  • - asimtools - -
  • -
  • - asimtools.calculators - -
  • -
  • - asimtools.job - -
  • -
  • - asimtools.scripts - -
  • -
  • - asimtools.scripts.asim_check - -
  • -
  • - asimtools.scripts.asim_execute - -
  • -
  • - asimtools.scripts.asim_run - -
  • -
  • - asimtools.scripts.atom_relax - -
  • -
  • - asimtools.scripts.calc_array - -
  • -
  • - asimtools.scripts.cell_relax - -
  • -
  • - asimtools.scripts.chained - -
  • -
  • - asimtools.scripts.distributed - -
  • -
  • - asimtools.scripts.do_nothing - -
  • -
    -
  • - asimtools.scripts.env_array - -
  • -
  • - asimtools.scripts.eos - -
  • -
  • - asimtools.scripts.eos.eos - -
  • -
  • - asimtools.scripts.eos.postprocess - -
  • -
  • - asimtools.scripts.eos.preprocess - -
  • -
  • - asimtools.scripts.image_array - -
  • -
  • - asimtools.scripts.lammps - -
  • -
  • - asimtools.scripts.parity - -
  • -
  • - asimtools.scripts.sim_array - -
  • -
  • - asimtools.scripts.singlepoint - -
  • -
  • - asimtools.scripts.template - -
  • -
  • - asimtools.utils - -
  • -
  • atom_relax() (in module asimtools.scripts.atom_relax) -
  • -
- -

C

- - - -
- -

D

- - - -
- -

E

- - - -
- -

F

- - -
- -

G

- - - -
- -

I

- - -
- -

J

- - - -
- -

L

- - - -
- -

M

- - -
- -

N

- - -
- -

P

- - - -
- -

R

- - - -
- -

S

- - - -
- -

T

- - -
- -

U

- - - -
- -

W

- - - -
- - - -
-
-
- -
- -
-

© Copyright 2023, Mgcini Keith Phuthi, Emil Annevelink.

-
- - Built with Sphinx using a - theme - provided by Read the Docs. - - -
-
-
-
-
- - - - \ No newline at end of file diff --git a/build/include_contributing.html b/build/include_contributing.html deleted file mode 100644 index b75dbeb..0000000 --- a/build/include_contributing.html +++ /dev/null @@ -1,219 +0,0 @@ - - - - - - - Contributing to ASIMTools — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

Contributing to ASIMTools

-
-

Contributing

-

When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change.

-

Please note we have a code of conduct, please follow it in all your interactions with the project.

-

There are two main ways to contribute. One is to make changes to the existing -codes and scripts. In this case, please make sure your change passes tests or -submit a test accordinly. The second way to contribute is to add a new script -to the core set of scripts. Reviewers will work with you to make sure your new -script follows best practice including using utils, syntax highlighting, docs -and tests.

-
-

Pull Request Process

-
    -
  1. Ensure any install or build dependencies are removed before the end of the layer when doing a build.

  2. -
  3. Update the README.md with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters.

  4. -
  5. Increase the version numbers in any examples files and the README.md to the new version that this Pull Request would represent. The versioning scheme we use is SemVer.

  6. -
  7. You may merge the Pull Request in once you have the sign-off of one other developer, or if you do not have permission to do that, you may request the other reviewer to merge it for you.

  8. -
-
-
-

Code of Conduct

-
-

Our Pledge

-

In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation.

-
-
-

Our Standards

-

Examples of behavior that contributes to creating a positive environment -include:

-
    -
  • Using welcoming and inclusive language

  • -
  • Being respectful of differing viewpoints and experiences

  • -
  • Gracefully accepting constructive criticism

  • -
  • Focusing on what is best for the community

  • -
  • Showing empathy towards other community members

  • -
-

Examples of unacceptable behavior by participants include:

-
    -
  • The use of sexualized language or imagery and unwelcome sexual attention or -advances

  • -
  • Trolling, insulting/derogatory comments, and personal or political attacks

  • -
  • Public or private harassment

  • -
  • Publishing others’ private information, such as a physical or electronic -address, without explicit permission

  • -
  • Other conduct which could reasonably be considered inappropriate in a -professional setting

  • -
-
-
-

Our Responsibilities

-

Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior.

-

Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful.

-
-
-

Scope

-

This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers.

-
-
-

Enforcement

-

Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at [INSERT EMAIL ADDRESS]. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately.

-

Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project’s leadership.

-
-
-

Attribution

-

This Code of Conduct is adapted from the Contributor Covenant, version 1.4, -available at http://contributor-covenant.org/version/1/4

-
-
-
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/include_readme.html b/build/include_readme.html deleted file mode 100644 index 3bc711e..0000000 --- a/build/include_readme.html +++ /dev/null @@ -1,309 +0,0 @@ - - - - - - - README — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

README

-
-

Atomic SIMulation Tools

-

This package is a lightweight workflow and simulation manager for reproducible -atomistic simulations that can be transferred across environments, calculators -and structures. By using in-built or user-defined scripts and utilities, users -can run/build their own simulation recipes and automagically scale them on -slurm based clusters. The core idea is to separate the dependence of the -atomistic potential/calculator and the simulations steps thereby allowing the -same simulation to be run with multiple calculators and the same calculator to -be used for multiple simulations. Input and output files must follow a strict -format so that consistent analysis pipelines can be used across users

-
-

Developer philosophy

-

The goal of asimtools is to push all the complexity of workflow -management, best-practices, file management etc. into the backend such that the -everyday user only has to handle input files for existing workflows and script -files for workflows they want to implement. The following are the guiding -principles for how asimtools should work:

-
    -
  • Scripts should resemble boilerplate ASE code as much as possible.

  • -
  • Scripts should not explicitly depend on a calculator

  • -
  • Scripts should not explicitly depend on the context in which they run

  • -
  • It should be easy to debug individual scripts/parts in large workflows. In -addition, it should always be possible to debug/resubmit jobs without using -asimtools.

  • -
  • input file structure and format should be standard across all scripts. In -addition all input parameters should match how they would like without -asimtools i.e. do not provide an API! This means extensive use of importlib.

  • -
  • output file structure should be predictable across all scripts

  • -
  • Job progress tracking must be incorporated

  • -
  • Best practices should be built-in e.g. if multiple jobs of the same slurm -context are submitted simulataneously, it must be a job array

  • -
-
-
-

Philosophy on User Experience

-

The philosophy is to build “simulations” using building blocks of scripts. -These scripts can be as complicated/efficient as you make them using any -external packages you want and can be optimized with time but can still be run -within the framework. This allows a test friendly way to transition from say a -tutorial on the ASE/pymatgen website to an asimtools script. So while -complicated wrappers are discouraged, they would still work as long as the -script works. The benefit out of the box is that you can make your script -independent of calculator or input structures and submit them easily.

-
-
-

Getting Started

-

These instructions will give you a copy of the project up and running on -your local machine for development and testing purposes.

-
-

Installing

-

You can install asimtools in a new conda environment using:

-
conda create -n asimtools python=3.9
-conda activate asimtools
-
-conda install ase -c conda-forge
-
-git clone https://github.com/BattModels/asimtools.git
-cd asimtools
-pip install -e .
-
-
-

This installs the base package in developer mode so that you do not have to -reinstall if you make changes to the core package.

-

Individual calculators may need external packages for loading those -calculators. Similarly some scripts e.g. lammps.py might also need external packages to be used. It is up to the user to make sure those are installed.

-

You will also need to setup some environment variables, these variables point -to a env_input.yaml and calc_input.yaml with your favorite configs since -these are commonly shared among simulations. You can also directly specify them -when running asim-execute (See asim-execute -h). -Examples of these files can be found in the examples.

-

Add the following to your .bashrc

-
export ASIMTOOLS_ENV_INPUT=/path/to/my/global/env_input.yaml
-export ASIMTOOLS_CALC_INPUT=/path/to/my/global/calc_input.yaml
-export ASIMTOOLS_SCRIPT_DIR=/path/to/my/script/dir
-
-
- - - - -
-
-
-

Running the tests

-

To run all tests from the tests directory, call:

-
pytest
-
-
-

To run the test suite on a component component.py , call:

-
pytest test_component.py
-
-
- -
-
-

Contributing

-

Please read CONTRIBUTING.md for details on our code -of conduct, and the process for submitting pull requests to us.

-
-
-

Authors

- -

See also the list of -contributors -who participated in this project.

-
-
-

License

-

This project is licensed under the MIT License

-
-
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/index.html b/build/index.html deleted file mode 100644 index c9a675c..0000000 --- a/build/index.html +++ /dev/null @@ -1,345 +0,0 @@ - - - - - - - Welcome to asimtools’s documentation! — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

Welcome to asimtools’s documentation!

-
-

Contents:

- -
-
-
-

Indices and tables

- -
- - -
-
-
- -
- -
-

© Copyright 2023, Mgcini Keith Phuthi, Emil Annevelink.

-
- - Built with Sphinx using a - theme - provided by Read the Docs. - - -
-
-
-
-
- - - - \ No newline at end of file diff --git a/build/modules.html b/build/modules.html deleted file mode 100644 index fb03205..0000000 --- a/build/modules.html +++ /dev/null @@ -1,281 +0,0 @@ - - - - - - - asimtools — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

asimtools

-
- -
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/objects.inv b/build/objects.inv deleted file mode 100644 index a801a03..0000000 Binary files a/build/objects.inv and /dev/null differ diff --git a/build/py-modindex.html b/build/py-modindex.html deleted file mode 100644 index 6a1ff8e..0000000 --- a/build/py-modindex.html +++ /dev/null @@ -1,245 +0,0 @@ - - - - - - Python Module Index — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
-
-
- - -

Python Module Index

- -
- a -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
- a
- asimtools -
    - asimtools.calculators -
    - asimtools.job -
    - asimtools.scripts -
    - asimtools.scripts.asim_check -
    - asimtools.scripts.asim_execute -
    - asimtools.scripts.asim_run -
    - asimtools.scripts.atom_relax -
    - asimtools.scripts.calc_array -
    - asimtools.scripts.cell_relax -
    - asimtools.scripts.chained -
    - asimtools.scripts.distributed -
    - asimtools.scripts.do_nothing -
    - asimtools.scripts.env_array -
    - asimtools.scripts.eos -
    - asimtools.scripts.eos.eos -
    - asimtools.scripts.eos.postprocess -
    - asimtools.scripts.eos.preprocess -
    - asimtools.scripts.image_array -
    - asimtools.scripts.lammps -
    - asimtools.scripts.parity -
    - asimtools.scripts.sim_array -
    - asimtools.scripts.singlepoint -
    - asimtools.scripts.template -
    - asimtools.utils -
- - -
-
-
- -
- -
-

© Copyright 2023, Mgcini Keith Phuthi, Emil Annevelink.

-
- - Built with Sphinx using a - theme - provided by Read the Docs. - - -
-
-
-
-
- - - - \ No newline at end of file diff --git a/build/search.html b/build/search.html deleted file mode 100644 index d7b7b8b..0000000 --- a/build/search.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - Search — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
-
-
- - - - -
- -
- -
-
-
- -
- -
-

© Copyright 2023, Mgcini Keith Phuthi, Emil Annevelink.

-
- - Built with Sphinx using a - theme - provided by Read the Docs. - - -
-
-
-
-
- - - - - - - - - \ No newline at end of file diff --git a/build/searchindex.js b/build/searchindex.js deleted file mode 100644 index ce0f119..0000000 --- a/build/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({"docnames": ["CONTRIBUTING", "README", "asimplify", "asimtools", "asimtools.scripts", "asimtools.scripts.eos", "include_contributing", "include_readme", "index", "modules", "usage", "workflows"], "filenames": ["CONTRIBUTING.md", "README.md", "asimplify.rst", "asimtools.rst", "asimtools.scripts.rst", "asimtools.scripts.eos.rst", "include_contributing.rst", "include_readme.rst", "index.rst", "modules.rst", "usage.rst", "workflows.rst"], "titles": ["Contributing", "Atomic SIMulation Tools", "Developing Custom Scripts", "asimtools package", "asimtools.scripts package", "asimtools.scripts.eos package", "Contributing to ASIMTools", "README", "Welcome to asimtools\u2019s documentation!", "asimtools", "Running an existing script", "Using built-in workflow tools"], "terms": {"thi": [0, 1, 2, 3, 4, 6, 7, 10, 11], "section": [2, 10], "guid": [1, 2, 7, 10], "you": [0, 1, 2, 3, 6, 7, 10, 11], "through": [2, 10], "take": [0, 2, 6, 10], "your": [0, 1, 2, 6, 7, 10, 11], "own": [1, 2, 7, 11], "hous": 2, "simul": [2, 3, 4, 8, 10], "integr": 2, "asimtool": [1, 2, 7, 10, 11], "The": [0, 1, 2, 3, 4, 6, 7, 10], "process": [1, 2, 4, 7, 8], "i": [0, 1, 2, 3, 4, 6, 7, 10, 11], "design": 2, "straight": 2, "forward": 2, "reason": [0, 2, 6], "possibl": [1, 2, 4, 7], "As": [2, 10], "an": [0, 1, 2, 3, 4, 6, 7, 11], "exampl": [0, 1, 2, 6, 7, 10, 11], "we": [0, 2, 3, 4, 6, 10], "asimplifi": 2, "calcul": [1, 2, 4, 5, 7, 8, 9, 10, 11], "adsorption_energi": 2, "shown": [2, 10], "ASE": [1, 2, 3, 4, 7, 10], "To": [1, 2, 7, 10], "understand": 2, "code": [1, 2, 7, 8, 10], "visit": 2, "tutori": [1, 2, 7], "page": [2, 8], "http": [0, 1, 2, 6, 7], "wiki": [0, 2, 6], "fysik": 2, "dtu": 2, "dk": 2, "ase": [1, 2, 3, 4, 7, 10], "db": [2, 3], "html": 2, "ultim": 2, "work": [0, 1, 2, 3, 4, 6, 7, 10], "ani": [0, 1, 2, 3, 6, 7, 10], "element": 2, "environ": [0, 1, 2, 3, 4, 6, 7, 10], "fulli": 2, "parallel": [2, 4, 10, 11], "job": [1, 2, 4, 7, 8, 9, 11], "submiss": 2, "just": [2, 10], "few": [2, 10], "step": [1, 2, 3, 4, 7, 11], "us": [0, 1, 2, 3, 4, 6, 7, 10], "toi": 2, "make": [0, 1, 2, 6, 7, 10], "thing": [2, 5], "easier": 2, "best": [0, 1, 2, 6, 7], "first": [0, 2, 4, 6], "replac": [2, 3], "expens": 2, "part": [1, 2, 7, 10], "workflow": [1, 2, 3, 4, 7, 8, 10], "e": [0, 1, 2, 4, 6, 7, 10, 11], "g": [1, 2, 4, 7, 10, 11], "empir": 2, "like": [1, 2, 3, 7, 10], "eam": 2, "instead": [2, 10], "dft": 2, "smaller": [2, 3], "simpler": 2, "structur": [1, 2, 4, 5, 7, 10, 11], "loop": 2, "over": [2, 4, 11], "fewer": 2, "case": [0, 2, 4, 6, 10], "etc": [1, 2, 4, 7, 11], "alreadi": [2, 10], "simpl": 2, "emt": 2, "which": [0, 1, 2, 3, 4, 6, 7, 10], "provid": [1, 2, 3, 4, 7, 10], "here": [2, 4], "refer": [2, 4, 10], "from": [0, 1, 2, 3, 4, 6, 7, 8], "build": [0, 1, 2, 3, 6, 7, 10, 11], "import": [2, 8], "bulk": [2, 3, 10], "eo": [2, 3, 4, 8, 9], "calculate_eo": 2, "connect": [2, 3], "symb": 2, "al": 2, "ni": 2, "cu": 2, "pd": [2, 3], "ag": [0, 2, 6], "pt": 2, "au": 2, "atom": [2, 3, 4, 8], "fcc": 2, "calc": [2, 4, 10], "v": 2, "b": 2, "fit": 2, "find": [2, 4], "minimum": 2, "do": [0, 1, 2, 6, 7, 10], "one": [0, 2, 3, 4, 6, 10, 11], "more": [2, 3, 10], "minimu": 2, "write": [2, 3, 10], "databas": [2, 3], "cell": [2, 4], "get_volum": 2, "1": [0, 2, 3, 4, 5, 6, 10, 11], "3": [1, 2, 4, 7, 10], "get_potential_energi": 2, "bm": 2, "wrap": 2, "function": [2, 3, 8], "workhors": 2, "return": [2, 3, 4, 10], "dictionari": [2, 3, 4, 10], "can": [1, 2, 3, 4, 7, 10, 11], "run": [2, 3, 4, 8, 11], "within": [0, 1, 2, 6, 7], "framework": [1, 2, 7], "easiest": 2, "would": [0, 1, 2, 6, 7, 10], "copi": [1, 2, 3, 7], "past": 2, "defin": [0, 1, 2, 4, 6, 7, 10], "insid": 2, "same": [1, 2, 3, 4, 7, 10, 11], "name": [2, 3, 10], "def": 2, "ase_eo": 2, "immedi": 2, "follow": [0, 1, 2, 3, 4, 6, 7, 10], "sim_input": [2, 3, 4, 8, 11], "yaml": [1, 2, 3, 4, 7, 8], "path": [1, 2, 3, 7, 10], "py": [1, 2, 7, 10], "env_id": [2, 4, 10], "inlin": [2, 10], "workdir": [2, 3, 4, 10], "result": [0, 2, 3, 4, 6, 10, 11], "call": [1, 2, 3, 4, 7, 10], "asim": [1, 2, 7, 8], "execut": [1, 2, 4, 7, 8], "onli": [1, 2, 3, 4, 7, 10], "littl": 2, "bit": 2, "complic": [1, 2, 7, 10], "than": [2, 4], "python": [1, 2, 7, 10], "howev": 2, "still": [1, 2, 7], "depend": [0, 1, 2, 3, 4, 6, 7, 10, 11], "specif": [0, 2, 3, 4, 6, 10], "let": 2, "": [0, 2, 3, 4, 6, 10], "easi": [1, 2, 7], "chang": [0, 1, 2, 3, 6, 7, 10], "load_calc": [2, 3, 8, 9], "calc_id": [2, 3, 4, 5, 10], "now": 2, "correctli": 2, "configur": [2, 4, 10], "all": [0, 1, 2, 3, 4, 6, 7, 10, 11], "even": [2, 10], "calc_arrai": [2, 3, 8, 9, 11], "iter": [2, 3, 4, 11], "get": [2, 3, 4, 8, 10], "differ": [0, 2, 4, 6, 10, 11], "final": [2, 4], "below": [2, 10], "imag": [2, 3, 4, 5, 8], "get_atom": [2, 3, 4, 8, 9, 10], "peasi": 2, "have": [0, 1, 2, 3, 6, 7, 10], "arbitrari": 2, "input": [1, 2, 3, 4, 7, 8], "Of": 2, "cours": 2, "fail": [2, 3, 8, 9], "give": [1, 2, 7], "bad": 2, "some": [1, 2, 7, 11], "cleanup": 2, "so": [1, 2, 7, 10], "send": 2, "output": [1, 2, 3, 4, 7, 8], "log": [2, 3, 10], "checkpoint": 2, "addition": [2, 10], "ad": [2, 10], "repositori": [0, 2, 6, 10], "need": [1, 2, 7, 10], "clear": [2, 10], "syntax": [0, 2, 6], "highlight": [0, 2, 6], "document": [2, 11], "type": [2, 3, 4, 11], "dict": [2, 3, 4, 5, 10], "util": [0, 1, 2, 4, 6, 7, 8, 9, 10], "str": [2, 3, 4, 5, 10], "db_file": 2, "info": [2, 3], "successfulli": 2, "float": [2, 3, 4, 5], "sai": [1, 2, 7], "argon": 2, "lennardjon": [2, 10], "slurm": [1, 2, 3, 4, 7, 10], "file": [0, 1, 2, 3, 4, 6, 7, 8], "batch": [2, 3, 10], "arg": [2, 3, 4, 10], "builder": [2, 3, 10], "ar": [0, 1, 2, 3, 4, 6, 7, 10, 11], "gpaw": [2, 10], "calc_input": [1, 2, 3, 4, 7, 8], "lj": [2, 10], "modul": [2, 8, 9, 10], "sigma": [2, 10], "54": [2, 10], "epsilon": [2, 10], "0": [2, 3, 4, 5, 10], "00802236": [2, 10], "env_input": [1, 2, 3, 4, 7, 8], "mode": [1, 2, 7, 10], "use_slurm": [2, 4, 10], "true": [2, 3, 4, 5, 10], "interact": [0, 2, 3, 4, 6, 10], "fals": [2, 3, 4, 10], "flag": [2, 10], "n": [1, 2, 7, 10], "2": [2, 10, 11], "precommand": [2, 10], "sourc": [2, 3, 4, 5, 10], "bashrc": [1, 2, 7, 10], "conda": [1, 2, 7, 10], "activ": [1, 2, 7, 10], "go": [2, 3, 10], "back": 2, "origin": 2, "problem": [2, 10], "want": [1, 2, 7, 10], "mulitpl": 2, "achiev": 2, "nest": 2, "sim_arrai": [2, 3, 8, 9, 11], "key_sequ": [2, 3, 4], "array_valu": [2, 4], "template_sim_input": [2, 4], "crystalstructur": [2, 10], "access": [2, 3, 4], "without": [0, 1, 2, 3, 6, 7, 10], "full": [2, 4], "set": [0, 2, 3, 4, 6, 10], "variabl": [0, 1, 2, 3, 4, 6, 7, 10], "export": [1, 2, 7, 10], "asimtools_script_dir": [1, 2, 7], "my": [1, 2, 7, 10], "dir": [1, 2, 7], "move": 2, "directori": [1, 2, 3, 4, 7, 10, 11], "place": [2, 3], "allow": [1, 2, 7], "prepend": [2, 10], "script": [0, 1, 3, 6, 7, 8, 9, 11], "postprocess": [3, 4, 8, 9], "preprocess": [3, 4, 8, 9], "asim_check": [3, 8, 9], "get_status_and_color": [3, 4, 8, 9], "get_subjob": [3, 4, 8, 9], "load_job_tre": [3, 4, 8, 9], "main": [0, 3, 4, 6, 8, 9, 10], "parse_command_lin": [3, 4, 8, 9], "print_job_tre": [3, 4, 8, 9], "asim_execut": [3, 8, 9], "asim_run": [3, 8, 9], "atom_relax": [3, 8, 9], "cell_relax": [3, 8, 9], "chain": [3, 8, 9, 10, 11], "distribut": [3, 8, 9, 10, 11], "do_noth": [3, 8, 9], "env_arrai": [3, 8, 9], "image_arrai": [3, 8, 9, 11], "lammp": [1, 3, 7, 8, 9, 10], "pariti": [3, 8, 9], "calc_parity_data": [3, 4, 8, 9], "rmse": [3, 4, 8, 9], "singlepoint": [3, 8, 9, 10], "templat": [3, 8, 9, 10, 11], "tool": [3, 8], "load": [1, 3, 4, 7, 10], "object": [3, 10, 11], "load_ase_calc": [3, 8, 9], "calc_param": 3, "builtin": 3, "none": [3, 4, 5], "given": [3, 4, 10, 11], "paramet": [0, 1, 3, 4, 6, 7, 10], "option": [3, 4, 10], "id": [3, 4], "kei": [3, 4, 10], "suppli": [3, 10], "global": [1, 3, 4, 7, 10], "default": [3, 4, 10], "singl": [3, 4, 10], "instanc": [0, 3, 4, 6], "load_dp": [3, 8, 9], "deep": 3, "potenti": [1, 3, 7], "pass": [0, 3, 6, 10], "loader": 3, "dp": 3, "deepmd": 3, "load_nequip": [3, 8, 9], "nequip": 3, "allegro": 3, "nequip_calcul": 3, "nequipcalcul": 3, "handl": [1, 3, 7], "temin": 3, "author": [3, 4, 8], "mkphuthi": [1, 3, 4, 7], "github": [1, 3, 4, 7], "com": [1, 3, 4, 7], "class": [3, 4, 10], "chainedjob": [3, 8, 9], "base": [1, 3, 4, 7, 10], "made": 3, "sequenti": 3, "unitjob": [3, 8, 9], "note": [0, 3, 4, 6, 10], "well": [3, 4], "unit": [3, 4], "If": [3, 4, 10], "intern": [3, 4], "correct": [0, 3, 6, 10], "gener": [3, 4, 10], "appropri": [0, 3, 6], "submit": [0, 1, 3, 4, 6, 7, 8, 9, 10], "them": [1, 3, 4, 7, 10], "manual": [3, 10], "get_last_output": [3, 8, 9], "last": 3, "list": [1, 3, 4, 7, 10], "termin": [3, 10], "distributedjob": [3, 8, 9], "arrai": [1, 3, 4, 7], "abil": 3, "simultan": 3, "gen_input_fil": [3, 8, 9], "kwarg": 3, "submit_arrai": [3, 8, 9], "array_max": [3, 4], "env": [3, 4], "submit_job": [3, 8, 9], "lot": 3, "recommend": [3, 4, 10], "abstract": 3, "add_output_fil": [3, 8, 9], "file_dict": 3, "add": [0, 1, 3, 6, 7], "complet": [3, 8, 9, 10], "updat": [0, 3, 6], "signal": 3, "wa": [3, 4, 10], "start": [3, 4, 8, 9, 10], "discard": [3, 8, 9, 10], "statu": [3, 8], "get_calc_input": [3, 8, 9], "get_env_input": [3, 8, 9], "get_logg": [3, 8, 9], "logfilenam": 3, "level": [0, 3, 4, 6, 10], "logger": 3, "get_output": [3, 8, 9], "valu": [3, 4, 10], "get_output_yaml": [3, 8, 9], "current": [3, 4, 10], "get_sim_input": [3, 8, 9], "get_statu": [3, 8, 9], "displai": 3, "tupl": [3, 4, 5], "bool": [3, 5, 10], "check": [3, 4, 8], "get_workdir": [3, 8, 9], "go_to_workdir": [3, 8, 9], "leave_workdir": [3, 8, 9], "goe": 3, "launch": [3, 4, 10], "mkworkdir": [3, 8, 9], "creat": [0, 1, 3, 6, 7, 10], "doesn": 3, "t": 3, "exist": [0, 1, 3, 4, 6, 7], "set_workdir": [3, 8, 9], "update_calc_input": [3, 8, 9], "new_param": 3, "update_env_input": [3, 8, 9], "update_output": [3, 8, 9], "output_upd": 3, "new": [0, 1, 3, 6, 7, 10], "update_sim_input": [3, 8, 9], "update_statu": [3, 8, 9], "specifici": 3, "requir": [3, 10], "complex": [1, 3, 7], "built": [1, 3, 4, 7], "up": [1, 3, 4, 7, 10], "gen_run_command": [3, 8, 9], "command": [3, 4, 10], "independ": [1, 3, 7], "load_job_from_directori": [3, 8, 9], "helper": [3, 4], "read": [1, 3, 7, 10], "data": [3, 4], "standard": [1, 3, 4, 7, 8, 10, 11], "change_dict_valu": [3, 8, 9], "d": [3, 10], "new_valu": 3, "sequenc": [3, 4], "return_copi": 3, "specifi": [1, 3, 4, 7, 8, 11], "_type_": [3, 4], "old": 3, "order": [3, 4], "thei": [0, 1, 3, 6, 7, 10, 11], "whether": [3, 10], "modifi": [3, 10], "check_if_slurm_job_is_run": [3, 8, 9], "slurm_job_id": 3, "int": [3, 4, 5], "job_id": [3, 10], "union": [3, 4], "image_fil": [3, 10], "repeat": [3, 10], "rattle_stdev": [3, 10], "config": [1, 3, 4, 7, 10], "wai": [0, 1, 3, 6, 7, 10], "There": [0, 3, 6], "three": 3, "readabl": [3, 10], "extra": 3, "keyword": [3, 10], "argument": [3, 4, 10, 11], "number": [0, 3, 4, 6, 10], "time": [1, 3, 4, 7, 10], "supercel": [3, 10], "stdev": [3, 10], "rattl": [3, 10], "One": [0, 3, 6, 10], "get_axis_lim": [3, 8, 9], "x": 3, "y": [3, 4], "pad": 3, "estim": 3, "good": [0, 3, 6], "limit": 3, "plot": [3, 4, 5], "axi": 3, "tri": 3, "dotfil": 3, "empti": [3, 10], "found": [1, 3, 7, 10, 11], "get_imag": [3, 8, 9, 10], "pattern": [3, 10], "index": [3, 4, 8, 10], "string": [3, 4, 10], "search": [3, 8], "each": [3, 4, 11], "io": [3, 10], "A": [3, 10], "when": [0, 1, 3, 6, 7], "logfil": 3, "fstr": 3, "asctim": 3, "funcnam": 3, "levelnam": 3, "messag": 3, "debug": [1, 3, 4, 7, 10], "join_nam": [3, 8, 9], "substr": 3, "join": 3, "multipl": [1, 3, 4, 7, 10, 11], "hyphen": 3, "neatli": 3, "merg": [0, 3, 6], "new_db": [3, 8, 9], "dbname": 3, "overwrit": [3, 10], "parse_slic": [3, 8, 9], "slice": 3, "pars": [3, 4], "stop": 3, "read_yaml": [3, 8, 9], "yaml_path": 3, "strip_symbol": [3, 8, 9], "format": [1, 3, 4, 7, 10], "filenam": 3, "strip": 3, "write_csv_from_dict": [3, 8, 9], "fname": 3, "column": 3, "header": 3, "datafram": 3, "tabul": 3, "csv": 3, "becom": 3, "must": [1, 3, 4, 7, 10], "length": [3, 4], "panda": 3, "to_csv": 3, "write_yaml": [3, 8, 9], "yaml_dict": 3, "recurs": 4, "progress": [1, 4, 7], "pring": 4, "out": [1, 4, 7, 10], "print": [4, 10], "color": 4, "_description_": 4, "tree": [4, 10], "root": [4, 10], "mimick": 4, "cmdline": 4, "line": 4, "job_tre": 4, "indent_str": 4, "indent": 4, "optin": 4, "directli": [1, 4, 7, 10, 11], "relax": 4, "optim": [1, 4, 7], "gpmin": 4, "properti": 4, "energi": [4, 10], "forc": 4, "fmax": 4, "02": 4, "tomic": 4, "prefix": 4, "converg": 4, "threshold": 4, "appli": [0, 4, 6, 10], "subscript_input": 4, "template_calc_id": 4, "necessari": [0, 4, 6], "custom": [4, 8, 10], "bfg": 4, "smax": 4, "002": 4, "mask": 4, "see": [1, 4, 7, 10], "func": 4, "maximum": 4, "stress": 4, "ev": 4, "aa": 4, "constrain": 4, "deform": 4, "while": [1, 4, 7], "includ": [0, 4, 6, 10], "after": [4, 10, 11], "other": [0, 4, 6, 10, 11], "subscript": 4, "anoth": 4, "expect": [0, 4, 6], "In": [0, 1, 4, 6, 7, 10], "those": [1, 4, 7, 10], "where": [4, 10], "overrid": 4, "inpout": 4, "scnario": 4, "test": [0, 4, 6, 8], "doe": 4, "noth": [4, 10], "durat": 4, "5": [4, 5], "sleep": 4, "second": [0, 4, 6], "60": 4, "slept": 4, "overid": 4, "total": 4, "simultant": 4, "user": [4, 8, 10, 11], "atom_styl": 4, "lmp_cmd": 4, "lmp": 4, "collect": 4, "evalu": 4, "statist": 4, "compar": 4, "dataset": 4, "core": [0, 1, 4, 6, 7, 10], "subset": 4, "force_prob": 4, "cailcul": 4, "choos": 4, "fraction": 4, "consid": [0, 4, 6], "mai": [0, 1, 4, 6, 7, 10], "speed": 4, "sampl": 4, "larg": [1, 4, 7], "subsampl": 4, "randomli": 4, "predict": [1, 4, 7], "nproc": 4, "mev": 4, "less": 4, "datapoint": 4, "kcal": 4, "mol": 4, "yhat": 4, "mean": [1, 4, 7], "squar": 4, "error": [4, 10], "between": [4, 10], "contain": [0, 4, 6, 10], "either": [4, 10], "mani": [4, 10], "point": [1, 4, 7], "implement": [1, 4, 7, 10], "describ": [4, 10], "briefli": 4, "restructuredtext": 4, "autodoc": 4, "cite": 4, "paper": 4, "method": [0, 4, 6, 10], "introduc": 4, "xyz": [4, 10], "xauthor": 5, "singlepoint_env_id": 5, "preprocess_env_id": 5, "nimag": 5, "scale_rang": 5, "95": 5, "05": 5, "prepar": 5, "readm": [0, 6, 8], "usag": 8, "restart": 8, "api": [1, 7, 8, 10], "doc": [0, 6, 8], "packag": [1, 7, 8, 9], "contribut": 8, "content": 9, "written": 10, "yourself": 10, "c": [1, 7, 10], "For": 10, "most": [10, 11], "ever": 10, "explain": 10, "minim": 10, "my_env_var": 10, "arg1": 10, "value_1": 10, "arg2": 10, "value_2": 10, "context": [1, 7, 10], "consol": 10, "wherea": 10, "ignor": 10, "befor": [0, 6, 10], "postcommand": 10, "being": 10, "pair": 10, "These": [1, 7, 10, 11], "whatev": 10, "detail": [0, 1, 6, 7, 10], "kind": 10, "run_prefix": 10, "run_postfix": 10, "flag1": 10, "flag2": 10, "precommand1": 10, "precommand2": 10, "postcommand1": 10, "postcommand2": 10, "concret": 10, "commandlin": 10, "batch_job": 10, "task": 10, "deactiv": 10, "interactive_job": 10, "gre": 10, "gpu": 10, "highest": 10, "That": 10, "repeatedli": 10, "particular": 10, "asimtools_env_input": [1, 7, 10], "uniqu": 10, "identifi": 10, "match": [1, 7, 10], "request": [1, 7, 8, 10], "alloc": 10, "salloc": 10, "submita": 10, "sbatch": 10, "append": 10, "mpirun": 10, "inkok": 10, "equival": 10, "my_script": 10, "alwai": [1, 7, 10], "txt": 10, "4": [0, 6, 10], "common": [10, 11], "ca": 10, "lastli": 10, "abov": 10, "asimtools_calc_input": [1, 7, 10], "farther": 10, "down": 10, "kpt": 10, "h": [1, 7, 10], "xc": 10, "pbe": 10, "gpaw_output": 10, "bbbb": 10, "extern": [1, 7, 10], "avail": [0, 6, 10], "initi": 10, "instanti": 10, "ref": 10, "applic": 10, "unif": 10, "interfac": [0, 6, 10], "download": 10, "materialsproject": 10, "pymatgen": [1, 7, 10], "futur": 10, "descript": 10, "crystal": 10, "exactli": 10, "li": 10, "bcc": 10, "cubic": 10, "surfac": 10, "fcc100": 10, "symbol": 10, "fe": 10, "vacuum": 10, "8": 10, "period": 10, "3x3x3": 10, "01": 10, "univers": 10, "itself": 10, "similarli": [1, 7, 10], "strictli": 10, "especi": 10, "across": [1, 7, 10], "previou": 10, "extxyz": 10, "certain": 10, "wildcard": 10, "cif": 10, "cfg": 10, "atoms1": 10, "atoms2": 10, "major": 10, "account": [0, 6, 10], "fact": 10, "control": 10, "verbos": 10, "produc": 10, "addit": [1, 7, 10], "clean": 10, "status": 10, "self": 10, "explanatori": 10, "never": 10, "edit": [0, 6, 10], "tell": 10, "irredem": 10, "delet": 10, "also": [1, 7, 10, 11], "ok": 10, "downstream": 10, "importantli": 10, "should": [1, 7, 10], "purpos": [1, 7, 10], "success": 10, "end_tim": 10, "2023": 10, "08": 10, "28": 10, "21": 10, "50": 10, "51": 10, "368025": 10, "13": 10, "77302319846367": 10, "scinglepoint": 10, "image_output": 10, "372919": 10, "start_tim": 10, "46": 10, "188300": 10, "14": 10, "29": 10, "55": 10, "10": 10, "06": 10, "23": 10, "captur": [10, 11], "It": [1, 7, 10], "extrem": 10, "usual": 10, "lead": 10, "traceback": 10, "caus": 10, "failur": 10, "stderr": 10, "backtrac": 10, "inform": [0, 6, 10], "direct": 10, "stdout": 10, "mostli": 10, "safeti": 10, "measur": 10, "catch": 10, "anyth": 10, "rare": 10, "ha": [1, 7, 10], "unless": 10, "statement": 10, "input_imag": 10, "sure": [0, 1, 6, 7, 10], "artifact": 10, "visual": 10, "flexibl": 10, "standar": 10, "accord": 10, "otherwis": [0, 6, 10], "slurm_": 10, "whose": 10, "mistak": 10, "could": [0, 6, 10], "resolv": 10, "issu": [0, 6, 10], "workdirectori": 10, "why": 10, "fix": 10, "simpli": 10, "rerun": 10, "skip": 10, "becaus": 10, "what": [0, 6, 10], "mere": 10, "offer": 11, "perform": 11, "except": 11, "serv": 11, "advanc": [0, 6, 11], "pleas": [0, 1, 6, 7], "discuss": [0, 6], "wish": [0, 6], "via": [0, 6], "email": [0, 6], "owner": [0, 6], "project": [0, 1, 6, 7], "two": [0, 6], "accordinli": [0, 6], "review": [0, 6], "practic": [0, 1, 6, 7], "ensur": [0, 6], "instal": [0, 6, 8], "remov": [0, 6], "end": [0, 6], "layer": [0, 6], "md": [0, 1, 6, 7], "expos": [0, 6], "port": [0, 6], "locat": [0, 6], "increas": [0, 6], "version": [0, 6], "repres": [0, 6], "scheme": [0, 6], "semver": [0, 6], "onc": [0, 6], "sign": [0, 6], "off": [0, 6], "develop": [0, 6, 8], "permiss": [0, 6], "interest": [0, 6], "foster": [0, 6], "open": [0, 6], "welcom": [0, 6], "contributor": [0, 1, 6, 7], "maintain": [0, 6], "particip": [0, 1, 6, 7], "commun": [0, 6], "harass": [0, 6], "free": [0, 6], "experi": [0, 6, 8], "everyon": [0, 6], "regardless": [0, 6], "bodi": [0, 6], "size": [0, 6], "disabl": [0, 6], "ethnic": [0, 6], "gender": [0, 6], "ident": [0, 6], "express": [0, 6], "nation": [0, 6], "person": [0, 6], "appear": [0, 6], "race": [0, 6], "religion": [0, 6], "sexual": [0, 6], "orient": [0, 6], "behavior": [0, 6], "posit": [0, 6], "inclus": [0, 6], "languag": [0, 6], "Being": [0, 6], "respect": [0, 6], "viewpoint": [0, 6], "gracefulli": [0, 6], "accept": [0, 6], "construct": [0, 6], "critic": [0, 6], "focus": [0, 6], "show": [0, 6], "empathi": [0, 6], "toward": [0, 6], "member": [0, 6], "unaccept": [0, 6], "imageri": [0, 6], "unwelcom": [0, 6], "attent": [0, 6], "troll": [0, 6], "insult": [0, 6], "derogatori": [0, 6], "comment": [0, 6], "polit": [0, 6], "attack": [0, 6], "public": [0, 6], "privat": [0, 6], "publish": [0, 6], "physic": [0, 6], "electron": [0, 6], "address": [0, 6], "explicit": [0, 6], "inappropri": [0, 6], "profession": [0, 6], "clarifi": [0, 6], "fair": [0, 6], "action": [0, 6], "right": [0, 6], "reject": [0, 6], "commit": [0, 6], "align": [0, 6], "ban": [0, 6], "temporarili": [0, 6], "perman": [0, 6], "deem": [0, 6], "threaten": [0, 6], "offens": [0, 6], "harm": [0, 6], "both": [0, 6], "space": [0, 6], "individu": [0, 1, 6, 7], "its": [0, 6], "offici": [0, 6], "mail": [0, 6], "post": [0, 6], "social": [0, 6], "media": [0, 6], "act": [0, 6], "appoint": [0, 6], "onlin": [0, 6], "offlin": [0, 6], "event": [0, 6], "represent": [0, 6], "further": [0, 6], "abus": [0, 6], "report": [0, 6], "contact": [0, 6], "team": [0, 6], "insert": [0, 6], "complaint": [0, 6], "investig": [0, 6], "circumst": [0, 6], "oblig": [0, 6], "confidenti": [0, 6], "regard": [0, 6], "incid": [0, 6], "polici": [0, 6], "separ": [0, 1, 6, 7], "who": [0, 1, 6, 7], "faith": [0, 6], "face": [0, 6], "temporari": [0, 6], "repercuss": [0, 6], "determin": [0, 6], "leadership": [0, 6], "adapt": [0, 6], "coven": [0, 6], "org": [0, 6], "lightweight": [1, 7], "manag": [1, 7], "reproduc": [1, 7], "atomist": [1, 7], "transfer": [1, 7], "By": [1, 7], "recip": [1, 7], "automag": [1, 7], "scale": [1, 7], "cluster": [1, 7], "idea": [1, 7], "therebi": [1, 7], "strict": [1, 7], "consist": [1, 7], "analysi": [1, 7], "pipelin": [1, 7], "goal": [1, 7], "push": [1, 7], "backend": [1, 7], "everydai": [1, 7], "principl": [1, 7], "how": [1, 7], "resembl": [1, 7], "boilerpl": [1, 7], "much": [1, 7], "explicitli": [1, 7], "resubmit": [1, 7], "extens": [1, 7], "importlib": [1, 7], "track": [1, 7], "incorpor": [1, 7], "simulatan": [1, 7], "block": [1, 7], "effici": [1, 7], "friendli": [1, 7], "transit": [1, 7], "websit": [1, 7], "wrapper": [1, 7], "discourag": [1, 7], "long": [1, 7], "benefit": [1, 7], "box": [1, 7], "easili": [1, 7], "instruct": [1, 7], "local": [1, 7], "machin": [1, 7], "9": [1, 7], "forg": [1, 7], "git": [1, 7], "clone": [1, 7], "battmodel": [1, 7], "cd": [1, 7], "pip": [1, 7], "reinstal": [1, 7], "might": [1, 7], "setup": [1, 7], "favorit": [1, 7], "sinc": [1, 7], "commonli": [1, 7], "share": [1, 7], "among": [1, 7], "pytest": [1, 7], "suit": [1, 7], "compon": [1, 7], "test_compon": [1, 7], "our": [1, 7, 8], "conduct": [1, 7, 8], "pull": [1, 7, 8], "u": [1, 7], "keith": [1, 7], "phuthi": [1, 7], "emil": [1, 7], "annevelink": [1, 7], "emilannevelink": [1, 7], "under": [1, 7], "mit": [1, 7], "philosophi": 8, "licens": 8, "pledg": 8, "respons": 8, "scope": 8, "enforc": 8, "attribut": 8}, "objects": {"": [[3, 0, 0, "-", "asimtools"]], "asimtools": [[3, 0, 0, "-", "calculators"], [3, 0, 0, "-", "job"], [4, 0, 0, "-", "scripts"], [3, 0, 0, "-", "utils"]], "asimtools.calculators": [[3, 1, 1, "", "load_ase_calc"], [3, 1, 1, "", "load_calc"], [3, 1, 1, "", "load_dp"], [3, 1, 1, "", "load_nequip"]], "asimtools.job": [[3, 2, 1, "", "ChainedJob"], [3, 2, 1, "", "DistributedJob"], [3, 2, 1, "", "Job"], [3, 2, 1, "", "UnitJob"], [3, 1, 1, "", "load_job_from_directory"]], "asimtools.job.ChainedJob": [[3, 3, 1, "", "get_last_output"], [3, 3, 1, "", "submit"]], "asimtools.job.DistributedJob": [[3, 3, 1, "", "gen_input_files"], [3, 3, 1, "", "submit"], [3, 3, 1, "", "submit_array"], [3, 3, 1, "", "submit_jobs"]], "asimtools.job.Job": [[3, 3, 1, "", "add_output_files"], [3, 3, 1, "", "complete"], [3, 3, 1, "", "discard"], [3, 3, 1, "", "fail"], [3, 3, 1, "", "get_calc_input"], [3, 3, 1, "", "get_env_input"], [3, 3, 1, "", "get_logger"], [3, 3, 1, "", "get_output"], [3, 3, 1, "", "get_output_yaml"], [3, 3, 1, "", "get_sim_input"], [3, 3, 1, "", "get_status"], [3, 3, 1, "", "get_workdir"], [3, 3, 1, "", "go_to_workdir"], [3, 3, 1, "", "leave_workdir"], [3, 3, 1, "", "mkworkdir"], [3, 3, 1, "", "set_workdir"], [3, 3, 1, "", "start"], [3, 3, 1, "", "update_calc_input"], [3, 3, 1, "", "update_env_input"], [3, 3, 1, "", "update_output"], [3, 3, 1, "", "update_sim_input"], [3, 3, 1, "", "update_status"]], "asimtools.job.UnitJob": [[3, 3, 1, "", "gen_input_files"], [3, 3, 1, "", "gen_run_command"], [3, 3, 1, "", "submit"]], "asimtools.scripts": [[4, 0, 0, "-", "asim_check"], [4, 0, 0, "-", "asim_execute"], [4, 0, 0, "-", "asim_run"], [4, 0, 0, "-", "atom_relax"], [4, 0, 0, "-", "calc_array"], [4, 0, 0, "-", "cell_relax"], [4, 0, 0, "-", "chained"], [4, 0, 0, "-", "distributed"], [4, 0, 0, "-", "do_nothing"], [4, 0, 0, "-", "env_array"], [5, 0, 0, "-", "eos"], [4, 0, 0, "-", "image_array"], [4, 0, 0, "-", "lammps"], [4, 0, 0, "-", "parity"], [4, 0, 0, "-", "sim_array"], [4, 0, 0, "-", "singlepoint"], [4, 0, 0, "-", "template"]], "asimtools.scripts.asim_check": [[4, 1, 1, "", "get_status_and_color"], [4, 1, 1, "", "get_subjobs"], [4, 1, 1, "", "load_job_tree"], [4, 1, 1, "", "main"], [4, 1, 1, "", "parse_command_line"], [4, 1, 1, "", "print_job_tree"]], "asimtools.scripts.asim_execute": [[4, 1, 1, "", "main"], [4, 1, 1, "", "parse_command_line"]], "asimtools.scripts.asim_run": [[4, 1, 1, "", "main"], [4, 1, 1, "", "parse_command_line"]], "asimtools.scripts.atom_relax": [[4, 1, 1, "", "atom_relax"]], "asimtools.scripts.calc_array": [[4, 1, 1, "", "calc_array"]], "asimtools.scripts.cell_relax": [[4, 1, 1, "", "cell_relax"]], "asimtools.scripts.chained": [[4, 1, 1, "", "chained"]], "asimtools.scripts.distributed": [[4, 1, 1, "", "distributed"]], "asimtools.scripts.do_nothing": [[4, 1, 1, "", "do_nothing"]], "asimtools.scripts.env_array": [[4, 1, 1, "", "env_array"]], "asimtools.scripts.eos": [[5, 0, 0, "-", "eos"], [5, 0, 0, "-", "postprocess"], [5, 0, 0, "-", "preprocess"]], "asimtools.scripts.eos.eos": [[5, 1, 1, "", "eos"]], "asimtools.scripts.eos.postprocess": [[5, 1, 1, "", "postprocess"]], "asimtools.scripts.eos.preprocess": [[5, 1, 1, "", "preprocess"]], "asimtools.scripts.image_array": [[4, 1, 1, "", "image_array"]], "asimtools.scripts.lammps": [[4, 1, 1, "", "lammps"]], "asimtools.scripts.parity": [[4, 1, 1, "", "calc_parity_data"], [4, 1, 1, "", "parity"], [4, 1, 1, "", "rmse"]], "asimtools.scripts.sim_array": [[4, 1, 1, "", "sim_array"]], "asimtools.scripts.singlepoint": [[4, 1, 1, "", "singlepoint"]], "asimtools.scripts.template": [[4, 1, 1, "", "template"]], "asimtools.utils": [[3, 1, 1, "", "change_dict_value"], [3, 1, 1, "", "check_if_slurm_job_is_running"], [3, 1, 1, "", "get_atoms"], [3, 1, 1, "", "get_axis_lims"], [3, 1, 1, "", "get_calc_input"], [3, 1, 1, "", "get_env_input"], [3, 1, 1, "", "get_images"], [3, 1, 1, "", "get_logger"], [3, 1, 1, "", "join_names"], [3, 1, 1, "", "new_db"], [3, 1, 1, "", "parse_slice"], [3, 1, 1, "", "read_yaml"], [3, 1, 1, "", "strip_symbols"], [3, 1, 1, "", "write_csv_from_dict"], [3, 1, 1, "", "write_yaml"]]}, "objtypes": {"0": "py:module", "1": "py:function", "2": "py:class", "3": "py:method"}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "function", "Python function"], "2": ["py", "class", "Python class"], "3": ["py", "method", "Python method"]}, "titleterms": {"develop": [1, 2, 7], "custom": 2, "script": [2, 4, 5, 10], "asimtool": [3, 4, 5, 6, 8, 9], "packag": [3, 4, 5], "calcul": 3, "modul": [3, 4, 5], "job": [3, 10], "util": 3, "content": [3, 4, 5, 8], "asim_check": 4, "asim_execut": 4, "asim_run": 4, "atom_relax": 4, "calc_arrai": 4, "cell_relax": 4, "chain": 4, "distribut": 4, "do_noth": 4, "env_arrai": 4, "image_arrai": 4, "lammp": 4, "pariti": 4, "sim_arrai": 4, "singlepoint": 4, "templat": 4, "eo": 5, "postprocess": 5, "preprocess": 5, "contribut": [0, 1, 6, 7], "readm": 7, "welcom": 8, "": 8, "document": 8, "indic": 8, "tabl": 8, "run": [1, 7, 10], "an": 10, "exist": 10, "input": 10, "file": 10, "sim_input": 10, "yaml": 10, "env_input": 10, "calc_input": 10, "specifi": 10, "imag": 10, "atom": [1, 7, 10], "usag": 10, "asim": 10, "execut": 10, "output": 10, "check": 10, "statu": 10, "restart": 10, "fail": 10, "import": 10, "function": 10, "from": 10, "us": 11, "built": 11, "workflow": 11, "tool": [1, 7, 11], "pull": [0, 6], "request": [0, 6], "process": [0, 6], "code": [0, 6], "conduct": [0, 6], "our": [0, 6], "pledg": [0, 6], "standard": [0, 6], "respons": [0, 6], "scope": [0, 6], "enforc": [0, 6], "attribut": [0, 6], "simul": [1, 7], "philosophi": [1, 7], "user": [1, 7], "experi": [1, 7], "get": [1, 7], "start": [1, 7], "instal": [1, 7], "test": [1, 7], "author": [1, 7], "licens": [1, 7]}, "envversion": {"sphinx.domains.c": 2, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 8, "sphinx.domains.index": 1, "sphinx.domains.javascript": 2, "sphinx.domains.math": 2, "sphinx.domains.python": 3, "sphinx.domains.rst": 2, "sphinx.domains.std": 2, "sphinx.ext.todo": 2, "sphinx.ext.viewcode": 1, "sphinx.ext.intersphinx": 1, "sphinx": 57}, "alltitles": {"Contributing": [[0, "contributing"], [1, "contributing"], [6, "contributing"], [7, "contributing"]], "Pull Request Process": [[0, "pull-request-process"], [6, "pull-request-process"]], "Code of Conduct": [[0, "code-of-conduct"], [6, "code-of-conduct"]], "Our Pledge": [[0, "our-pledge"], [6, "our-pledge"]], "Our Standards": [[0, "our-standards"], [6, "our-standards"]], "Our Responsibilities": [[0, "our-responsibilities"], [6, "our-responsibilities"]], "Scope": [[0, "scope"], [6, "scope"]], "Enforcement": [[0, "enforcement"], [6, "enforcement"]], "Attribution": [[0, "attribution"], [6, "attribution"]], "Atomic SIMulation Tools": [[1, "atomic-simulation-tools"], [7, "atomic-simulation-tools"]], "Developer philosophy": [[1, "developer-philosophy"], [7, "developer-philosophy"]], "Philosophy on User Experience": [[1, "philosophy-on-user-experience"], [7, "philosophy-on-user-experience"]], "Getting Started": [[1, "getting-started"], [7, "getting-started"]], "Installing": [[1, "installing"], [7, "installing"]], "Running the tests": [[1, "running-the-tests"], [7, "running-the-tests"]], "Authors": [[1, "authors"], [7, "authors"]], "License": [[1, "license"], [7, "license"]], "Developing Custom Scripts": [[2, "developing-custom-scripts"]], "asimtools package": [[3, "asimtools-package"]], "asimtools.calculators module": [[3, "module-asimtools.calculators"]], "asimtools.job module": [[3, "module-asimtools.job"]], "asimtools.utils module": [[3, "module-asimtools.utils"]], "Module contents": [[3, "module-asimtools"], [4, "module-asimtools.scripts"], [5, "module-asimtools.scripts.eos"]], "asimtools.scripts package": [[4, "asimtools-scripts-package"]], "asimtools.scripts.asim_check module": [[4, "module-asimtools.scripts.asim_check"]], "asimtools.scripts.asim_execute module": [[4, "module-asimtools.scripts.asim_execute"]], "asimtools.scripts.asim_run module": [[4, "module-asimtools.scripts.asim_run"]], "asimtools.scripts.atom_relax module": [[4, "module-asimtools.scripts.atom_relax"]], "asimtools.scripts.calc_array module": [[4, "module-asimtools.scripts.calc_array"]], "asimtools.scripts.cell_relax module": [[4, "module-asimtools.scripts.cell_relax"]], "asimtools.scripts.chained module": [[4, "module-asimtools.scripts.chained"]], "asimtools.scripts.distributed module": [[4, "module-asimtools.scripts.distributed"]], "asimtools.scripts.do_nothing module": [[4, "module-asimtools.scripts.do_nothing"]], "asimtools.scripts.env_array module": [[4, "module-asimtools.scripts.env_array"]], "asimtools.scripts.image_array module": [[4, "module-asimtools.scripts.image_array"]], "asimtools.scripts.lammps module": [[4, "module-asimtools.scripts.lammps"]], "asimtools.scripts.parity module": [[4, "module-asimtools.scripts.parity"]], "asimtools.scripts.sim_array module": [[4, "module-asimtools.scripts.sim_array"]], "asimtools.scripts.singlepoint module": [[4, "module-asimtools.scripts.singlepoint"]], "asimtools.scripts.template module": [[4, "module-asimtools.scripts.template"]], "asimtools.scripts.eos package": [[5, "asimtools-scripts-eos-package"]], "asimtools.scripts.eos.eos module": [[5, "module-asimtools.scripts.eos.eos"]], "asimtools.scripts.eos.postprocess module": [[5, "module-asimtools.scripts.eos.postprocess"]], "asimtools.scripts.eos.preprocess module": [[5, "module-asimtools.scripts.eos.preprocess"]], "Contributing to ASIMTools": [[6, "contributing-to-asimtools"]], "README": [[7, "readme"]], "Welcome to asimtools\u2019s documentation!": [[8, "welcome-to-asimtools-s-documentation"]], "Contents:": [[8, null]], "Indices and tables": [[8, "indices-and-tables"]], "asimtools": [[9, "asimtools"]], "Running an existing script": [[10, "running-an-existing-script"]], "Input files": [[10, "input-files"]], "sim_input.yaml": [[10, "sim-input-yaml"]], "env_input.yaml": [[10, "env-input-yaml"]], "calc_input.yaml": [[10, "calc-input-yaml"]], "Specifying Images/Atoms": [[10, "specifying-images-atoms"]], "Usage of asim-execute and asim-run": [[10, "usage-of-asim-execute-and-asim-run"]], "Output files": [[10, "output-files"]], "Checking job status and Restarting failed jobs": [[10, "checking-job-status-and-restarting-failed-jobs"]], "Importing functions from scripts": [[10, "importing-functions-from-scripts"]], "Using built-in workflow tools": [[11, "using-built-in-workflow-tools"]]}, "indexentries": {"chainedjob (class in asimtools.job)": [[3, "asimtools.job.ChainedJob"]], "distributedjob (class in asimtools.job)": [[3, "asimtools.job.DistributedJob"]], "job (class in asimtools.job)": [[3, "asimtools.job.Job"]], "unitjob (class in asimtools.job)": [[3, "asimtools.job.UnitJob"]], "add_output_files() (asimtools.job.job method)": [[3, "asimtools.job.Job.add_output_files"]], "asimtools": [[3, "module-asimtools"]], "asimtools.calculators": [[3, "module-asimtools.calculators"]], "asimtools.job": [[3, "module-asimtools.job"]], "asimtools.utils": [[3, "module-asimtools.utils"]], "change_dict_value() (in module asimtools.utils)": [[3, "asimtools.utils.change_dict_value"]], "check_if_slurm_job_is_running() (in module asimtools.utils)": [[3, "asimtools.utils.check_if_slurm_job_is_running"]], "complete() (asimtools.job.job method)": [[3, "asimtools.job.Job.complete"]], "discard() (asimtools.job.job method)": [[3, "asimtools.job.Job.discard"]], "fail() (asimtools.job.job method)": [[3, "asimtools.job.Job.fail"]], "gen_input_files() (asimtools.job.distributedjob method)": [[3, "asimtools.job.DistributedJob.gen_input_files"]], "gen_input_files() (asimtools.job.unitjob method)": [[3, "asimtools.job.UnitJob.gen_input_files"]], "gen_run_command() (asimtools.job.unitjob method)": [[3, "asimtools.job.UnitJob.gen_run_command"]], "get_atoms() (in module asimtools.utils)": [[3, "asimtools.utils.get_atoms"]], "get_axis_lims() (in module asimtools.utils)": [[3, "asimtools.utils.get_axis_lims"]], "get_calc_input() (asimtools.job.job method)": [[3, "asimtools.job.Job.get_calc_input"]], "get_calc_input() (in module asimtools.utils)": [[3, "asimtools.utils.get_calc_input"]], "get_env_input() (asimtools.job.job method)": [[3, "asimtools.job.Job.get_env_input"]], "get_env_input() (in module asimtools.utils)": [[3, "asimtools.utils.get_env_input"]], "get_images() (in module asimtools.utils)": [[3, "asimtools.utils.get_images"]], "get_last_output() (asimtools.job.chainedjob method)": [[3, "asimtools.job.ChainedJob.get_last_output"]], "get_logger() (asimtools.job.job method)": [[3, "asimtools.job.Job.get_logger"]], "get_logger() (in module asimtools.utils)": [[3, "asimtools.utils.get_logger"]], "get_output() (asimtools.job.job method)": [[3, "asimtools.job.Job.get_output"]], "get_output_yaml() (asimtools.job.job method)": [[3, "asimtools.job.Job.get_output_yaml"]], "get_sim_input() (asimtools.job.job method)": [[3, "asimtools.job.Job.get_sim_input"]], "get_status() (asimtools.job.job method)": [[3, "asimtools.job.Job.get_status"]], "get_workdir() (asimtools.job.job method)": [[3, "asimtools.job.Job.get_workdir"]], "go_to_workdir() (asimtools.job.job method)": [[3, "asimtools.job.Job.go_to_workdir"]], "join_names() (in module asimtools.utils)": [[3, "asimtools.utils.join_names"]], "leave_workdir() (asimtools.job.job method)": [[3, "asimtools.job.Job.leave_workdir"]], "load_ase_calc() (in module asimtools.calculators)": [[3, "asimtools.calculators.load_ase_calc"]], "load_calc() (in module asimtools.calculators)": [[3, "asimtools.calculators.load_calc"]], "load_dp() (in module asimtools.calculators)": [[3, "asimtools.calculators.load_dp"]], "load_job_from_directory() (in module asimtools.job)": [[3, "asimtools.job.load_job_from_directory"]], "load_nequip() (in module asimtools.calculators)": [[3, "asimtools.calculators.load_nequip"]], "mkworkdir() (asimtools.job.job method)": [[3, "asimtools.job.Job.mkworkdir"]], "module": [[3, "module-asimtools"], [3, "module-asimtools.calculators"], [3, "module-asimtools.job"], [3, "module-asimtools.utils"], [4, "module-asimtools.scripts"], [4, "module-asimtools.scripts.asim_check"], [4, "module-asimtools.scripts.asim_execute"], [4, "module-asimtools.scripts.asim_run"], [4, "module-asimtools.scripts.atom_relax"], [4, "module-asimtools.scripts.calc_array"], [4, "module-asimtools.scripts.cell_relax"], [4, "module-asimtools.scripts.chained"], [4, "module-asimtools.scripts.distributed"], [4, "module-asimtools.scripts.do_nothing"], [4, "module-asimtools.scripts.env_array"], [4, "module-asimtools.scripts.image_array"], [4, "module-asimtools.scripts.lammps"], [4, "module-asimtools.scripts.parity"], [4, "module-asimtools.scripts.sim_array"], [4, "module-asimtools.scripts.singlepoint"], [4, "module-asimtools.scripts.template"], [5, "module-asimtools.scripts.eos"], [5, "module-asimtools.scripts.eos.eos"], [5, "module-asimtools.scripts.eos.postprocess"], [5, "module-asimtools.scripts.eos.preprocess"]], "new_db() (in module asimtools.utils)": [[3, "asimtools.utils.new_db"]], "parse_slice() (in module asimtools.utils)": [[3, "asimtools.utils.parse_slice"]], "read_yaml() (in module asimtools.utils)": [[3, "asimtools.utils.read_yaml"]], "set_workdir() (asimtools.job.job method)": [[3, "asimtools.job.Job.set_workdir"]], "start() (asimtools.job.job method)": [[3, "asimtools.job.Job.start"]], "strip_symbols() (in module asimtools.utils)": [[3, "asimtools.utils.strip_symbols"]], "submit() (asimtools.job.chainedjob method)": [[3, "asimtools.job.ChainedJob.submit"]], "submit() (asimtools.job.distributedjob method)": [[3, "asimtools.job.DistributedJob.submit"]], "submit() (asimtools.job.unitjob method)": [[3, "asimtools.job.UnitJob.submit"]], "submit_array() (asimtools.job.distributedjob method)": [[3, "asimtools.job.DistributedJob.submit_array"]], "submit_jobs() (asimtools.job.distributedjob method)": [[3, "asimtools.job.DistributedJob.submit_jobs"]], "update_calc_input() (asimtools.job.job method)": [[3, "asimtools.job.Job.update_calc_input"]], "update_env_input() (asimtools.job.job method)": [[3, "asimtools.job.Job.update_env_input"]], "update_output() (asimtools.job.job method)": [[3, "asimtools.job.Job.update_output"]], "update_sim_input() (asimtools.job.job method)": [[3, "asimtools.job.Job.update_sim_input"]], "update_status() (asimtools.job.job method)": [[3, "asimtools.job.Job.update_status"]], "write_csv_from_dict() (in module asimtools.utils)": [[3, "asimtools.utils.write_csv_from_dict"]], "write_yaml() (in module asimtools.utils)": [[3, "asimtools.utils.write_yaml"]], "asimtools.scripts": [[4, "module-asimtools.scripts"]], "asimtools.scripts.asim_check": [[4, "module-asimtools.scripts.asim_check"]], "asimtools.scripts.asim_execute": [[4, "module-asimtools.scripts.asim_execute"]], "asimtools.scripts.asim_run": [[4, "module-asimtools.scripts.asim_run"]], "asimtools.scripts.atom_relax": [[4, "module-asimtools.scripts.atom_relax"]], "asimtools.scripts.calc_array": [[4, "module-asimtools.scripts.calc_array"]], "asimtools.scripts.cell_relax": [[4, "module-asimtools.scripts.cell_relax"]], "asimtools.scripts.chained": [[4, "module-asimtools.scripts.chained"]], "asimtools.scripts.distributed": [[4, "module-asimtools.scripts.distributed"]], "asimtools.scripts.do_nothing": [[4, "module-asimtools.scripts.do_nothing"]], "asimtools.scripts.env_array": [[4, "module-asimtools.scripts.env_array"]], "asimtools.scripts.image_array": [[4, "module-asimtools.scripts.image_array"]], "asimtools.scripts.lammps": [[4, "module-asimtools.scripts.lammps"]], "asimtools.scripts.parity": [[4, "module-asimtools.scripts.parity"]], "asimtools.scripts.sim_array": [[4, "module-asimtools.scripts.sim_array"]], "asimtools.scripts.singlepoint": [[4, "module-asimtools.scripts.singlepoint"]], "asimtools.scripts.template": [[4, "module-asimtools.scripts.template"]], "atom_relax() (in module asimtools.scripts.atom_relax)": [[4, "asimtools.scripts.atom_relax.atom_relax"]], "calc_array() (in module asimtools.scripts.calc_array)": [[4, "asimtools.scripts.calc_array.calc_array"]], "calc_parity_data() (in module asimtools.scripts.parity)": [[4, "asimtools.scripts.parity.calc_parity_data"]], "cell_relax() (in module asimtools.scripts.cell_relax)": [[4, "asimtools.scripts.cell_relax.cell_relax"]], "chained() (in module asimtools.scripts.chained)": [[4, "asimtools.scripts.chained.chained"]], "distributed() (in module asimtools.scripts.distributed)": [[4, "asimtools.scripts.distributed.distributed"]], "do_nothing() (in module asimtools.scripts.do_nothing)": [[4, "asimtools.scripts.do_nothing.do_nothing"]], "env_array() (in module asimtools.scripts.env_array)": [[4, "asimtools.scripts.env_array.env_array"]], "get_status_and_color() (in module asimtools.scripts.asim_check)": [[4, "asimtools.scripts.asim_check.get_status_and_color"]], "get_subjobs() (in module asimtools.scripts.asim_check)": [[4, "asimtools.scripts.asim_check.get_subjobs"]], "image_array() (in module asimtools.scripts.image_array)": [[4, "asimtools.scripts.image_array.image_array"]], "lammps() (in module asimtools.scripts.lammps)": [[4, "asimtools.scripts.lammps.lammps"]], "load_job_tree() (in module asimtools.scripts.asim_check)": [[4, "asimtools.scripts.asim_check.load_job_tree"]], "main() (in module asimtools.scripts.asim_check)": [[4, "asimtools.scripts.asim_check.main"]], "main() (in module asimtools.scripts.asim_execute)": [[4, "asimtools.scripts.asim_execute.main"]], "main() (in module asimtools.scripts.asim_run)": [[4, "asimtools.scripts.asim_run.main"]], "parity() (in module asimtools.scripts.parity)": [[4, "asimtools.scripts.parity.parity"]], "parse_command_line() (in module asimtools.scripts.asim_check)": [[4, "asimtools.scripts.asim_check.parse_command_line"]], "parse_command_line() (in module asimtools.scripts.asim_execute)": [[4, "asimtools.scripts.asim_execute.parse_command_line"]], "parse_command_line() (in module asimtools.scripts.asim_run)": [[4, "asimtools.scripts.asim_run.parse_command_line"]], "print_job_tree() (in module asimtools.scripts.asim_check)": [[4, "asimtools.scripts.asim_check.print_job_tree"]], "rmse() (in module asimtools.scripts.parity)": [[4, "asimtools.scripts.parity.rmse"]], "sim_array() (in module asimtools.scripts.sim_array)": [[4, "asimtools.scripts.sim_array.sim_array"]], "singlepoint() (in module asimtools.scripts.singlepoint)": [[4, "asimtools.scripts.singlepoint.singlepoint"]], "template() (in module asimtools.scripts.template)": [[4, "asimtools.scripts.template.template"]], "asimtools.scripts.eos": [[5, "module-asimtools.scripts.eos"]], "asimtools.scripts.eos.eos": [[5, "module-asimtools.scripts.eos.eos"]], "asimtools.scripts.eos.postprocess": [[5, "module-asimtools.scripts.eos.postprocess"]], "asimtools.scripts.eos.preprocess": [[5, "module-asimtools.scripts.eos.preprocess"]], "eos() (in module asimtools.scripts.eos.eos)": [[5, "asimtools.scripts.eos.eos.eos"]], "postprocess() (in module asimtools.scripts.eos.postprocess)": [[5, "asimtools.scripts.eos.postprocess.postprocess"]], "preprocess() (in module asimtools.scripts.eos.preprocess)": [[5, "asimtools.scripts.eos.preprocess.preprocess"]]}}) \ No newline at end of file diff --git a/build/usage.html b/build/usage.html deleted file mode 100644 index f57be73..0000000 --- a/build/usage.html +++ /dev/null @@ -1,522 +0,0 @@ - - - - - - - Running an existing script — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

Running an existing script

-

This section will guide you through running an already existing script whether -it be one of the scripts provided in the main repository, from the script -repository or a custom script you have written yourself.

-

To run a simulation, all you need to do is run either of the following -commands:

-
asim-execute sim_input.yaml -c calc_input.yaml -e env_input.yaml
-
-
-

or

-
asim-run sim_input.yaml -c calc_input.yaml
-
-
-

In either case, providing calc_input.yaml or env_input.yaml is optional. If -not provided, the globally configured files will be used. See env_input.yaml. For most -cases, you will only ever use asim-execute. The differences between -asim-execute and asim-run are explained in Usage of asim-execute and asim-run.

-
-

Input files

-
-

sim_input.yaml

-

The minimal requirement to run a script is to provide a sim_input.yaml file. -An example of a sim_input.yaml is shown below:

-
script: singlepoint
-env_id: inline
-overwrite: false
-submit: true
-workdir: results
-precommands:
-    - export MY_ENV_VAR=3
-args:
-    arg1: value_1
-    arg2: value_2
-    ...
-
-
-

The parameters are:

-
    -
  • script: (str) name of core script or /path/to/my/script.py

  • -
  • env_id: (str, optional) Environment/context in which to run script -configured in env_input.yaml, defaults to running in the current console

  • -
  • overwrite: (bool, optional) (bool) whether or not to overwrite work -directories if they exist, defaults to false

  • -
  • submit: (bool, optional) whether or not to run the script or instead just -write the files to run the script without running the script, defaults to true

  • -
  • workdir: (str, optional) The directory in which the script will be run, -asim-execute will create the directory whereas asim-run ignores this -parameter, defaults to ‘./’

  • -
  • precommands: (list, optional) a list of commands to run in the console -before running the script, defaults to empty list

  • -
  • postcommands: (list, optional) a list of commands to run in the console -after running the script, defaults to empty list

  • -
  • args: (dict) The arguments of the function being called in the script as -key-value pairs. These are specific to the script being run.

  • -
-

All ASIMTools generated files are named sim_input.yaml but you can name -user defined files as whatever you like

-
-
-

env_input.yaml

-

Additionally, one can provide an env_input.yaml file that details the kind of -environments in which scripts can be run. This file can be provided with the -asim-execute command using the -e flag or set globally. An example of an -env_input.yaml file is given below

-
# template
-env_id:
-  mode:
-    use_slurm: true
-    interactive: false
-    run_prefix: ...
-    run_postfix: ...
-  slurm:
-    flags: [flag1, flag2, ...]
-    precommands: [precommand1, precommand2, ...]
-    postcommands: [postcommand1, postcommand2, ...]
-
-# Concrete examples below
-inline: # Run the script directly in the commandline
-  mode:
-    use_slurm: false
-    interactive: true
-
-batch_job: # Submit a batch job using slurm with 2 tasks
-  mode:
-    use_slurm: true
-    interactive: false
-  slurm:
-    flags:
-        - -n 2
-    precommands:
-        - source ~/.bashrc
-        - conda activate asimtools
-    postcommands:
-        - conda deactivate asimtools
-
-interactive_job: # Submit an interactive job using slurm
-  mode:
-    use_slurm: true
-    interactive: true
-  slurm:
-    flags:
-      - -n 2
-      - --gres:gpu=2
-    precommands:
-      - module load lammps
-
-
-

The highest level key is the env_id which is specified in the -sim_input.yaml. An env_input.yaml can have any number of env_id s. That -way you can specify one global file if you use the same environments -repeatedly. In particular, you can configure a global config file by setting -the environment variable.

-
export ASIMTOOLS_ENV_INPUT=/path/to/my/global/env_input.yaml
-
-
-

The parameters, required, shown in the template section are are described below

-
    -
  • env_id: (str) unique key for identifying the environment, env_id in -sim_input.yaml must match one of the env_id s defined in the provided -env_input.yaml

  • -
  • env_id.mode.use_slurm: (bool) whether or not to request a slurm -allocation to run the script

  • -
  • env_id.mode.interactive: (bool) whether or not to request a slurm -allocation to run the script directly in the terminal (using salloc) or -to submita batch job (using sbatch)

  • -
  • env_id.mode.run_prefix: (str) string to append before running the script -e.g. if run_prefix=mpirun the script will be inkoked with the equivalent -of mpirun python my_script.py. run_prefix in env_input.yaml is -always prepended before the one provided by calc_input.yaml.

  • -
  • env_id.mode.run_postfix: (str) string to append after running the script -e.g. if run_postfix=' &> out.txt' the script will be inkoked with the -equivalent of python my_script.py &> out.txt. run_postfix in -env_input.yaml is always appended after the one provided by -calc_input.yaml.

  • -
  • env_id.slurm.flags: (list, optional) The slurm flags for the allocation -as a list of flags e.g. [-n 4, -N 1]

  • -
  • env_id.slurm.precommands: (list, optional) Commands to be run/added to -the job script before running the script. A common use cas is loading a -module or activating an environment

  • -
  • env_id.slurm.postcommands: (list, optional) Commands to be run/added to -the job script after running the script.

  • -
-
-
-

calc_input.yaml

-

Lastly the calc_input.yaml is used to configure an ASE calculator. As -above, a global configuration file can be set using

-
export ASIMTOOLS_CALC_INPUT=/path/to/my/global/calc_input.yaml
-
-
-

or provided to asim-execute at run time. Note that if you launch a chained -workflow with asim-run instead of asim-execute, scripts farther down -the chain will use the global calc_input.yaml

-
# Template
-calc_id:
-  name: ...
-  module: ...
-  precommands: [precommand1, ...]
-  postcommands: [postcommand1, ...]
-  run_prefix: ...
-  run_postfix: ...
-  args:
-    arg1: value_1
-    ...
-
-# Concrete examples
-lj:
-  name: LennardJones
-  module: ase.calculators.lj
-  args:
-    sigma: 3.54
-    epsilon: 0.00802236
-
-# GPAW needs a run_prefix to work in parallel using mpirun
-gpaw:
-  name: GPAW
-  module: gpaw.calculator
-  run_prefix: mpirun
-  args:
-    kpts: [2,2,2]
-    h: 0.1
-    xc: PBE
-    txt: gpaw_output.txt
-
-
-
    -
  • calc_id: (str) unique key for identifying the calculator, calc_id in -sim_input.yaml must match one of the calc_id s defined in the -provided calc_input.yaml

  • -
  • calc_id.name: (str) Either the name of the class or the reference to one -of the provided BBBB external calculators.

  • -
  • calc_id.module: (str) The module from which the calculator class is -imported. e.g. if name=LennardJones and module=ase.calculators.lj, -then the calculator object is imported as from ase.calculators.lj import -LennardJones. This works if the calculator is available in ASE or follows -ASE format for initialization such as GPAW. Any other ASE calculator will -need to have the instantiation defined in :ref:calculators.py

  • -
  • calc_id.mode.run_prefix: (str) string to append before running the script -e.g. if run_prefix=mpirun the script will be inkoked with the equivalent -of mpirun python my_script.py. run_prefix in env_input.yaml is -always prepended before the one provided by calc_input.yaml.

  • -
  • calc_id.mode.run_postfix: (str) string to append after running the script -e.g. if run_postfix=' &> out.txt' the script will be inkoked with the -equivalent of python my_script.py &> out.txt. run_postfix in -env_input.yaml is always appended after the one provided by -calc_input.yaml.

  • -
  • calc_id.precommands: (list, optional) Commands to be run/added to the job -script before running the script. A common use cas is loading a module or -activating an environment

  • -
  • calc_id.postcommands: (list, optional) Commands to be run/added to the -job script after running the script.

  • -
  • calc_id.args: (dict) key-value pairs to be passed as arguments for the -initialization of the calculator class. e.g. if the class is LennardJones, -the arguments are passed as calc = LennardJones(**{'sigma':3.2, -'epsilon':3})

  • -
-
-
-

Specifying Images/Atoms

-

One of the most useful applications of ASIMTools is the unification of methods -for setting up ASE atoms objects using the same interface. If a script requires -a single or multiple atoms objects as input, they are provided as either an -image dictionary for a single Atoms object or images for a list of -Atoms objects as part of the args section. Below are all the different ways -to get an atoms object. Downloading images from MaterialsProject and Generating -them from Pymatgen will implemented in future.

-

For a detailed description of the API, see asimtools.utils.get_atoms()

-
# Reading a specific image from a structure file using ase.io.read
-image:
-  image_file: /path/to/my/ASE-readable/image/file.xyz
-  # Optional keyword argument passed to ase.io.read
-  index: 3
-
-# Building a bulk crystal using ase.build.bulk
-image:
-  builder: bulk
-  # Optional keyword arguments passed to the builder, must match ASE exactly
-  name: Li
-  crystalstructure: bcc
-  a: 4.3
-  cubic: True
-
-# Building a surface using ase.build.fcc100
-image:
-  builder: fcc100
-  # Optional keyword arguments passed to the builder, must match ASE exactly
-  symbol: Fe
-  vacuum: 8
-  periodic: False
-
-# Building a 3x3x3 supercell of Ar using ase.build.bulk then
-# Atoms.repeat(repeat) and then applying Atoms.rattle(stdev=rattle_stdev)
-image:
-  name: Ar
-  repeat: [3,3,3]
-  rattle_stdev: 0.01
-
-# You can even supply an atoms object directly so that the interface is
-# universal. This is most useful in the script code itself.
-image:
-  atoms: Atoms
-
-
-

Similarly, if the script requires multiple image inputs, there exists a -universal interface. The keyword is strictly specified as images This is especially useful for distributing simulations -across multiple structures or reading structures from multiple previous -simulations.

-

For a detailed description of the API, see asimtools.utils.get_images()

-
# Reading specific images from a structure file using ase.io.read
-images:
-  image_file: /path/to/my/ASE-readable/image/file.xyz
-  # Optional keyword arguments passed to ase.io.read
-  index: '3:8'
-  format: extxyz
-
-# You can read all files matching a certain pattern using a wildcard
-images:
-  pattern: /path/to/my/structure/files/*.cif
-  # Optional keyword argument passed to ase.io.read
-  index: -1
-
-# You can read all files matching a certain pattern using a wildcard
-images:
-  patterns:
-  - /path/to/my/structure/files/*.cif
-  - /path/to/my/other/structure/files/*.cfg
-
-# You can even supply a list of atoms objects directly so that the interface
-# is universal. This is most useful in the script code itself.
-images:
-  images: [Atoms1, Atoms2, ...]
-
-
-
-
-
-

Usage of asim-execute and asim-run

-

The major difference between asim-execute and asim-run is that, -asim-execute takes into account the workdir and the env_id. -asim-run will the script in the current directory and in the current -console. In fact, asim-execute will create the workdir and then run -asim-run in the correct environment/batch job. You can always for example, -request a slurm allocation, go to the directory where you want the script to be -run and call asim-run from there if you would like more control or to debug. -If you want verbose logs for debugging, you can run with the -d or ---debug flag.

-
-
-

Output files

-

A job or script run through ASIMTools will always produce a standard set of -output files in addition to whatever outputs the script produces. In particular -the most important output are the output.yaml and the job.log file.

-
    -
  1. ``output.yaml`` contains the status of the job being run in the current -directory which can be one of clean, started, complete, failed, discard. -The statuses are self-explanatory, the discard status is never written -by ASIMTools but a user can edit an output.yaml file and change it’s -status to discard to tell ASIMTools to ignore that job in any workflows. -This is common for example if you launch multiple jobs and one of them fails -irredemably. Deleting the directory for that job is also ok if nothing -depends on it downstream. Importantly, any results returned by the function -defined in the script are found in output.yaml. Script main functions -should always return a dictionary for this purpose.

    -

    An example of an output.yaml file is shown below

    -
  2. -
-
# Successful output for singlepoint script
-end_time: 2023-08-28 21:50:51.368025
-energy: 13.77302319846367  #This was added by the scinglepoint script
-files:
-  image: image_output.xyz
-job_ids: '372919'
-start_time: 2023-08-28 21:50:46.188300
-status: complete
-
-# Failed output
-start_time: 14:29:55, 10/06/23
-status: failed
-
-
-
    -
  1. job.log captures the logged output of asim-run or scripts -that use logging. It is extremely useful for debugging as following the logs -starting from the base directory will usually lead you to the correct -traceback that caused the failure.

  2. -
  3. stderr.txt captures errors and backtraces from running scripts. This is -usually the most informative file for debugging. You can be directed to the -correct one by noting errors in job.log

  4. -
  5. stdout.txt captures any stdout from running scripts. It is mostly a -safety measure for catching anything that prints to stdout and rarely has -useful information unless you write a script that uses print statements.

  6. -
  7. input_image.xyz and input_images.xyz capture the images input into -the script. This makes sure there is a concrete artifact for the structure -used in the script for the purposes of visualization and debugging. They are -always in extxyz format as a flexible standar format

  8. -
  9. slurm* are slurm job files which can be named according to flags -specified in env_input.yaml otherwise are named slurm_[job_id].out

  10. -
-
-
-

Checking job status and Restarting failed jobs

-

To check the status of jobs, even complicated chains and distributed jobs, we -provide the asim-check utility which can be run using:

-
asim-check /path/to/sim_input.yaml
-
-
-

This will print the job tree, including statuses and work directories of the -jobs whose root directory is specified as workdir in sim_input.yaml.

-

In many cases, there may be mistakes in one of your configuration files leading to -a failed workflow. In these cases there are a few ways you could resolve the -issue:

-
    -
  • Delete the workdirectory and restart the workflow. This is why it is -recommended that the base sim_input.yaml has workdir set to a new -directory that only has the results of the workflow.

  • -
  • Modify the ASIMTools generated sim_input.yaml to fix the problem. If -there are downstream sim_input.yaml files in a chain, they will have to -be deleted or set overwrite=True. Deleting is recommended for safety -purposes.

  • -
  • Simply rerun asim-execute. This will rerun the jobs, skipping any jobs -with a status of complete or discard. Note that error files are not -deleted so you will have to clear those manually

  • -
-
-
-

Importing functions from scripts

-

Because scripts contain what are merely Python functions, you can always import -them and use them in any other code for example, you can import -asimtools.scripts.singlepoint() and use it as below.

-
from asimtools.scripts.singlepoint import singlepoint
-
-results = singlepoint(image={'name': 'Ar'}, calc_id='lj')
-print(results)
-
-
-
-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/build/workflows.html b/build/workflows.html deleted file mode 100644 index 33af331..0000000 --- a/build/workflows.html +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - - Using built-in workflow tools — asimtools 0.0.0 documentation - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -
-

Using built-in workflow tools

-

ASIMTools offers some standard tools for performing common workflows. These -are:

-
    -
  1. asimtools.scripts.sim_array.sim_array() - Run the same script with one -specified argument of the script iterated over. This is the most useful -script and captures all the others except chained

  2. -
  3. asimtools.scripts.image_array.image_array() - Run the same script -on multiple structures

  4. -
  5. asimtools.scripts.calc_array.calc_array() - Run the same script -using different calculators

  6. -
  7. asimtools.scripts.distributed.distributed() - Run multiple -sim_inputs in parallel

  8. -
  9. asimtools.scripts.chained.chained() - Run scripts one after the -other, e.g. if step 2 results depend on step 1 etc.

  10. -
-

Examples for each type of workflow are given in the examples directory and -documentation can be found in asimtools.scripts. They also serve as templates for -you to build your own workflows directly using asimtools.job.Job() -objects as an advanced user.

-
- - -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/asimplify.rst b/docs/asimplify.rst index 07e9b16..5bc043f 100644 --- a/docs/asimplify.rst +++ b/docs/asimplify.rst @@ -1,13 +1,14 @@ -Developing Custom Scripts -========================= +Developing Custom asimmodules +============================= -This section will guide you through taking your own in-house simulation and -integrating it in to asimtools. The process is designed to be as +This section will guide you through taking your own in-house simulation and +integrating it in to asimtools. The process is designed to be as straight-forward as reasonably possible. As an example, we will ASIMplify the calculation of adsorption_energies as -shown in ASE. To understand the code, visit the ASE `tutorials page `. Ultimately, the -ASIMplified code will work with any element and , any calculator and any +shown in ASE. To understand the code, visit the ASE `tutorials page +`. Ultimately, the +ASIMplified code will work with any element and any calculator and any environment and be fully parallelized job submission in just a few steps! **Use toy example** To make things easier, it is best to first replace the @@ -37,13 +38,12 @@ provided here for reference: db.write(atoms, bm=B) -**Wrap in a function** The workhorse of ASIMTools is the function, any code +**Wrap in a function** The workhorse of ASIMTools is the asimmodule, any code wrapped in a function that returns a dictionary can be run within the ASIMTools framework. The easiest thing to do would be to take the code and -copy and paste it in a function which is defined inside a script with the +copy and paste it in a function which is defined inside an asimmodule with the same name - .. code-block:: from ase.build import bulk @@ -65,12 +65,12 @@ same name return {} -Immediately as it is, this script can be run in ASIMTools. You can run it with -the following sim_input.yaml +Immediately as it is, this is an asimmodule can be run in ASIMTools. You can +run it with the following sim_input.yaml .. code-block:: yaml - script: /path/to/ase_eos.py + asimmodule: /path/to/ase_eos.py env_id: inline workdir: results @@ -81,8 +81,8 @@ then call asim-execute sim_input.yaml Only a little bit more complicated than calling ``python ase_eos.py`` -This script however still depends on specific structures and a specific -calculator. Let's do the easy thing first, let's make the script work with any +This asimmodule however still depends on specific structures and a specific +calculator. Let's do the easy thing first, let's make the asimmodule work with any calculator using a simple change. .. code-block:: @@ -109,8 +109,8 @@ calculator using a simple change. return {} -Just like that we can now run the script with any correctly configure -calculator for the all the structures! We can even now run ``calc_array`` to +Just like that we can now run the asimmodule with any correctly configured +calculator for all the structures! We can even now run ``calc_array`` to iterate getting the results using different calculators. The final change we will make is to parallelize over structures as below @@ -139,13 +139,13 @@ The final change we will make is to parallelize over structures as below return {} -Easy-peasy. We now have a script that works with arbitrary environment, +Easy-peasy. We now have an asimmodule that works with arbitrary environment, arbitrary calculator and arbitrary input structure (Of course the simulation -will fail if we give a bad structure for example) +will fail if we give a bad structure/calculator for example) -We can do some final cleanup of the script so that it sends outputs to -``output.yaml`` and logs some checkpoints. Additionally, any scripts added to -the repository will need clear syntax highlighting and documentation. +We can do some final cleanup of the asimmodule so that it sends outputs to +``output.yaml`` and logs some checkpoints. Additionally, any asimmodules added +to the repository will need clear syntax highlighting and documentation. .. code-block:: @@ -176,7 +176,7 @@ the repository will need clear syntax highlighting and documentation. results = {'v': float(v), 'e': float(e), 'B': float(B)} return results -To run this script on an arbitrary structure say Argon with say the +To run this asimmodule on an arbitrary structure say Argon with say the LennardJones calculator, in a slurm job we can now use the following input files. @@ -184,20 +184,20 @@ sim_input.yaml: .. code-block:: yaml - script: /path/to/ase_eos.py + asimmodule: /path/to/ase_eos.py env_id: batch workdir: results args: image: builder: bulk name: Ar - calc_id: gpaw + calc_id: lj_Ar calc_input.yaml: .. code-block:: yaml - lj: + lj_Ar: name: LennardJones module: ase.calculators.lj args: @@ -227,49 +227,50 @@ env_input.yaml: use_slurm: false interactive: true -Going back to the original problem, we wanted to run the simulation of mulitple +Going back to the original problem, we wanted to run the simulation of multiple different elements with the EMT calculator. To achieve that in parallel, we can -nest the ``ase_eos`` script in a :func:`asimtools.scripts.sim_array.sim_array` script as follows +nest the ``ase_eos`` asimmodule in a +:func:`asimtools.asimmodules.sim_array.sim_array` asimmodule as follows sim_input.yaml: .. code-block:: yaml - script: sim_array + asimmodule: sim_array workdir: results args: key_sequence: ['args', 'image', 'name'] array_values: ['Al', 'Ni', 'Cu', 'Pd', 'Ag', 'Pt', 'Au'] env_ids: 'batch' template_sim_input: - script: /path/to/ase_eos.py + asimmodule: /path/to/ase_eos.py args: calc_id: emt image: builder: bulk crystalstructure: 'fcc' -To make the script easier to access without having to use the full path, you +To make the asimmodule easier to access without having to use the full path, you can set the environment variable .. code-block:: console - export ASIMTOOLS_SCRIPT_DIR=/path/to/my/script/dir/ + export ASIMTOOLS_ASIMMODULE_DIR=/path/to/my/asimmodule/dir/ -You can then move the ``ase_eos.py`` script into a directory called ``ase_eos`` -and place it in the script directory. This allows you to refer to scripts -prepended with the script dir as below +You can then move the ``ase_eos.py`` asimmodule to +``/path/to/my/asimmodule/dir/`` i.e. the asimmodule directory. This allows you +to refer to asimmodules prepended with the asimmodule dir as below .. code-block:: yaml - script: sim_array + asimmodule: sim_array workdir: results args: key_sequence: ['args', 'image', 'name'] array_values: ['Al', 'Ni', 'Cu', 'Pd', 'Ag', 'Pt', 'Au'] env_ids: 'batch' template_sim_input: - script: ase_eos/ase_eos.py + asimmodule: ase_eos/ase_eos.py args: calc_id: emt image: diff --git a/docs/asimtools.asimmodules.benchmarking.rst b/docs/asimtools.asimmodules.benchmarking.rst new file mode 100644 index 0000000..1582ba2 --- /dev/null +++ b/docs/asimtools.asimmodules.benchmarking.rst @@ -0,0 +1,20 @@ +asimtools.asimmodules.benchmarking package +========================================== + + + +asimtools.asimmodules.benchmarking.parity module +------------------------------------------------ + +.. automodule:: asimtools.asimmodules.benchmarking.parity + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: asimtools.asimmodules.benchmarking + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/asimtools.asimmodules.elastic_constants.rst b/docs/asimtools.asimmodules.elastic_constants.rst new file mode 100644 index 0000000..d5619d4 --- /dev/null +++ b/docs/asimtools.asimmodules.elastic_constants.rst @@ -0,0 +1,20 @@ +asimtools.asimmodules.elastic\_constants package +================================================ + + + +asimtools.asimmodules.elastic\_constants.cubic\_energy\_expansion module +------------------------------------------------------------------------ + +.. automodule:: asimtools.asimmodules.elastic_constants.cubic_energy_expansion + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: asimtools.asimmodules.elastic_constants + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/asimtools.asimmodules.eos.rst b/docs/asimtools.asimmodules.eos.rst new file mode 100644 index 0000000..b79c880 --- /dev/null +++ b/docs/asimtools.asimmodules.eos.rst @@ -0,0 +1,20 @@ +asimtools.asimmodules.eos package +================================= + + + +asimtools.asimmodules.eos.postprocess module +-------------------------------------------- + +.. automodule:: asimtools.asimmodules.eos.postprocess + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: asimtools.asimmodules.eos + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/asimtools.asimmodules.geometry_optimization.rst b/docs/asimtools.asimmodules.geometry_optimization.rst new file mode 100644 index 0000000..2133eb5 --- /dev/null +++ b/docs/asimtools.asimmodules.geometry_optimization.rst @@ -0,0 +1,52 @@ +asimtools.asimmodules.geometry\_optimization package +==================================================== + + + +asimtools.asimmodules.geometry\_optimization.ase\_cubic\_eos\_optimization module +--------------------------------------------------------------------------------- + +.. automodule:: asimtools.asimmodules.geometry_optimization.ase_cubic_eos_optimization + :members: + :undoc-members: + :show-inheritance: + +asimtools.asimmodules.geometry\_optimization.atom\_relax module +--------------------------------------------------------------- + +.. automodule:: asimtools.asimmodules.geometry_optimization.atom_relax + :members: + :undoc-members: + :show-inheritance: + +asimtools.asimmodules.geometry\_optimization.cell\_relax module +--------------------------------------------------------------- + +.. automodule:: asimtools.asimmodules.geometry_optimization.cell_relax + :members: + :undoc-members: + :show-inheritance: + +asimtools.asimmodules.geometry\_optimization.optimize module +------------------------------------------------------------ + +.. automodule:: asimtools.asimmodules.geometry_optimization.optimize + :members: + :undoc-members: + :show-inheritance: + +asimtools.asimmodules.geometry\_optimization.symmetric\_cell\_relax module +-------------------------------------------------------------------------- + +.. automodule:: asimtools.asimmodules.geometry_optimization.symmetric_cell_relax + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: asimtools.asimmodules.geometry_optimization + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/asimtools.asimmodules.lammps.rst b/docs/asimtools.asimmodules.lammps.rst new file mode 100644 index 0000000..c4c7427 --- /dev/null +++ b/docs/asimtools.asimmodules.lammps.rst @@ -0,0 +1,20 @@ +asimtools.asimmodules.lammps package +==================================== + + + +asimtools.asimmodules.lammps.lammps module +------------------------------------------ + +.. automodule:: asimtools.asimmodules.lammps.lammps + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: asimtools.asimmodules.lammps + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/asimtools.asimmodules.phonons.rst b/docs/asimtools.asimmodules.phonons.rst new file mode 100644 index 0000000..d7ac070 --- /dev/null +++ b/docs/asimtools.asimmodules.phonons.rst @@ -0,0 +1,20 @@ +asimtools.asimmodules.phonons package +===================================== + + + +asimtools.asimmodules.phonons.ase\_phonons module +------------------------------------------------- + +.. automodule:: asimtools.asimmodules.phonons.ase_phonons + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: asimtools.asimmodules.phonons + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/asimtools.asimmodules.rst b/docs/asimtools.asimmodules.rst new file mode 100644 index 0000000..74e4281 --- /dev/null +++ b/docs/asimtools.asimmodules.rst @@ -0,0 +1,52 @@ +asimtools.asimmodules package +============================= + + + +.. toctree:: + :maxdepth: 4 + + asimtools.asimmodules.benchmarking + asimtools.asimmodules.elastic_constants + asimtools.asimmodules.eos + asimtools.asimmodules.geometry_optimization + asimtools.asimmodules.lammps + asimtools.asimmodules.phonons + asimtools.asimmodules.surface_energies + asimtools.asimmodules.transformations + asimtools.asimmodules.vacancy_formation_energy + asimtools.asimmodules.workflows + + + +asimtools.asimmodules.do\_nothing module +---------------------------------------- + +.. automodule:: asimtools.asimmodules.do_nothing + :members: + :undoc-members: + :show-inheritance: + +asimtools.asimmodules.singlepoint module +---------------------------------------- + +.. automodule:: asimtools.asimmodules.singlepoint + :members: + :undoc-members: + :show-inheritance: + +asimtools.asimmodules.template module +------------------------------------- + +.. automodule:: asimtools.asimmodules.template + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: asimtools.asimmodules + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/asimtools.asimmodules.surface_energies.rst b/docs/asimtools.asimmodules.surface_energies.rst new file mode 100644 index 0000000..07cdae7 --- /dev/null +++ b/docs/asimtools.asimmodules.surface_energies.rst @@ -0,0 +1,20 @@ +asimtools.asimmodules.surface\_energies package +=============================================== + + + +asimtools.asimmodules.surface\_energies.surface\_energies module +---------------------------------------------------------------- + +.. automodule:: asimtools.asimmodules.surface_energies.surface_energies + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: asimtools.asimmodules.surface_energies + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/asimtools.asimmodules.transformations.rst b/docs/asimtools.asimmodules.transformations.rst new file mode 100644 index 0000000..52170fb --- /dev/null +++ b/docs/asimtools.asimmodules.transformations.rst @@ -0,0 +1,20 @@ +asimtools.asimmodules.transformations package +============================================= + + + +asimtools.asimmodules.transformations.scale\_unit\_cells module +--------------------------------------------------------------- + +.. automodule:: asimtools.asimmodules.transformations.scale_unit_cells + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: asimtools.asimmodules.transformations + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/asimtools.asimmodules.vacancy_formation_energy.rst b/docs/asimtools.asimmodules.vacancy_formation_energy.rst new file mode 100644 index 0000000..30cb7a8 --- /dev/null +++ b/docs/asimtools.asimmodules.vacancy_formation_energy.rst @@ -0,0 +1,20 @@ +asimtools.asimmodules.vacancy\_formation\_energy package +======================================================== + + + +asimtools.asimmodules.vacancy\_formation\_energy.vacancy\_formation\_energy module +---------------------------------------------------------------------------------- + +.. automodule:: asimtools.asimmodules.vacancy_formation_energy.vacancy_formation_energy + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: asimtools.asimmodules.vacancy_formation_energy + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/asimtools.asimmodules.workflows.rst b/docs/asimtools.asimmodules.workflows.rst new file mode 100644 index 0000000..137c318 --- /dev/null +++ b/docs/asimtools.asimmodules.workflows.rst @@ -0,0 +1,52 @@ +asimtools.asimmodules.workflows package +======================================= + + + +asimtools.asimmodules.workflows.calc\_array module +-------------------------------------------------- + +.. automodule:: asimtools.asimmodules.workflows.calc_array + :members: + :undoc-members: + :show-inheritance: + +asimtools.asimmodules.workflows.chained module +---------------------------------------------- + +.. automodule:: asimtools.asimmodules.workflows.chained + :members: + :undoc-members: + :show-inheritance: + +asimtools.asimmodules.workflows.distributed module +-------------------------------------------------- + +.. automodule:: asimtools.asimmodules.workflows.distributed + :members: + :undoc-members: + :show-inheritance: + +asimtools.asimmodules.workflows.image\_array module +--------------------------------------------------- + +.. automodule:: asimtools.asimmodules.workflows.image_array + :members: + :undoc-members: + :show-inheritance: + +asimtools.asimmodules.workflows.sim\_array module +------------------------------------------------- + +.. automodule:: asimtools.asimmodules.workflows.sim_array + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: asimtools.asimmodules.workflows + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/asimtools.rst b/docs/asimtools.rst index f555217..4e1ba28 100644 --- a/docs/asimtools.rst +++ b/docs/asimtools.rst @@ -6,7 +6,7 @@ asimtools package .. toctree:: :maxdepth: 4 - asimtools.scripts + asimtools.asimmodules diff --git a/docs/asimtools.scripts.eos.rst b/docs/asimtools.scripts.eos.rst deleted file mode 100644 index 8aba0f2..0000000 --- a/docs/asimtools.scripts.eos.rst +++ /dev/null @@ -1,36 +0,0 @@ -asimtools.scripts.eos package -============================= - - - -asimtools.scripts.eos.eos module --------------------------------- - -.. automodule:: asimtools.scripts.eos.eos - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.eos.postprocess module ----------------------------------------- - -.. automodule:: asimtools.scripts.eos.postprocess - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.eos.preprocess module ---------------------------------------- - -.. automodule:: asimtools.scripts.eos.preprocess - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: asimtools.scripts.eos - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/asimtools.scripts.rst b/docs/asimtools.scripts.rst deleted file mode 100644 index 2cfbf33..0000000 --- a/docs/asimtools.scripts.rst +++ /dev/null @@ -1,147 +0,0 @@ -asimtools.scripts package -========================= - - - -.. toctree:: - :maxdepth: 4 - - asimtools.scripts.eos - - - -asimtools.scripts.asim\_check module ------------------------------------- - -.. automodule:: asimtools.scripts.asim_check - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.asim\_execute module --------------------------------------- - -.. automodule:: asimtools.scripts.asim_execute - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.asim\_run module ----------------------------------- - -.. automodule:: asimtools.scripts.asim_run - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.atom\_relax module ------------------------------------- - -.. automodule:: asimtools.scripts.atom_relax - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.calc\_array module ------------------------------------- - -.. automodule:: asimtools.scripts.calc_array - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.cell\_relax module ------------------------------------- - -.. automodule:: asimtools.scripts.cell_relax - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.chained module --------------------------------- - -.. automodule:: asimtools.scripts.chained - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.distributed module ------------------------------------- - -.. automodule:: asimtools.scripts.distributed - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.do\_nothing module ------------------------------------- - -.. automodule:: asimtools.scripts.do_nothing - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.env\_array module ------------------------------------ - -.. automodule:: asimtools.scripts.env_array - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.image\_array module -------------------------------------- - -.. automodule:: asimtools.scripts.image_array - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.lammps module -------------------------------- - -.. automodule:: asimtools.scripts.lammps - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.parity module -------------------------------- - -.. automodule:: asimtools.scripts.parity - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.sim\_array module ------------------------------------ - -.. automodule:: asimtools.scripts.sim_array - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.singlepoint module ------------------------------------- - -.. automodule:: asimtools.scripts.singlepoint - :members: - :undoc-members: - :show-inheritance: - -asimtools.scripts.template module ---------------------------------- - -.. automodule:: asimtools.scripts.template - :members: - :undoc-members: - :show-inheritance: - -Module contents ---------------- - -.. automodule:: asimtools.scripts - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/index.rst b/docs/index.rst index 06cd649..4e56338 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -11,8 +11,8 @@ Welcome to asimtools's documentation! :caption: Contents: README - Running Scripts - Custom Scripts + Running asimmodules + Custom asimmodules Workflows API Docs Contributing diff --git a/docs/usage.rst b/docs/usage.rst index 6168305..2d6f701 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -1,9 +1,9 @@ -Running an existing script -========================== +Running an Existing asimmodule +============================== -This section will guide you through running an already existing script whether -it be one of the scripts provided in the main repository, from the script -repository or a custom script you have written yourself. +This section will guide you through running an already existing asimmodule +whether it be one of the asimmodules provided in the main repository, from the +asimmodule repository or a custom asimmodule you have written yourself. To run a simulation, all you need to do is run either of the following commands: @@ -12,16 +12,21 @@ commands: asim-execute sim_input.yaml -c calc_input.yaml -e env_input.yaml -or +Providing ``calc_input.yaml`` or ``env_input.yaml`` is optional. If not +provided, the globally configured files will be used. See :ref:`envinput`. This +command will automatically run the specified simulation in the correct +directory and environment. + +A different option is to use the following command: .. code-block:: console - asim-run sim_input.yaml -c calc_input.yaml + asim-execute sim_input.yaml -c calc_input.yaml -e env_input.yaml -In either case, providing ``calc_input.yaml`` or ``env_input.yaml`` is optional. If -not provided, the globally configured files will be used. See :ref:`envinput`. For most -cases, you will only ever use ``asim-execute``. The differences between -``asim-execute`` and ``asim-run`` are explained in :ref:`asimrunvsasimexecute`. +This command will run the simulation in the current directory and environment. +For most cases, you will only ever use ``asim-execute``. The differences +between ``asim-execute`` and ``asim-run`` are explained in +:ref:`asimrunvsasimexecute`. .. _inputs: @@ -33,12 +38,12 @@ Input files sim_input.yaml -------------- -The minimal requirement to run a script is to provide a ``sim_input.yaml`` file. -An example of a ``sim_input.yaml`` is shown below: +The minimal requirement to run an asimmodule is to provide a ``sim_input.yaml`` +file. An example of a ``sim_input.yaml`` is shown below: .. code-block:: yaml - script: singlepoint + asimmodule: singlepoint env_id: inline overwrite: false submit: true @@ -52,22 +57,27 @@ An example of a ``sim_input.yaml`` is shown below: The parameters are: -- **script**: (str) name of core script or /path/to/my/script.py -- **env_id**: (str, optional) Environment/context in which to run script +- **asimmodule**: (str) name of core asimmodule or /path/to/my/asimmodule.py. + Core asimmodules defined in the asimmodules directory can be simply referred + to using Python dot notation. E.g. to specify the + :func:`asimtools.asimmodules.workflows.sim_array` asimmodule, you would + specify `workflows.sim_array`. Any other asimmodule should be specified as + either a full path or a path relative to ``ASIMTOOLS_ASIMMODULE_DIR`` + variable to a python file. E.g. ``my_asimmodules/asim_ple.py`` +- **env_id**: (str, optional) Environment/context in which to run asimmodule configured in env_input.yaml, defaults to running in the current console - **overwrite**: (bool, optional) (bool) whether or not to overwrite work directories if they exist, defaults to false -- **submit**: (bool, optional) whether or not to run the script or instead just - write the files to run the script without running the script, defaults to true -- **workdir**: (str, optional) The directory in which the script will be run, - `asim-execute` will create the directory whereas `asim-run` ignores this - parameter, defaults to './' +- **submit**: (bool, optional) whether running the asimmodule, defaults to true +- **workdir**: (str, optional) The directory in which the asimmodule will be + run, `asim-execute` will create the directory whereas `asim-run` ignores this + parameter, defaults to './results' - **precommands**: (list, optional) a list of commands to run in the console - before running the script, defaults to empty list + before running the asimmodule, defaults to empty list - **postcommands**: (list, optional) a list of commands to run in the console - after running the script, defaults to empty list -- **args**: (dict) The arguments of the function being called in the script as - key-value pairs. These are specific to the script being run. + after running the asimmodule, defaults to empty list +- **args**: (dict) The arguments of the function being called in the asimmodule + as key-value pairs. These are specific to the asimmodule being run. All ASIMTools generated files are named ``sim_input.yaml`` but you can name user defined files as whatever you like @@ -76,10 +86,10 @@ user defined files as whatever you like env_input.yaml -------------- -Additionally, one can provide an ``env_input.yaml`` file that details the kind of -environments in which scripts can be run. This file can be provided with the -asim-execute command using the ``-e`` flag or set globally. An example of an -env_input.yaml file is given below +Additionally, one can provide an ``env_input.yaml`` file that details the kind +of environments in which asimmodules can be run. This file can be provided with +the asim-execute command using the ``-e`` flag or set globally. An example of +an env_input.yaml file is given below .. code-block:: yaml @@ -96,7 +106,7 @@ env_input.yaml file is given below postcommands: [postcommand1, postcommand2, ...] # Concrete examples below - inline: # Run the script directly in the commandline + inline: # Run the asimmodule directly in the console mode: use_slurm: false interactive: true @@ -141,26 +151,28 @@ The parameters, required, shown in the template section are are described below ``sim_input.yaml`` must match one of the ``env_id`` s defined in the provided ``env_input.yaml`` - **env_id.mode.use_slurm**: (bool) whether or not to request a slurm - allocation to run the script + allocation to run the asimmodule - **env_id.mode.interactive**: (bool) whether or not to request a slurm - allocation to run the script directly in the terminal (using ``salloc``) or - to submita batch job (using ``sbatch``) -- **env_id.mode.run_prefix**: (str) string to append before running the script - e.g. if ``run_prefix=mpirun`` the script will be inkoked with the equivalent - of ``mpirun python my_script.py``. ``run_prefix`` in ``env_input.yaml`` is - always prepended before the one provided by ``calc_input.yaml``. -- **env_id.mode.run_postfix**: (str) string to append after running the script - e.g. if ``run_postfix=' &> out.txt'`` the script will be inkoked with the - equivalent of ``python my_script.py &> out.txt``. ``run_postfix`` in - ``env_input.yaml`` is always appended after the one provided by + allocation to run the asimmodule directly in the terminal (using ``salloc``) + or to submita batch job (using ``sbatch``) +- **env_id.mode.run_prefix**: (str) string to append before running the + asimmodule e.g. if ``run_prefix=mpirun`` the asimmodule will be invoked with + the equivalent of ``mpirun python my_asimmodule.py``. ``run_prefix`` in + ``env_input.yaml`` is always prepended before the one provided by ``calc_input.yaml``. -- **env_id.slurm.flags**: (list, optional) The slurm flags for the allocation - as a list of flags e.g. ``[-n 4, -N 1]`` +- **env_id.mode.run_postfix**: (str) string to append after running the + asimmodule e.g. if ``run_postfix=' &> out.txt'`` the asimmodule will be + invoked with the equivalent of ``python my_asimmodule.py &> out.txt``. + ``run_postfix`` in ``env_input.yaml`` is always appended after the one + provided by ``calc_input.yaml``. +- **env_id.slurm.flags**: (list/dict, optional) The slurm flags for the + allocation as a list of flags e.g. ``[-n 4, -N 1]``. One can also specify a + dictionary e.g. ``'{-n': 4, '-N': 1, '--mem':2G}`` - **env_id.slurm.precommands**: (list, optional) Commands to be run/added to - the job script before running the script. A common use cas is loading a - module or activating an environment + the job asimmodule before running the asimmodule. A common use cas is loading + a module or activating an environment - **env_id.slurm.postcommands**: (list, optional) Commands to be run/added to - the job script after running the script. + the job asimmodule after running the asimmodule. .. _calcinput: @@ -174,8 +186,9 @@ above, a global configuration file can be set using export ASIMTOOLS_CALC_INPUT=/path/to/my/global/calc_input.yaml or provided to asim-execute at run time. Note that if you launch a chained -workflow with ``asim-run`` instead of ``asim-execute``, scripts farther down -the chain will use the global ``calc_input.yaml`` +workflow with ``asim-run`` instead of ``asim-execute``, asimmodules farther +down the chain will use the global ``calc_input.yaml``, so always use +``asim-execute`` .. code-block:: yaml @@ -211,32 +224,44 @@ the chain will use the global ``calc_input.yaml`` xc: PBE txt: gpaw_output.txt +The parameters for the calculators provided directly in ASE are specified under +the assumption that the calculator will be initiated as follows: + +.. code-block:: + + from module import name + calc = name(**args) + +This works for all calculators defined in ASE v3.22 and below. For externally +defined calculators, you can submit an issue and we will implement it. For +example, calculators for NequIP and Deep Potential force fields are +implemented. - **calc_id**: (str) unique key for identifying the calculator, ``calc_id`` in ``sim_input.yaml`` must match one of the ``calc_id`` s defined in the provided ``calc_input.yaml`` - **calc_id.name**: (str) Either the name of the class or the reference to one - of the provided BBBB external calculators. + of the provided external calculators. - **calc_id.module**: (str) The module from which the calculator class is imported. e.g. if ``name=LennardJones`` and ``module=ase.calculators.lj``, then the calculator object is imported as ``from ase.calculators.lj import LennardJones``. This works if the calculator is available in ASE or follows ASE format for initialization such as GPAW. Any other ASE calculator will need to have the instantiation defined in :ref:calculators.py -- **calc_id.mode.run_prefix**: (str) string to append before running the script - e.g. if ``run_prefix=mpirun`` the script will be inkoked with the equivalent - of ``mpirun python my_script.py``. ``run_prefix`` in ``env_input.yaml`` is +- **calc_id.mode.run_prefix**: (str) string to append before running the asimmodule + e.g. if ``run_prefix=mpirun`` the asimmodule will be invoked with the equivalent + of ``mpirun python my_asimmodule.py``. ``run_prefix`` in ``env_input.yaml`` is always prepended before the one provided by ``calc_input.yaml``. -- **calc_id.mode.run_postfix**: (str) string to append after running the script - e.g. if ``run_postfix=' &> out.txt'`` the script will be inkoked with the - equivalent of ``python my_script.py &> out.txt``. ``run_postfix`` in +- **calc_id.mode.run_postfix**: (str) string to append after running the asimmodule + e.g. if ``run_postfix=' &> out.txt'`` the asimmodule will be invoked with the + equivalent of ``python my_asimmodule.py &> out.txt``. ``run_postfix`` in ``env_input.yaml`` is always appended after the one provided by ``calc_input.yaml``. - **calc_id.precommands**: (list, optional) Commands to be run/added to the job - script before running the script. A common use cas is loading a module or + asimmodule before running the asimmodule. A common use cas is loading a module or activating an environment - **calc_id.postcommands**: (list, optional) Commands to be run/added to the - job script after running the script. + job asimmodule after running the asimmodule. - **calc_id.args**: (dict) key-value pairs to be passed as arguments for the initialization of the calculator class. e.g. if the class is LennardJones, the arguments are passed as ``calc = LennardJones(**{'sigma':3.2, @@ -248,12 +273,12 @@ Specifying Images/Atoms ----------------------- One of the most useful applications of ASIMTools is the unification of methods -for setting up ASE atoms objects using the same interface. If a script requires -a single or multiple atoms objects as input, they are provided as either an -``image`` dictionary for a single Atoms object or ``images`` for a list of -Atoms objects as part of the ``args`` section. Below are all the different ways -to get an atoms object. Downloading images from MaterialsProject and Generating -them from Pymatgen will implemented in future. +for setting up ASE atoms objects using the same interface. If an asimmodule +requires a single or multiple atoms objects as input, they are provided as +either an ``image`` dictionary for a single Atoms object or ``images`` for a +list of Atoms objects as part of the ``args`` section. Below are the +different ways to get an atoms object. Downloading images from MaterialsProject +and Generating them from Pymatgen will implemented in future. For a detailed description of the API, see :func:`asimtools.utils.get_atoms` @@ -290,14 +315,14 @@ For a detailed description of the API, see :func:`asimtools.utils.get_atoms` rattle_stdev: 0.01 # You can even supply an atoms object directly so that the interface is - # universal. This is most useful in the script code itself. + # universal. This is most useful in the asimmodule code itself. image: atoms: Atoms -Similarly, if the script requires multiple image inputs, there exists a -universal interface. The keyword is strictly specified as ``images`` This is especially useful for distributing simulations -across multiple structures or reading structures from multiple previous -simulations. +Similarly, if the asimmodule requires multiple image inputs, there exists a +universal interface. The keyword is strictly specified as ``images``. This is +especially useful for distributing simulations across multiple structures or +reading structures from multiple previous simulations. For a detailed description of the API, see :func:`asimtools.utils.get_images` @@ -316,14 +341,14 @@ For a detailed description of the API, see :func:`asimtools.utils.get_images` # Optional keyword argument passed to ase.io.read index: -1 - # You can read all files matching a certain pattern using a wildcard + # You can read all files matching certain patterns using a wildcard images: patterns: - /path/to/my/structure/files/*.cif - /path/to/my/other/structure/files/*.cfg # You can even supply a list of atoms objects directly so that the interface - # is universal. This is most useful in the script code itself. + # is universal. This is most useful in the asimmodule code itself. images: images: [Atoms1, Atoms2, ...] @@ -333,21 +358,22 @@ Usage of asim-execute and asim-run ********************************** The major difference between ``asim-execute`` and ``asim-run`` is that, ``asim-execute`` takes into account the ``workdir`` and the ``env_id``. -``asim-run`` will the script in the current directory and in the current -console. In fact, ``asim-execute`` will create the ``workdir`` and then run -``asim-run`` in the correct environment/batch job. You can always for example, -request a slurm allocation, go to the directory where you want the script to be -run and call ``asim-run`` from there if you would like more control or to debug. -If you want verbose logs for debugging, you can run with the ``-d`` or -``--debug`` flag. +``asim-run`` will run the asimmodule in the current directory and in the +current console. In fact, ``asim-execute`` will create the ``workdir`` and then +run ``asim-run`` in the correct environment/batch job. You can always for +example, request a slurm allocation, go to the directory where you want the +asimmodule to be run and call ``asim-run`` from there if you would like more +control or to debug. If you want verbose logs for debugging, you can run with +the ``-d`` or ``--debug`` flag. .. _outputs: Output files -**************** -A job or script run through ASIMTools will always produce a standard set of -output files in addition to whatever outputs the script produces. In particular -the most important output are the ``output.yaml`` and the ``job.log`` file. +************ +A job or asimmodule run through ASIMTools will always produce a standard set of +output files in addition to whatever outputs the asimmodule produces. In +particular the most important outputs are the ``output.yaml`` and the +``job.log`` file. #. \``output.yaml`` contains the status of the job being run in the current directory which can be one of ``clean, started, complete, failed, discard``. @@ -357,16 +383,16 @@ the most important output are the ``output.yaml`` and the ``job.log`` file. This is common for example if you launch multiple jobs and one of them fails irredemably. Deleting the directory for that job is also ok if nothing depends on it downstream. Importantly, any results returned by the function - defined in the script are found in ``output.yaml``. Script main functions - should always return a dictionary for this purpose. + defined in the asimmodule are found in ``output.yaml``. Asimmodule main + functions should always return a dictionary for this purpose. - An example of an ``output.yaml`` file is shown below + An example of an ``output.yaml`` file is shown below. .. code-block:: yaml - # Successful output for singlepoint script + # Successful output for singlepoint asimmodule end_time: 2023-08-28 21:50:51.368025 - energy: 13.77302319846367 #This was added by the scinglepoint script + energy: 13.77302319846367 #This was added by the scinglepoint asimmodule files: image: image_output.xyz job_ids: '372919' @@ -377,23 +403,25 @@ the most important output are the ``output.yaml`` and the ``job.log`` file. start_time: 14:29:55, 10/06/23 status: failed -#. ``job.log`` captures the logged output of ``asim-run`` or scripts +#. ``job.log`` captures the logged output of ``asim-run`` or asimmodules that use logging. It is extremely useful for debugging as following the logs starting from the base directory will usually lead you to the correct traceback that caused the failure. -#. ``stderr.txt`` captures errors and backtraces from running scripts. This is - usually the most informative file for debugging. You can be directed to the - correct one by noting errors in ``job.log`` +#. ``stderr.txt`` captures errors and backtraces from running asimmodules. This + is usually the most informative file for debugging. You can be directed to + the correct one by noting errors in ``job.log`` files. -#. ``stdout.txt`` captures any stdout from running scripts. It is mostly a +#. ``stdout.txt`` captures any stdout from running asimmodules. It is mostly a safety measure for catching anything that prints to stdout and rarely has - useful information unless you write a script that uses ``print`` statements. + useful information unless you write an asimmodule that uses ``print`` + statements. In batch jobs, this output this goes to the slurm job output. #. ``input_image.xyz`` and ``input_images.xyz`` capture the images input into - the script. This makes sure there is a concrete artifact for the structure - used in the script for the purposes of visualization and debugging. They are - always in ``extxyz`` format as a flexible standar format + the asimmodule. This makes sure there is a concrete artifact for the + structure used by the asimmodule for the purposes of visualization and + debugging. They are always in ``extxyz`` format as a flexible standard + format #. ``slurm*`` are slurm job files which can be named according to flags specified in ``env_input.yaml`` otherwise are named ``slurm_[job_id].out`` @@ -402,7 +430,7 @@ the most important output are the ``output.yaml`` and the ``job.log`` file. .. _restarting: Checking job status and Restarting failed jobs -************************************************** +********************************************** To check the status of jobs, even complicated chains and distributed jobs, we provide the ``asim-check`` utility which can be run using: @@ -413,8 +441,8 @@ provide the ``asim-check`` utility which can be run using: This will print the job tree, including statuses and work directories of the jobs whose root directory is specified as ``workdir`` in ``sim_input.yaml``. -In many cases, there may be mistakes in one of your configuration files leading to -a failed workflow. In these cases there are a few ways you could resolve the +In many cases, there may be mistakes in one of your configuration files leading +to a failed workflow. In these cases there are a few ways you could resolve the issue: * Delete the workdirectory and restart the workflow. This is why it is @@ -428,16 +456,16 @@ issue: with a status of ``complete`` or ``discard``. Note that error files are not deleted so you will have to clear those manually -Importing functions from scripts -******************************** +Importing functions from asimmodules +************************************ -Because scripts contain what are merely Python functions, you can always import -them and use them in any other code for example, you can import -:func:`asimtools.scripts.singlepoint` and use it as below. +Because asimmodules contain what are merely Python functions, you can always +import them and use them in any other code for example, you can import +:func:`asimtools.asimmodules.singlepoint` and use it as below. .. code-block:: python - from asimtools.scripts.singlepoint import singlepoint + from asimtools.asimmodules.singlepoint import singlepoint results = singlepoint(image={'name': 'Ar'}, calc_id='lj') print(results) diff --git a/docs/workflows.rst b/docs/workflows.rst index 7e12001..d9fef05 100644 --- a/docs/workflows.rst +++ b/docs/workflows.rst @@ -4,23 +4,25 @@ Using built-in workflow tools ASIMTools offers some standard tools for performing common workflows. These are: -#. :func:`asimtools.scripts.sim_array.sim_array` - Run the same script with one - specified argument of the script iterated over. This is the most useful - script and captures all the others except ``chained`` +#. :func:`asimtools.asimmodules.sim_array.sim_array` - Run the same asimmodule + with one or more specified arguments of the asimmodule iterated over. This + is the most useful asimmodule and can substitute all the others except + ``chained`` -#. :func:`asimtools.scripts.image_array.image_array` - Run the same script - on multiple structures +#. :func:`asimtools.asimmodules.image_array.image_array` - Run the same + asimmodule on multiple images -#. :func:`asimtools.scripts.calc_array.calc_array` - Run the same script - using different calculators +#. :func:`asimtools.asimmodules.calc_array.calc_array` - Run the same + asimmodule using different calc_ids or calculator parameters based on a + template -#. :func:`asimtools.scripts.distributed.distributed` - Run multiple - sim_inputs in parallel +#. :func:`asimtools.asimmodules.distributed.distributed` - Run multiple + sim_inputs in parallel -#. :func:`asimtools.scripts.chained.chained` - Run scripts one after the - other, e.g. if step 2 results depend on step 1 etc. +#. :func:`asimtools.asimmodules.chained.chained` - Run asimmodules one after + the other, e.g. if step 2 results depend on step 1 etc. Examples for each type of workflow are given in the examples directory and -documentation can be found in :mod:`asimtools.scripts`. They also serve as templates for -you to build your own workflows directly using :func:`asimtools.job.Job` -objects as an advanced user. +documentation can be found in :mod:`asimtools.asimmodules`. They also serve as +templates for you to build your own workflows directly using +:func:`asimtools.job.Job` objects as an advanced user. diff --git a/examples/ase_cubic_eos/ase_cubic_eos_sim_input.yaml b/examples/ase_cubic_eos/ase_cubic_eos_sim_input.yaml new file mode 100644 index 0000000..e175500 --- /dev/null +++ b/examples/ase_cubic_eos/ase_cubic_eos_sim_input.yaml @@ -0,0 +1,13 @@ +asimmodule: geometry_optimization.ase_cubic_eos_optimization +workdir: eos_results +args: + calc_id: emt + image: + name: Cu + a: 3.59 + cubic: true + npoints: 5 + eps: 0.02 + plot: true + eos_string: vinet + diff --git a/examples/ase_cubic_eos/run.sh b/examples/ase_cubic_eos/run.sh new file mode 100644 index 0000000..6f28a23 --- /dev/null +++ b/examples/ase_cubic_eos/run.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +# These examples explicitly specify a choice of env_input.yaml and calc_input.yaml +# If you want to use the globally set ones instead, it is not necessary to specify +# them. + +asim-execute ase_cubic_eos_sim_input.yaml -c ../calc_input.yaml diff --git a/examples/ase_cubic_eos_custom/array_ase_eos_sim_input.yaml b/examples/ase_cubic_eos_custom/array_ase_eos_sim_input.yaml new file mode 100644 index 0000000..4dbc5b9 --- /dev/null +++ b/examples/ase_cubic_eos_custom/array_ase_eos_sim_input.yaml @@ -0,0 +1,15 @@ +asimmodule: workflows.sim_array +workdir: array_results +args: + key_sequence: ['args', 'image', 'name'] + array_values: ['Al', 'Ni', 'Cu', 'Pd', 'Ag', 'Pt', 'Au'] + template_sim_input: + asimmodule: ../../ase_eos.py + args: + calc_id: emt + image: + builder: bulk + crystalstructure: fcc + cubic: true + db_file: bulk.db + diff --git a/examples/ase_eos/ase_eos.py b/examples/ase_cubic_eos_custom/ase_eos.py similarity index 94% rename from examples/ase_eos/ase_eos.py rename to examples/ase_cubic_eos_custom/ase_eos.py index 1285ea0..f99cbd0 100644 --- a/examples/ase_eos/ase_eos.py +++ b/examples/ase_cubic_eos_custom/ase_eos.py @@ -1,6 +1,6 @@ ''' Calculate the equation of state, fit it and extract the equilibrium -volume, energy and bulk modulus. This script is based on the example provided +volume, energy and bulk modulus. This asimmodule is based on the example provided on the ASE website: https://wiki.fysik.dtu.dk/ase/tutorials/db/db.html author: mkphuthi@github.com diff --git a/examples/ase_cubic_eos_custom/ase_eos_sim_input.yaml b/examples/ase_cubic_eos_custom/ase_eos_sim_input.yaml new file mode 100644 index 0000000..ee1f78c --- /dev/null +++ b/examples/ase_cubic_eos_custom/ase_eos_sim_input.yaml @@ -0,0 +1,10 @@ +asimmodule: ../ase_eos.py +env_id: inline +workdir: Cu_results +args: + calc_id: emt + image: + name: Cu + cubic: true + db_file: bulk.db + diff --git a/examples/ase_cubic_eos_custom/calc_input.yaml b/examples/ase_cubic_eos_custom/calc_input.yaml new file mode 100644 index 0000000..1546fc1 --- /dev/null +++ b/examples/ase_cubic_eos_custom/calc_input.yaml @@ -0,0 +1,4 @@ +emt: + name: EMT + module: ase.calculators.emt + args: {} diff --git a/examples/ase_cubic_eos_custom/run.sh b/examples/ase_cubic_eos_custom/run.sh new file mode 100644 index 0000000..a2ac3fd --- /dev/null +++ b/examples/ase_cubic_eos_custom/run.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +# These examples explicitly specify a choice of calc_input.yaml +# If you want to use the globally set ones instead, it is not necessary to specify +# them. + +# Example 1: This example uses the custom defined script ase_eos.py to +# calculate the eos for one strucuture + +export ASIMTOOLS_ASIMMODULE_DIR="./" # Set the directory to start looking for assimmodule +asim-execute ase_eos_sim_input.yaml -c calc_input.yaml + +# Example 2: This recreates the ASE example for finding the eos for fcc +# metals, but is now completely parallelized and can be used for any +# structure with cubic symmetry + +asim-execute array_ase_eos_sim_input.yaml -c calc_input.yaml diff --git a/examples/ase_eos/array_ase_eos_sim_input.yaml b/examples/ase_eos/array_ase_eos_sim_input.yaml deleted file mode 100644 index a7c310b..0000000 --- a/examples/ase_eos/array_ase_eos_sim_input.yaml +++ /dev/null @@ -1,15 +0,0 @@ -script: sim_array -workdir: results -args: - key_sequence: ['args', 'image', 'name'] - array_values: ['Al', 'Ni', 'Cu', 'Pd', 'Ag', 'Pt', 'Au'] - env_ids: 'batch' - template_sim_input: - script: /home/mphuthi/tests/asimtools/examples/ase_eos/ase_eos.py - args: - calc_id: emt - image: - builder: bulk - crystalstructure: 'fcc' - db_file: /home/mphuthi/tests/asimtools/examples/ase_eos/results/bulk.db - diff --git a/examples/ase_eos/ase_eos_sim_input.yaml b/examples/ase_eos/ase_eos_sim_input.yaml deleted file mode 100644 index 21e4d29..0000000 --- a/examples/ase_eos/ase_eos_sim_input.yaml +++ /dev/null @@ -1,9 +0,0 @@ -script: ase_eo.py -env_id: inline -workdir: results -args: - calc_id: emt - image: - name: Cu - db_file: /home/mphuthi/tests/asimtools/examples/ase_eos/results/bulk.db - diff --git a/examples/ase_eos/calc_input.yaml b/examples/ase_eos/calc_input.yaml deleted file mode 100644 index 593b430..0000000 --- a/examples/ase_eos/calc_input.yaml +++ /dev/null @@ -1,10 +0,0 @@ -lj: - name: LennardJones - module: ase.calculators.lj - args: - sigma: 3.54 - epsilon: 0.00802236 -emt: - name: EMT - module: ase.calculators.emt - args: {} diff --git a/examples/atom_relax/atom_relax_sim_input.yaml b/examples/atom_relax/atom_relax_sim_input.yaml index 0de60c3..36b32c6 100644 --- a/examples/atom_relax/atom_relax_sim_input.yaml +++ b/examples/atom_relax/atom_relax_sim_input.yaml @@ -1,11 +1,14 @@ -script: atom_relax -env_id: srun -workdir: 'results' -overwrite: true +asimmodule: geometry_optimization.atom_relax +env_id: +workdir: relax_results args: - calc_id: lj + calc_id: lj_Ar image: name: Ar builder: bulk - crystalstructure: 'fcc' - fmax: 0.05 + crystalstructure: fcc + a: 5.3 + rattle_stdev: 0.03 + repeat: [4,4,4] + fmax: 0.01 + optimizer: GPMin diff --git a/examples/atom_relax/run.sh b/examples/atom_relax/run.sh new file mode 100644 index 0000000..80bad3c --- /dev/null +++ b/examples/atom_relax/run.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +# These examples explicitly specify a choice of env_input.yaml and calc_input.yaml +# If you want to use the globally set ones instead, it is not necessary to specify +# them. + +asim-execute atom_relax_sim_input.yaml -c ../calc_input.yaml diff --git a/examples/calc_array/calc_array_calc_id_sim_input.yaml b/examples/calc_array/calc_array_calc_id_sim_input.yaml new file mode 100644 index 0000000..0b678b3 --- /dev/null +++ b/examples/calc_array/calc_array_calc_id_sim_input.yaml @@ -0,0 +1,13 @@ +asimmodule: workflows.calc_array +overwrite: true +workdir: calc_id_results +args: + calc_ids: [emt, lj_Cu] + env_ids: [inline, inline] + subasimmodule_input: + asimmodule: geometry_optimization.atom_relax + args: + image: + name: Cu + rattle_stdev: 0.05 + fmax: 0.03 diff --git a/examples/calc_array/calc_array_elastic_constant_sim_input.yaml b/examples/calc_array/calc_array_elastic_constant_sim_input.yaml new file mode 100644 index 0000000..fc3e243 --- /dev/null +++ b/examples/calc_array/calc_array_elastic_constant_sim_input.yaml @@ -0,0 +1,16 @@ +asimmodule: workflows.calc_array +workdir: env_results +args: + calc_ids: [emt, lj_Cu] + env_ids: [inline, inline] # Must correspond to calc_id if list is given + subasimmodule_input: + asimmodule: elastic_constants.cubic_energy_expansion + args: + calc_id: emt + image: + name: Cu + a: 3.6 + ase_cubic_eos_args: # The minimum for every calculator should be in the e-v range + npoints: 5 + eps: 0.04 + eos_string: sj diff --git a/examples/calc_array/calc_array_keys_sim_input.yaml b/examples/calc_array/calc_array_keys_sim_input.yaml deleted file mode 100644 index 9c7eaa6..0000000 --- a/examples/calc_array/calc_array_keys_sim_input.yaml +++ /dev/null @@ -1,19 +0,0 @@ -script: calc_array -overwrite: true -workdir: results_keys -args: - template_calc_id: 'lj' - key_sequence: ['args', 'sigma'] - array_values: [2.30, 2.40, 2.50] - env_ids: 'inline' - subscript_input: - script: eos.eos - overwrite: true - args: - image: - builder: bulk - name: Cu - nimages: 5 - scale_range: [0.95, 1.05] - singlepoint_env_id: inline - preprocess_env_id: inline diff --git a/examples/calc_array/calc_array_sigma_sim_input.yaml b/examples/calc_array/calc_array_sigma_sim_input.yaml new file mode 100644 index 0000000..4f5cb70 --- /dev/null +++ b/examples/calc_array/calc_array_sigma_sim_input.yaml @@ -0,0 +1,11 @@ +asimmodule: workflows.calc_array +workdir: sigma_results +args: + template_calc_id: lj_Cu + key_sequence: ['args', 'sigma'] + array_values: [2.30, 2.33, 2.36] + subasimmodule_input: + asimmodule: singlepoint + args: + image: + name: Cu diff --git a/examples/calc_array/calc_array_sim_input.yaml b/examples/calc_array/calc_array_sim_input.yaml deleted file mode 100644 index f4c24a9..0000000 --- a/examples/calc_array/calc_array_sim_input.yaml +++ /dev/null @@ -1,30 +0,0 @@ -script: calc_array -overwrite: true -workdir: results -args: - calc_ids: ['emt', 'lj', 'nequip'] - env_ids: ['inline', 'batch', 'gpu'] - subscript_input: - script: eos.eos - overwrite: true - args: - image: - builder: bulk - name: Cu - nimages: 5 - scale_range: [0.95, 1.05] - singlepoint_env_id: batch - preprocess_env_id: batch - # calc_input: - # emt: - # name: EMT - # module: ase.calculators.emt - # args: - # fixed_cutoff: True - # lj: - # name: LennardJones - # module: ase.calculators.lj - # args: - # sigma: 2.338 - # epsilon: 0.409 - diff --git a/examples/calc_array/calc_env_array/sim_input.yaml b/examples/calc_array/calc_env_array/sim_input.yaml deleted file mode 100644 index e2f83a4..0000000 --- a/examples/calc_array/calc_env_array/sim_input.yaml +++ /dev/null @@ -1,29 +0,0 @@ -script: calc_array -overwrite: true -workdir: results -args: - calc_ids: ['emt', 'lj'] - subscript_input: - script: eos.eos - overwrite: true - args: - image: - builder: bulk - name: Cu - nimages: 5 - scale_range: [0.95, 1.05] - singlepoint_env_id: inline - preprocess_env_id: inline - calc_input: - emt: - name: EMT - module: ase.calculators.emt - args: - fixed_cutoff: True - lj: - name: LennardJones - module: ase.calculators.lj - args: - sigma: 2.338 - epsilon: 0.409 - diff --git a/examples/calc_array/run.sh b/examples/calc_array/run.sh new file mode 100644 index 0000000..f2a9815 --- /dev/null +++ b/examples/calc_array/run.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# These examples explicitly specify a choice of calc_input.yaml +# If you want to use the globally set one instead, it is not necessary to +# specify it. + +# Example 1: +# This example runs the same simulation for Cu using different calc_id values +# See sim_array examples for an alternative way to do this +asim-execute calc_array_calc_id_sim_input.yaml -c ../calc_input.yaml + +# Example 2: +# This example runs the same elastic constant simulation for Cu using different calc_id values +asim-execute calc_array_elastic_constant_sim_input.yaml -c ../calc_input.yaml + +# Example 3: +# This example runs the same simulation on Cu using different Lennard-Jones sigma parameters +asim-execute calc_array_sigma_sim_input.yaml -c ../calc_input.yaml -e ../env_input.yaml diff --git a/examples/calc_array/run_slurm.sh b/examples/calc_array/run_slurm.sh new file mode 100644 index 0000000..c1e948c --- /dev/null +++ b/examples/calc_array/run_slurm.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# These examples explicitly specify a choice of calc_input.yaml +# If you want to use the globally set ones instead, it is not necessary to +# specify them. + +# Example 1: +# This example runs the same calculator on FCC Cu with different +# lattice parameters (a). + +asim-execute sim_array_batch_lattice_parameters_sim_input.yaml -c ../calc_input.yaml + +# Example 2: +# This example runs the same calculator on Cu with different +# crystal structures +asim-execute sim_array_batch_crystalstructure_sim_input.yaml -c ../calc_input.yaml + +# Example 3: +# This example runs the same Cu FCC structure with different calculators. +# Notice that you can individually specify the env_id for each calculator, +# this allows for example to have one be DFT and another be a ML potential +# which can have very different hardware and environment requirements. +# For an alternative way to do this that allows iterating over the internal +# calc_input parameters, see the workflows.calc_array asimmodule +asim-execute sim_array_batch_calc_id_sim_input.yaml -c ../calc_input.yaml -e ../env_input.yaml + +# Example 4: +# This example runs the same calculator on different input structure files. +# It also autmatically names the result directories (ids) corresponding to the +# name of the file. You can do this with any input file, not just structures +asim-execute sim_array_batch_image_file_sim_input.yaml -c ../calc_input.yaml -e ../env_input.yaml diff --git a/examples/calc_array/calc_input.yaml b/examples/calc_input.yaml similarity index 60% rename from examples/calc_array/calc_input.yaml rename to examples/calc_input.yaml index 71c59de..c6c3ab8 100644 --- a/examples/calc_array/calc_input.yaml +++ b/examples/calc_input.yaml @@ -1,14 +1,18 @@ -# Cu empirical potentials - emt: name: EMT module: ase.calculators.emt args: fixed_cutoff: True -lj: +lj_Cu: name: LennardJones module: ase.calculators.lj args: sigma: 2.338 epsilon: 0.409 +lj_Ar: + name: LennardJones + module: ase.calculators.lj + args: + sigma: 3.54 + epsilon: 0.00802236 diff --git a/examples/cell_relax/cell_relax_sim_input.yaml b/examples/cell_relax/cell_relax_sim_input.yaml index 5020536..fefaabe 100644 --- a/examples/cell_relax/cell_relax_sim_input.yaml +++ b/examples/cell_relax/cell_relax_sim_input.yaml @@ -1,13 +1,11 @@ -script: cell_relax -env_id: inline -workdir: 'results' -overwrite: true +asimmodule: geometry_optimization.cell_relax # The symmetry might change! +workdir: cell_relax_results args: - calc_id: lj + calc_id: lj_Ar image: name: Ar builder: bulk - crystalstructure: 'fcc' + crystalstructure: fcc a: 5.2 - smax: 0.005 + fmax: 0.005 # See ASE docs StrainFilter for best choices for setting this diff --git a/examples/cell_relax/run.sh b/examples/cell_relax/run.sh new file mode 100644 index 0000000..5589152 --- /dev/null +++ b/examples/cell_relax/run.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +# These examples explicitly specify a choice of calc_input.yaml +# If you want to use the globally set ones instead, it is not necessary to specify +# them. + +# Example 1: This relaxes all cell degrees of freedom using ase.filter.strainfilter +# If you want to maintain symmetry, use +# geometry_optimization.symmetric_cell_relax asimmodule +asim-execute cell_relax_sim_input.yaml -c ../calc_input.yaml diff --git a/examples/chained/chained_batch_sim_input.yaml b/examples/chained/chained_batch_sim_input.yaml new file mode 100644 index 0000000..6dc0af9 --- /dev/null +++ b/examples/chained/chained_batch_sim_input.yaml @@ -0,0 +1,37 @@ +asimmodule: workflows.chained +workdir: chained_results +args: + steps: + step-0: + asimmodule: geometry_optimization.atom_relax + env_id: batch + args: + calc_id: lj_Ar + image: + name: Ar + crystalstructure: fcc + a: 4.0 + repeat: [3,3,3] + rattle_stdev: 0.02 + fmax: 0.01 + optimizer: LBFGS + step-1: + asimmodule: geometry_optimization.cell_relax + env_id: batch + args: + calc_id: lj_Ar + image: + image_file: ../step-0/image_output.xyz + fmax: 0.006 + step-2: + asimmodule: do_nothing + env_id: batch + args: + duration: 20 # To give you a chance to see the job dependencies + step-3: + asimmodule: geometry_optimization.ase_cubic_eos_optimization + env_id: batch + args: + calc_id: lj_Ar + image: + image_file: ../step-1/image_output.xyz diff --git a/examples/chained/chained_sim_input.yaml b/examples/chained/chained_sim_input.yaml index dd90dd0..374b4ac 100644 --- a/examples/chained/chained_sim_input.yaml +++ b/examples/chained/chained_sim_input.yaml @@ -1,34 +1,29 @@ -script: chained -config_id: inline +asimmodule: workflows.chained +workdir: chained_results args: - workdir: results steps: step-0: - script: singlepoint - config_id: batch + asimmodule: geometry_optimization.atom_relax args: + calc_id: lj_Ar image: - name: 'Ar' - properties: ['energy', 'forces'] + name: Ar + crystalstructure: fcc + a: 4.0 + repeat: [3,3,3] + rattle_stdev: 0.02 + fmax: 0.01 + optimizer: LBFGS step-1: - script: singlepoint - config_id: batch + asimmodule: geometry_optimization.cell_relax args: + calc_id: lj_Ar image: - name: 'Ar' - properties: ['energy', 'forces'] + image_file: step-0/image_output.xyz + fmax: 0.006 step-2: - script: singlepoint - config_id: batch + asimmodule: geometry_optimization.ase_cubic_eos_optimization args: + calc_id: lj_Ar image: - name: 'Ar' - properties: ['energy', 'forces'] - step-3: - script: singlepoint - config_id: batch - args: - image: - name: 'Ar' - properties: ['energy', 'forces'] - + image_file: step-1/image_output.xyz diff --git a/examples/chained/chained_srun_sim_input.yaml b/examples/chained/chained_srun_sim_input.yaml new file mode 100644 index 0000000..2a9a975 --- /dev/null +++ b/examples/chained/chained_srun_sim_input.yaml @@ -0,0 +1,37 @@ +asimmodule: workflows.chained +workdir: chained_results +args: + steps: + step-0: + asimmodule: geometry_optimization.atom_relax + env_id: srun + args: + calc_id: lj_Ar + image: + name: Ar + crystalstructure: fcc + a: 4.0 + repeat: [3,3,3] + rattle_stdev: 0.02 + fmax: 0.01 + optimizer: LBFGS + step-1: + asimmodule: geometry_optimization.cell_relax + env_id: srun + args: + calc_id: lj_Ar + image: + image_file: ../step-0/image_output.xyz + fmax: 0.006 + step-2: + asimmodule: do_nothing + env_id: inline # You can mix env types + args: + duration: 20 # To give you a chance to see the job dependencies + step-3: + asimmodule: geometry_optimization.ase_cubic_eos_optimization + env_id: srun + args: + calc_id: lj_Ar + image: + image_file: ../step-1/image_output.xyz diff --git a/examples/chained/lj_calc_input.yaml b/examples/chained/lj_calc_input.yaml deleted file mode 100644 index ac18766..0000000 --- a/examples/chained/lj_calc_input.yaml +++ /dev/null @@ -1,51 +0,0 @@ -batch: - submit: True - overwrite: True - calc: - name: LennardJones - module: ase.calculators.lj - args: - sigma: 3.54 - epsilon: 0.00802236 - job: - use_slurm: true - interactive: false - slurm: - flags: - - -n 2 - - -p idle - - -J chain - precommands: - - source ~/.bashrc - - conda activate asimtools -srun: - submit: True - overwrite: True - calc: - name: LennardJones - module: ase.calculators.lj - args: - sigma: 3.54 - epsilon: 0.00802236 - job: - use_slurm: true - interactive: true - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools -inline: - submit: True - overwrite: True - job: - use_slurm: false - interactive: true - calc: - name: LennardJones - module: ase.calculators.lj - args: - sigma: 3.54 - epsilon: 0.00802236 diff --git a/examples/chained/run.sh b/examples/chained/run.sh new file mode 100644 index 0000000..095bd2a --- /dev/null +++ b/examples/chained/run.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# These examples explicitly specify a choice of calc_input.yaml +# If you want to use the globally set one instead, it is not necessary to +# specify it. + +# Example 1: +# This example runs multiple asimmodules one after the other, stopping on failure +# You can fix the failed job in its workdir or set the output.yaml status to +# "discard" or "complete" and rerun asim-execute to proceed +asim-execute chained_sim_input.yaml -c ../calc_input.yaml + diff --git a/examples/chained/run_slurm.sh b/examples/chained/run_slurm.sh new file mode 100644 index 0000000..f1276e5 --- /dev/null +++ b/examples/chained/run_slurm.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +# These examples explicitly specify a choice of env_input.yaml and calc_input.yaml +# If you want to use the globally set ones instead, it is not necessary to specify +# them. + +# Run the chain of asimmodules in a mix of interactive and inline jobs +asim-execute chained_srun_sim_input.yaml -c ../calc_input.yaml + +# Run the chain of asimmodule modules in batch jobs with dependencies +asim-execute chained_batch_sim_input.yaml -c ../calc_input.yaml diff --git a/examples/clean_all.sh b/examples/clean_all.sh new file mode 100644 index 0000000..f7fa121 --- /dev/null +++ b/examples/clean_all.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# This runs all the examples and might take a while, if any fail, feel free to +# check issues on github or submit a new one + +RUNFILE="run.sh" + +for d in */; do + cd $d + rm -r *results + cd ../ +done diff --git a/examples/distributed/distributed_batch_sim_input.yaml b/examples/distributed/distributed_batch_sim_input.yaml index beb7805..4e3e234 100644 --- a/examples/distributed/distributed_batch_sim_input.yaml +++ b/examples/distributed/distributed_batch_sim_input.yaml @@ -1,26 +1,39 @@ -script: distributed -env_id: inline -workdir: results +asimmodule: workflows.distributed +workdir: distributed_results args: - subscripts: - id-00: &id01 - script: singlepoint + array_max: 2 # You can limit how many slurm jobs are run at the same time here (only for job arrays) + subasimmodules: + id-0000: + asimmodule: singlepoint + env_id: batch #If all env_ids are slurm batch jobs using the same configuration, we automatically use arrays + args: + calc_id: lj_Ar + image: + name: Ar + id-0001: + # Doesn't have to be same asimmodule + asimmodule: geometry_optimization.atom_relax env_id: batch args: - calc_id: lj + calc_id: lj_Ar image: name: Ar - properties: ['energy', 'forces'] - id-01: *id01 - id-02: *id01 - id-03: *id01 - id-04: *id01 - id-05: *id01 - id-06: *id01 - id-07: *id01 - id-08: *id01 - id-09: *id01 - id-10: *id01 - id-11: *id01 - id-12: *id01 - + id-0002: + # This is just the calc_array example copied and pasted! + asimmodule: workflows.calc_array + env_id: batch + args: + calc_ids: [emt, lj_Cu] + env_ids: [inline, inline] # Must correspond to calc_id if list is given + subasimmodule_input: + asimmodule: elastic_constants.cubic_energy_expansion + args: + calc_id: emt + image: + name: Cu + a: 3.6 + cubic: true + ase_cubic_eos_args: # The minimum for every calculator should be in the e-v range + npoints: 5 + eps: 0.04 + eos_string: sj diff --git a/examples/distributed/distributed_mixed_sim_input.yaml b/examples/distributed/distributed_mixed_sim_input.yaml new file mode 100644 index 0000000..938c76b --- /dev/null +++ b/examples/distributed/distributed_mixed_sim_input.yaml @@ -0,0 +1,37 @@ +asimmodule: workflows.distributed +workdir: distributed_results +args: + subasimmodules: + id-0000: + asimmodule: singlepoint + env_id: inline # No array since one of them is not a slurm batch job + args: + calc_id: lj_Ar + image: + name: Ar + id-0001: + # Doesn't have to be same asimmodule + asimmodule: geometry_optimization.atom_relax + env_id: batch + args: + calc_id: lj_Ar + image: + name: Ar + id-0002: + # This is just the calc_array example copied and pasted! + asimmodule: workflows.calc_array + env_id: batch + args: + calc_ids: [emt, lj_Cu] + env_ids: [inline, inline] # Must correspond to calc_id if list is given + subasimmodule_input: + asimmodule: elastic_constants.cubic_energy_expansion + args: + calc_id: emt + image: + name: Cu + a: 3.6 + ase_cubic_eos_args: # The minimum for every calculator should be in the e-v range + npoints: 5 + eps: 0.04 + eos_string: sj diff --git a/examples/distributed/distributed_sim_input.yaml b/examples/distributed/distributed_sim_input.yaml index 004a7ac..21a96b4 100644 --- a/examples/distributed/distributed_sim_input.yaml +++ b/examples/distributed/distributed_sim_input.yaml @@ -1,27 +1,36 @@ -script: distributed -env_id: inline -workdir: results +asimmodule: workflows.distributed +workdir: distributed_results args: - subscripts: - id-00: &id01 - script: singlepoint - env_id: inline - config_id: inline + subasimmodules: + id-0000: + asimmodule: singlepoint args: - calc_id: lj + calc_id: lj_Ar image: name: Ar - properties: ['energy', 'forces'] - id-01: *id01 - id-02: *id01 - id-03: *id01 - id-04: *id01 - id-05: *id01 - id-06: *id01 - id-07: *id01 - id-08: *id01 - id-09: *id01 - id-10: *id01 - id-11: *id01 - id-12: *id01 - + id-0001: + # Doesn't have to be same asimmodule + asimmodule: geometry_optimization.atom_relax + args: + calc_id: lj_Ar + image: + name: Ar + optimizer: BFGS + id-0002: + # This is just the calc_array example copied and pasted! + asimmodule: workflows.calc_array + args: + calc_ids: [emt, lj_Cu] + env_ids: [inline, inline] # Must correspond to calc_id if list is given + subasimmodule_input: + asimmodule: elastic_constants.cubic_energy_expansion + args: + calc_id: emt + image: + name: Cu + a: 3.6 + cubic: true + ase_cubic_eos_args: # The minimum for every calculator should be in the e-v range + npoints: 5 + eps: 0.04 + eos_string: sj diff --git a/examples/distributed/lj_calc_input.yaml b/examples/distributed/lj_calc_input.yaml deleted file mode 100644 index 78b1867..0000000 --- a/examples/distributed/lj_calc_input.yaml +++ /dev/null @@ -1,50 +0,0 @@ -batch: - submit: True - overwrite: True - calc: - name: LennardJones - module: ase.calculators.lj - args: - sigma: 3.54 - epsilon: 0.00802236 - job: - use_slurm: true - interactive: true - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools -srun: - submit: True - overwrite: True - calc: - name: LennardJones - module: ase.calculators.lj - args: - sigma: 3.54 - epsilon: 0.00802236 - job: - use_slurm: true - interactive: false - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools -inline: - submit: True - overwrite: True - job: - use_slurm: false - interactive: true - calc: - name: LennardJones - module: ase.calculators.lj - args: - sigma: 3.54 - epsilon: 0.00802236 diff --git a/examples/distributed/run.sh b/examples/distributed/run.sh new file mode 100644 index 0000000..5c79577 --- /dev/null +++ b/examples/distributed/run.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# These examples explicitly specify a choice of calc_input.yaml +# If you want to use the globally set one instead, it is not necessary to +# specify it. + +# Example 1: +# This example runs multiple asimmodules one after the other, stopping on failure +# You can fix the failed job in its workdir or set the output.yaml status to +# "discard" or "complete" and rerun asim-execute to proceed +asim-execute distributed_sim_input.yaml -c ../calc_input.yaml + diff --git a/examples/distributed/run_slurm.sh b/examples/distributed/run_slurm.sh new file mode 100644 index 0000000..3b16c6d --- /dev/null +++ b/examples/distributed/run_slurm.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +# These examples explicitly specify a choice of env_input.yaml and calc_input.yaml +# If you want to use the globally set ones instead, it is not necessary to specify +# them. + +# Run the asimmodule in an interactive job +asim-execute distributed_batch_sim_input.yaml -c calc_input.yaml + +# Run the asimmodule in a batch job +asim-execute distributed_mixed_sim_input.yaml -c calc_input.yaml diff --git a/examples/do_nothing/do_nothing_sim_input.yaml b/examples/do_nothing/do_nothing_sim_input.yaml new file mode 100644 index 0000000..7a27fe4 --- /dev/null +++ b/examples/do_nothing/do_nothing_sim_input.yaml @@ -0,0 +1,3 @@ +asimmodule: do_nothing +args: + duration: 3 diff --git a/examples/do_nothing/run.sh b/examples/do_nothing/run.sh new file mode 100644 index 0000000..637ba61 --- /dev/null +++ b/examples/do_nothing/run.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +asim-execute do_nothing_sim_input.yaml diff --git a/examples/elastic_constants/elastic_constants_sim_input.yaml b/examples/elastic_constants/elastic_constants_sim_input.yaml new file mode 100644 index 0000000..3b935fb --- /dev/null +++ b/examples/elastic_constants/elastic_constants_sim_input.yaml @@ -0,0 +1,13 @@ +asimmodule: elastic_constants.cubic_energy_expansion +workdir: elastic_constant_results +args: + calc_id: emt + image: # Does not need to be relaxed, will use energy minimum volume from eos + name: Cu + a: 3.59 + cubic: true + ase_cubic_eos_args: # optional, defaults should be fine + npoints: 5 + eps: 0.02 + eos_string: sj + diff --git a/examples/elastic_constants/run.sh b/examples/elastic_constants/run.sh new file mode 100644 index 0000000..582bf14 --- /dev/null +++ b/examples/elastic_constants/run.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +# These examples explicitly specify a choice of env_input.yaml and calc_input.yaml +# If you want to use the globally set ones instead, it is not necessary to specify +# them. + +asim-execute elastic_constants_sim_input.yaml -c ../calc_input.yaml diff --git a/examples/env_array/env_array_sim_input.yaml b/examples/env_array/env_array_sim_input.yaml deleted file mode 100644 index 002aeba..0000000 --- a/examples/env_array/env_array_sim_input.yaml +++ /dev/null @@ -1,59 +0,0 @@ -script: env_array -overwrite: true -workdir: results -args: - subscript_input: - script: singlepoint - calc_id: qe_li - overwrite: true - args: - image: - builder: bulk - name: Li - repeat: [2,2,2] - rattle_stdev: 0.01 - calc_id: qe_li - properties: ['energy', 'forces'] - ids: &id003 ['n1', 'n2', 'n4', 'n8', 'n16'] - env_ids: *id003 - env_input: - n1: - mode: &id001 - use_slurm: true - interactive: false - slurm: - flags: - - -n 1 - - --tmp=40G - precommands: &id002 - - source ~/.bashrc - - conda activate asimtools - n2: - mode: *id001 - slurm: - flags: - - -n 2 - - --tmp=40G - precommands: *id002 - n4: - mode: *id001 - slurm: - flags: - - -n 4 - - --tmp=40G - precommands: *id002 - n8: - mode: *id001 - slurm: - flags: - - -n 8 - - --tmp=40G - precommands: *id002 - n16: - mode: *id001 - slurm: - flags: - - -n 16 - - --tmp=40G - precommands: *id002 - diff --git a/examples/env_array/env_input.yaml b/examples/env_array/env_input.yaml deleted file mode 100644 index 9e5565e..0000000 --- a/examples/env_array/env_input.yaml +++ /dev/null @@ -1,26 +0,0 @@ -batch: - mode: - use_slurm: true - interactive: false - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools -srun: - mode: - use_slurm: true - interactive: true - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools -inline: - mode: - use_slurm: false - interactive: true diff --git a/examples/env_input.yaml b/examples/env_input.yaml new file mode 100644 index 0000000..0410b58 --- /dev/null +++ b/examples/env_input.yaml @@ -0,0 +1,30 @@ +batch: + mode: + use_slurm: true + interactive: false + slurm: + flags: + - -n 1 + - -N 1 + - -J my_batch_job + - --mem=1G + # You can do things like activate environments, set environment variables + # etc. before running the asimmodule using precommands using bash commands + precommands: + - export MY_ASIMTOOLS_ENV_VAR="Hello" + - echo $MY_ASIMTOOLS_ENV_VAR + # You can do things like delete files that take up space etc. after the + # asimmodule is run using postcommands using bash commands + postcommands: + - echo "Bye" +srun: + mode: + use_slurm: true + interactive: true + slurm: + flags: + - -n 1 +inline: + mode: + use_slurm: false + interactive: true diff --git a/examples/eos/eos_sim_input.yaml b/examples/eos/eos_sim_input.yaml index 700fe65..7f6db6a 100644 --- a/examples/eos/eos_sim_input.yaml +++ b/examples/eos/eos_sim_input.yaml @@ -1,4 +1,4 @@ -script: eos.eos +asimmodule: eos.eos env_id: inline workdir: results args: diff --git a/examples/eos/results/output.yaml b/examples/eos/results/output.yaml deleted file mode 100644 index 4ec8f3d..0000000 --- a/examples/eos/results/output.yaml +++ /dev/null @@ -1,5 +0,0 @@ -bulk_modulus: 2.1450870527455046 -equilibrium_energy: -0.06369769932222447 -equilibrium_scale: 1.0042239008583418 -equilibrium_volume: 40.98467183640903 -status: complete diff --git a/examples/eos/results/step-0/calc_input.yaml b/examples/eos/results/step-0/calc_input.yaml deleted file mode 100644 index f2367f8..0000000 --- a/examples/eos/results/step-0/calc_input.yaml +++ /dev/null @@ -1,50 +0,0 @@ -batch: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: true - use_slurm: true - overwrite: true - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools - submit: true -inline: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: true - use_slurm: false - overwrite: true - submit: true -srun: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: false - use_slurm: true - overwrite: true - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools - submit: true diff --git a/examples/eos/results/step-0/id-0_x0.95/calc_input.yaml b/examples/eos/results/step-0/id-0_x0.95/calc_input.yaml deleted file mode 100644 index f2367f8..0000000 --- a/examples/eos/results/step-0/id-0_x0.95/calc_input.yaml +++ /dev/null @@ -1,50 +0,0 @@ -batch: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: true - use_slurm: true - overwrite: true - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools - submit: true -inline: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: true - use_slurm: false - overwrite: true - submit: true -srun: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: false - use_slurm: true - overwrite: true - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools - submit: true diff --git a/examples/eos/results/step-0/id-0_x0.95/image_input.xyz b/examples/eos/results/step-0/id-0_x0.95/image_input.xyz deleted file mode 100644 index 1fe3282..0000000 --- a/examples/eos/results/step-0/id-0_x0.95/image_input.xyz +++ /dev/null @@ -1,3 +0,0 @@ -1 -Lattice="0.0 2.58875 2.58875 2.58875 0.0 2.58875 2.58875 2.58875 0.0" Properties=species:S:1:pos:R:3 pbc="T T T" -Ar 0.00000000 0.00000000 0.00000000 diff --git a/examples/eos/results/step-0/id-0_x0.95/image_output.xyz b/examples/eos/results/step-0/id-0_x0.95/image_output.xyz deleted file mode 100644 index dd87c7a..0000000 --- a/examples/eos/results/step-0/id-0_x0.95/image_output.xyz +++ /dev/null @@ -1,3 +0,0 @@ -1 -Lattice="0.0 2.58875 2.58875 2.58875 0.0 2.58875 2.58875 2.58875 0.0" Properties=species:S:1:pos:R:3:forces:R:3:stresses:R:6:energies:R:1 energy=-0.053201752990899645 stress="-0.004250892646061392 1.253593350375051e-19 1.2691183158014984e-19 1.253593350375051e-19 -0.004250892646061393 -2.2140478015060673e-19 1.2691183158014984e-19 -2.2140478015060673e-19 -0.004250892646061394" free_energy=-0.053201752990899645 pbc="T T T" -Ar 0.00000000 0.00000000 0.00000000 -0.00000000 -0.00000000 0.00000000 -0.00425089 -0.00425089 -0.00425089 -0.00000000 0.00000000 0.00000000 -0.05320175 diff --git a/examples/eos/results/step-0/id-0_x0.95/output.yaml b/examples/eos/results/step-0/id-0_x0.95/output.yaml deleted file mode 100644 index 5204c6a..0000000 --- a/examples/eos/results/step-0/id-0_x0.95/output.yaml +++ /dev/null @@ -1,4 +0,0 @@ -energy: -0.053201752990899645 -files: - image: image_output.xyz -status: complete diff --git a/examples/eos/results/step-0/id-0_x0.95/sim_input.yaml b/examples/eos/results/step-0/id-0_x0.95/sim_input.yaml deleted file mode 100644 index 2c3b42d..0000000 --- a/examples/eos/results/step-0/id-0_x0.95/sim_input.yaml +++ /dev/null @@ -1,8 +0,0 @@ -args: - image: - image_file: image_input.xyz - properties: - - energy -config_id: inline -script: singlepoint -workdir: id-0_x0.95 diff --git a/examples/eos/results/step-0/id-1_x0.97/calc_input.yaml b/examples/eos/results/step-0/id-1_x0.97/calc_input.yaml deleted file mode 100644 index f2367f8..0000000 --- a/examples/eos/results/step-0/id-1_x0.97/calc_input.yaml +++ /dev/null @@ -1,50 +0,0 @@ -batch: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: true - use_slurm: true - overwrite: true - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools - submit: true -inline: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: true - use_slurm: false - overwrite: true - submit: true -srun: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: false - use_slurm: true - overwrite: true - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools - submit: true diff --git a/examples/eos/results/step-0/id-1_x0.97/image_input.xyz b/examples/eos/results/step-0/id-1_x0.97/image_input.xyz deleted file mode 100644 index ddb057c..0000000 --- a/examples/eos/results/step-0/id-1_x0.97/image_input.xyz +++ /dev/null @@ -1,3 +0,0 @@ -1 -Lattice="0.0 2.656875 2.656875 2.656875 0.0 2.656875 2.656875 2.656875 0.0" Properties=species:S:1:pos:R:3 pbc="T T T" -Ar 0.00000000 0.00000000 0.00000000 diff --git a/examples/eos/results/step-0/id-1_x0.97/image_output.xyz b/examples/eos/results/step-0/id-1_x0.97/image_output.xyz deleted file mode 100644 index 3963a91..0000000 --- a/examples/eos/results/step-0/id-1_x0.97/image_output.xyz +++ /dev/null @@ -1,3 +0,0 @@ -1 -Lattice="0.0 2.656875 2.656875 2.656875 0.0 2.656875 2.656875 2.656875 0.0" Properties=species:S:1:pos:R:3:forces:R:3:stresses:R:6:energies:R:1 energy=-0.061137210662256664 stress="-0.001659447787586583 -1.7731142139050546e-20 -1.7394419032551887e-20 -1.7731142139050546e-20 -0.0016594477875865826 2.3586980259816617e-20 -1.7394419032551887e-20 2.3586980259816617e-20 -0.0016594477875865824" free_energy=-0.061137210662256664 pbc="T T T" -Ar 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 -0.00000000 -0.00165945 -0.00165945 -0.00165945 0.00000000 -0.00000000 -0.00000000 -0.06113721 diff --git a/examples/eos/results/step-0/id-1_x0.97/output.yaml b/examples/eos/results/step-0/id-1_x0.97/output.yaml deleted file mode 100644 index 2a3a74c..0000000 --- a/examples/eos/results/step-0/id-1_x0.97/output.yaml +++ /dev/null @@ -1,4 +0,0 @@ -energy: -0.061137210662256664 -files: - image: image_output.xyz -status: complete diff --git a/examples/eos/results/step-0/id-1_x0.97/sim_input.yaml b/examples/eos/results/step-0/id-1_x0.97/sim_input.yaml deleted file mode 100644 index c1c6164..0000000 --- a/examples/eos/results/step-0/id-1_x0.97/sim_input.yaml +++ /dev/null @@ -1,8 +0,0 @@ -args: - image: - image_file: image_input.xyz - properties: - - energy -config_id: inline -script: singlepoint -workdir: id-1_x0.97 diff --git a/examples/eos/results/step-0/id-2_x1.00/calc_input.yaml b/examples/eos/results/step-0/id-2_x1.00/calc_input.yaml deleted file mode 100644 index f2367f8..0000000 --- a/examples/eos/results/step-0/id-2_x1.00/calc_input.yaml +++ /dev/null @@ -1,50 +0,0 @@ -batch: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: true - use_slurm: true - overwrite: true - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools - submit: true -inline: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: true - use_slurm: false - overwrite: true - submit: true -srun: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: false - use_slurm: true - overwrite: true - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools - submit: true diff --git a/examples/eos/results/step-0/id-2_x1.00/image_input.xyz b/examples/eos/results/step-0/id-2_x1.00/image_input.xyz deleted file mode 100644 index 5cc63dd..0000000 --- a/examples/eos/results/step-0/id-2_x1.00/image_input.xyz +++ /dev/null @@ -1,3 +0,0 @@ -1 -Lattice="0.0 2.725 2.725 2.725 0.0 2.725 2.725 2.725 0.0" Properties=species:S:1:pos:R:3 pbc="T T T" -Ar 0.00000000 0.00000000 0.00000000 diff --git a/examples/eos/results/step-0/id-2_x1.00/image_output.xyz b/examples/eos/results/step-0/id-2_x1.00/image_output.xyz deleted file mode 100644 index bead419..0000000 --- a/examples/eos/results/step-0/id-2_x1.00/image_output.xyz +++ /dev/null @@ -1,3 +0,0 @@ -1 -Lattice="0.0 2.725 2.725 2.725 0.0 2.725 2.725 2.725 0.0" Properties=species:S:1:pos:R:3:forces:R:3:stresses:R:6:energies:R:1 energy=-0.06363244367715234 stress="-0.00018204043822913946 -4.611716440476923e-20 -1.329158102790944e-19 -4.611716440476923e-20 -0.00018204043822913946 1.0810989963359191e-19 -1.329158102790944e-19 1.0810989963359191e-19 -0.00018204043822913946" free_energy=-0.06363244367715234 pbc="T T T" -Ar 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000 -0.00000000 -0.00018204 -0.00018204 -0.00018204 0.00000000 -0.00000000 -0.00000000 -0.06363244 diff --git a/examples/eos/results/step-0/id-2_x1.00/output.yaml b/examples/eos/results/step-0/id-2_x1.00/output.yaml deleted file mode 100644 index 9801ab1..0000000 --- a/examples/eos/results/step-0/id-2_x1.00/output.yaml +++ /dev/null @@ -1,4 +0,0 @@ -energy: -0.06363244367715234 -files: - image: image_output.xyz -status: complete diff --git a/examples/eos/results/step-0/id-2_x1.00/sim_input.yaml b/examples/eos/results/step-0/id-2_x1.00/sim_input.yaml deleted file mode 100644 index 618fe6f..0000000 --- a/examples/eos/results/step-0/id-2_x1.00/sim_input.yaml +++ /dev/null @@ -1,8 +0,0 @@ -args: - image: - image_file: image_input.xyz - properties: - - energy -config_id: inline -script: singlepoint -workdir: id-2_x1.00 diff --git a/examples/eos/results/step-0/id-3_x1.02/calc_input.yaml b/examples/eos/results/step-0/id-3_x1.02/calc_input.yaml deleted file mode 100644 index f2367f8..0000000 --- a/examples/eos/results/step-0/id-3_x1.02/calc_input.yaml +++ /dev/null @@ -1,50 +0,0 @@ -batch: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: true - use_slurm: true - overwrite: true - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools - submit: true -inline: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: true - use_slurm: false - overwrite: true - submit: true -srun: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: false - use_slurm: true - overwrite: true - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools - submit: true diff --git a/examples/eos/results/step-0/id-3_x1.02/image_input.xyz b/examples/eos/results/step-0/id-3_x1.02/image_input.xyz deleted file mode 100644 index d983807..0000000 --- a/examples/eos/results/step-0/id-3_x1.02/image_input.xyz +++ /dev/null @@ -1,3 +0,0 @@ -1 -Lattice="0.0 2.793125 2.793125 2.793125 0.0 2.793125 2.793125 2.793125 0.0" Properties=species:S:1:pos:R:3 pbc="T T T" -Ar 0.00000000 0.00000000 0.00000000 diff --git a/examples/eos/results/step-0/id-3_x1.02/image_output.xyz b/examples/eos/results/step-0/id-3_x1.02/image_output.xyz deleted file mode 100644 index 108be98..0000000 --- a/examples/eos/results/step-0/id-3_x1.02/image_output.xyz +++ /dev/null @@ -1,3 +0,0 @@ -1 -Lattice="0.0 2.793125 2.793125 2.793125 0.0 2.793125 2.793125 2.793125 0.0" Properties=species:S:1:pos:R:3:forces:R:3:stresses:R:6:energies:R:1 energy=-0.06280737756859853 stress="0.0006199639164565369 -1.506983292711994e-20 4.955010065276568e-21 -1.506983292711994e-20 0.0006199639164565369 4.103546990505632e-23 4.955010065276568e-21 4.103546990505632e-23 0.000619963916456537" free_energy=-0.06280737756859853 pbc="T T T" -Ar 0.00000000 0.00000000 0.00000000 0.00000000 -0.00000000 0.00000000 0.00061996 0.00061996 0.00061996 0.00000000 0.00000000 -0.00000000 -0.06280738 diff --git a/examples/eos/results/step-0/id-3_x1.02/output.yaml b/examples/eos/results/step-0/id-3_x1.02/output.yaml deleted file mode 100644 index 66b3a4c..0000000 --- a/examples/eos/results/step-0/id-3_x1.02/output.yaml +++ /dev/null @@ -1,4 +0,0 @@ -energy: -0.06280737756859853 -files: - image: image_output.xyz -status: complete diff --git a/examples/eos/results/step-0/id-3_x1.02/sim_input.yaml b/examples/eos/results/step-0/id-3_x1.02/sim_input.yaml deleted file mode 100644 index 9ec37d0..0000000 --- a/examples/eos/results/step-0/id-3_x1.02/sim_input.yaml +++ /dev/null @@ -1,8 +0,0 @@ -args: - image: - image_file: image_input.xyz - properties: - - energy -config_id: inline -script: singlepoint -workdir: id-3_x1.02 diff --git a/examples/eos/results/step-0/id-4_x1.05/calc_input.yaml b/examples/eos/results/step-0/id-4_x1.05/calc_input.yaml deleted file mode 100644 index f2367f8..0000000 --- a/examples/eos/results/step-0/id-4_x1.05/calc_input.yaml +++ /dev/null @@ -1,50 +0,0 @@ -batch: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: true - use_slurm: true - overwrite: true - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools - submit: true -inline: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: true - use_slurm: false - overwrite: true - submit: true -srun: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: false - use_slurm: true - overwrite: true - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools - submit: true diff --git a/examples/eos/results/step-0/id-4_x1.05/image_input.xyz b/examples/eos/results/step-0/id-4_x1.05/image_input.xyz deleted file mode 100644 index 3b3ba9c..0000000 --- a/examples/eos/results/step-0/id-4_x1.05/image_input.xyz +++ /dev/null @@ -1,3 +0,0 @@ -1 -Lattice="0.0 2.86125 2.86125 2.86125 0.0 2.86125 2.86125 2.86125 0.0" Properties=species:S:1:pos:R:3 pbc="T T T" -Ar 0.00000000 0.00000000 0.00000000 diff --git a/examples/eos/results/step-0/id-4_x1.05/image_output.xyz b/examples/eos/results/step-0/id-4_x1.05/image_output.xyz deleted file mode 100644 index b1c36c6..0000000 --- a/examples/eos/results/step-0/id-4_x1.05/image_output.xyz +++ /dev/null @@ -1,3 +0,0 @@ -1 -Lattice="0.0 2.86125 2.86125 2.86125 0.0 2.86125 2.86125 2.86125 0.0" Properties=species:S:1:pos:R:3:forces:R:3:stresses:R:6:energies:R:1 energy=-0.06009165705217707 stress="0.0009758698217740892 1.3936847276307408e-20 2.654811294380512e-21 1.3936847276307408e-20 0.0009758698217740892 -2.7533147049398793e-20 2.654811294380512e-21 -2.7533147049398793e-20 0.0009758698217740893" free_energy=-0.06009165705217707 pbc="T T T" -Ar 0.00000000 0.00000000 0.00000000 0.00000000 -0.00000000 -0.00000000 0.00097587 0.00097587 0.00097587 -0.00000000 0.00000000 0.00000000 -0.06009166 diff --git a/examples/eos/results/step-0/id-4_x1.05/output.yaml b/examples/eos/results/step-0/id-4_x1.05/output.yaml deleted file mode 100644 index 9b44f71..0000000 --- a/examples/eos/results/step-0/id-4_x1.05/output.yaml +++ /dev/null @@ -1,4 +0,0 @@ -energy: -0.06009165705217707 -files: - image: image_output.xyz -status: complete diff --git a/examples/eos/results/step-0/id-4_x1.05/sim_input.yaml b/examples/eos/results/step-0/id-4_x1.05/sim_input.yaml deleted file mode 100644 index a27f929..0000000 --- a/examples/eos/results/step-0/id-4_x1.05/sim_input.yaml +++ /dev/null @@ -1,8 +0,0 @@ -args: - image: - image_file: image_input.xyz - properties: - - energy -config_id: inline -script: singlepoint -workdir: id-4_x1.05 diff --git a/examples/eos/results/step-0/image_input.xyz b/examples/eos/results/step-0/image_input.xyz deleted file mode 100644 index 5cc63dd..0000000 --- a/examples/eos/results/step-0/image_input.xyz +++ /dev/null @@ -1,3 +0,0 @@ -1 -Lattice="0.0 2.725 2.725 2.725 0.0 2.725 2.725 2.725 0.0" Properties=species:S:1:pos:R:3 pbc="T T T" -Ar 0.00000000 0.00000000 0.00000000 diff --git a/examples/eos/results/step-0/input_images.xyz b/examples/eos/results/step-0/input_images.xyz deleted file mode 100644 index eafc58c..0000000 --- a/examples/eos/results/step-0/input_images.xyz +++ /dev/null @@ -1,15 +0,0 @@ -1 -Lattice="0.0 2.58875 2.58875 2.58875 0.0 2.58875 2.58875 2.58875 0.0" Properties=species:S:1:pos:R:3 pbc="T T T" -Ar 0.00000000 0.00000000 0.00000000 -1 -Lattice="0.0 2.656875 2.656875 2.656875 0.0 2.656875 2.656875 2.656875 0.0" Properties=species:S:1:pos:R:3 pbc="T T T" -Ar 0.00000000 0.00000000 0.00000000 -1 -Lattice="0.0 2.725 2.725 2.725 0.0 2.725 2.725 2.725 0.0" Properties=species:S:1:pos:R:3 pbc="T T T" -Ar 0.00000000 0.00000000 0.00000000 -1 -Lattice="0.0 2.793125 2.793125 2.793125 0.0 2.793125 2.793125 2.793125 0.0" Properties=species:S:1:pos:R:3 pbc="T T T" -Ar 0.00000000 0.00000000 0.00000000 -1 -Lattice="0.0 2.86125 2.86125 2.86125 0.0 2.86125 2.86125 2.86125 0.0" Properties=species:S:1:pos:R:3 pbc="T T T" -Ar 0.00000000 0.00000000 0.00000000 diff --git a/examples/eos/results/step-0/output.yaml b/examples/eos/results/step-0/output.yaml deleted file mode 100644 index 117cd08..0000000 --- a/examples/eos/results/step-0/output.yaml +++ /dev/null @@ -1 +0,0 @@ -status: complete diff --git a/examples/eos/results/step-0/sim_input.yaml b/examples/eos/results/step-0/sim_input.yaml deleted file mode 100644 index 4cda9d0..0000000 --- a/examples/eos/results/step-0/sim_input.yaml +++ /dev/null @@ -1,18 +0,0 @@ -args: - ids: - - x0.95 - - x0.97 - - x1.00 - - x1.02 - - x1.05 - images: - image_file: input_images.xyz - subscript_input: - args: - properties: - - energy - config_id: inline - script: singlepoint - workdir: . -config_id: inline -script: image_array diff --git a/examples/eos/results/step-0/stdout.txt b/examples/eos/results/step-0/stdout.txt deleted file mode 100644 index fe057ce..0000000 --- a/examples/eos/results/step-0/stdout.txt +++ /dev/null @@ -1 +0,0 @@ -{'image_file': 'image_input.xyz'} diff --git a/examples/eos/results/step-1/calc_input.yaml b/examples/eos/results/step-1/calc_input.yaml deleted file mode 100644 index f2367f8..0000000 --- a/examples/eos/results/step-1/calc_input.yaml +++ /dev/null @@ -1,50 +0,0 @@ -batch: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: true - use_slurm: true - overwrite: true - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools - submit: true -inline: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: true - use_slurm: false - overwrite: true - submit: true -srun: - calc: - args: - epsilon: 0.00802236 - sigma: 3.54 - module: ase.calculators.lj - name: LennardJones - job: - interactive: false - use_slurm: true - overwrite: true - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools - submit: true diff --git a/examples/eos/results/step-1/eos.png b/examples/eos/results/step-1/eos.png deleted file mode 100644 index 57c91d6..0000000 Binary files a/examples/eos/results/step-1/eos.png and /dev/null differ diff --git a/examples/eos/results/step-1/output.yaml b/examples/eos/results/step-1/output.yaml deleted file mode 100644 index 4ec8f3d..0000000 --- a/examples/eos/results/step-1/output.yaml +++ /dev/null @@ -1,5 +0,0 @@ -bulk_modulus: 2.1450870527455046 -equilibrium_energy: -0.06369769932222447 -equilibrium_scale: 1.0042239008583418 -equilibrium_volume: 40.98467183640903 -status: complete diff --git a/examples/eos/results/step-1/sim_input.yaml b/examples/eos/results/step-1/sim_input.yaml deleted file mode 100644 index d6c4d90..0000000 --- a/examples/eos/results/step-1/sim_input.yaml +++ /dev/null @@ -1,8 +0,0 @@ -args: - scale_range: - - 0.95 - - 1.05 - step0_dir: ../step-0 -config_id: inline -script: eos.postprocess -workdir: step-1 diff --git a/examples/image_array/image_array_batch_sim_input.yaml b/examples/image_array/image_array_batch_sim_input.yaml new file mode 100644 index 0000000..428ceea --- /dev/null +++ b/examples/image_array/image_array_batch_sim_input.yaml @@ -0,0 +1,13 @@ +asimmodule: workflows.image_array +workdir: image_array_batch_results +args: + images: + pattern: ../structures/Ar*.cif + array_max: 2 + subasimmodule_input: + asimmodule: singlepoint + env_id: batch + args: + calc_id: lj_Ar + properties: ['energy', 'forces'] + diff --git a/examples/image_array/image_array_sim_input.yaml b/examples/image_array/image_array_sim_input.yaml index 88cc63c..9921f62 100644 --- a/examples/image_array/image_array_sim_input.yaml +++ b/examples/image_array/image_array_sim_input.yaml @@ -1,17 +1,12 @@ -script: image_array -overwrite: true -workdir: results -env_id: batch +asimmodule: workflows.image_array +workdir: image_array_results args: images: - pattern: /home/mphuthi/tests/asimtools/examples/image_array/structures/Ar*.cif - ids: [2.5, 3.0, 3.5] - array_max: 3 - subscript_input: - script: singlepoint - env_id: batch - overwrite: true + pattern: ../structures/Ar*.cif + array_max: 2 + subasimmodule_input: + asimmodule: singlepoint args: - calc_id: lj + calc_id: lj_Ar properties: ['energy', 'forces'] diff --git a/examples/image_array/lj_calc_input.yaml b/examples/image_array/lj_calc_input.yaml deleted file mode 100644 index 78b1867..0000000 --- a/examples/image_array/lj_calc_input.yaml +++ /dev/null @@ -1,50 +0,0 @@ -batch: - submit: True - overwrite: True - calc: - name: LennardJones - module: ase.calculators.lj - args: - sigma: 3.54 - epsilon: 0.00802236 - job: - use_slurm: true - interactive: true - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools -srun: - submit: True - overwrite: True - calc: - name: LennardJones - module: ase.calculators.lj - args: - sigma: 3.54 - epsilon: 0.00802236 - job: - use_slurm: true - interactive: false - slurm: - flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools -inline: - submit: True - overwrite: True - job: - use_slurm: false - interactive: true - calc: - name: LennardJones - module: ase.calculators.lj - args: - sigma: 3.54 - epsilon: 0.00802236 diff --git a/examples/image_array/run.sh b/examples/image_array/run.sh new file mode 100644 index 0000000..5a707ce --- /dev/null +++ b/examples/image_array/run.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# These examples explicitly specify a choice of calc_input.yaml +# If you want to use the globally set one instead, it is not necessary to +# specify it. + +# Example 1: +# This example runs multiple asimmodules one after the other, stopping on failure +# You can fix the failed job in its workdir or set the output.yaml status to +# "discard" or "complete" and rerun asim-execute to proceed +asim-execute image_array_sim_input.yaml -c ../calc_input.yaml + diff --git a/examples/image_array/run_slurm.sh b/examples/image_array/run_slurm.sh new file mode 100644 index 0000000..90bd63f --- /dev/null +++ b/examples/image_array/run_slurm.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +# These examples explicitly specify a choice of env_input.yaml and calc_input.yaml +# If you want to use the globally set ones instead, it is not necessary to specify +# them. + +# Run the asimmodule in an interactive job +asim-execute image_array_batch_sim_input.yaml -c ../calc_input.yaml diff --git a/examples/lammps/lj_npt_template.lammps b/examples/lammps/lj_npt_template.lammps index f8f0dd8..05fa14a 100644 --- a/examples/lammps/lj_npt_template.lammps +++ b/examples/lammps/lj_npt_template.lammps @@ -4,8 +4,9 @@ atom_style atomic neighbor 1.0 bin -lattice fcc 5.26 -region box block 0 10 0 10 0 10 +# We explicitly epecify the initial structure and box in the template +lattice fcc 5.26 +region box block 0 10 0 10 0 10 create_box 1 box create_atoms 1 box @@ -16,7 +17,6 @@ pair_coeff * * 0.00802236 3.54 thermo_style custom step temp pe ke etotal press vol lx ly lz xy xz yz thermo ${THERMO_FREQ} -# dump 1 all custom ${DUMP_FREQ} traj/dump.lammpstrj id type x y z fix 1 all npt temp ${TEMP} ${TEMP} ${TAU_T} iso ${PRES} ${PRES} ${TAU_P} diff --git a/examples/lammps/lj_npt_template_with_image.lammps b/examples/lammps/lj_npt_template_with_image.lammps index d38edb9..37fac82 100644 --- a/examples/lammps/lj_npt_template_with_image.lammps +++ b/examples/lammps/lj_npt_template_with_image.lammps @@ -3,6 +3,8 @@ atom_style atomic neighbor 1.0 bin boundary p p p + +# We read in the initial structure from a file read_data image_input.lmpdat mass 1 39.948 diff --git a/examples/lammps/npt_sim_input.yaml b/examples/lammps/npt_sim_input.yaml index 5790077..bb6b4d3 100644 --- a/examples/lammps/npt_sim_input.yaml +++ b/examples/lammps/npt_sim_input.yaml @@ -1,18 +1,14 @@ -script: lammps +asimmodule: lammps.lammps env_id: inline -workdir: 'results' +workdir: image_results overwrite: true submit: true args: - lmp_cmd: 'lmp' - # template needs full path right now - template: /home/mphuthi/tests/asimtools/examples/lammps/lj_npt_template.lammps - # image: - # name: Ar - # builder: bulk - # crystalstructure: 'fcc' - # a: 5.3 - variables: + lmp_cmd: lmp + # path relative to workdir, full path usually works best, you can put them + # in a directory and version control! + template: ../lj_npt_template.lammps + variables: NSTEPS : 100 THERMO_FREQ: 10 DUMP_FREQ: 10 diff --git a/examples/lammps/npt_sim_input_with_image.yaml b/examples/lammps/npt_sim_input_with_image.yaml index fc1a557..5d34893 100644 --- a/examples/lammps/npt_sim_input_with_image.yaml +++ b/examples/lammps/npt_sim_input_with_image.yaml @@ -1,11 +1,9 @@ -script: lammps -env_id: inline -workdir: 'results' -overwrite: true -submit: true +asimmodule: lammps.lammps +workdir: npt_results args: - lmp_cmd: 'lmp' - # template needs full path right now + lmp_cmd: lmp + # path relative to workdir, full path usually works best, you can put them + # in a directory and version control! template: /home/mphuthi/dev/asimtools/examples/lammps/lj_npt_template_with_image.lammps image: name: Ar diff --git a/examples/optimize_geometry/optimize_sim_input.yaml b/examples/optimize_geometry/optimize_sim_input.yaml new file mode 100644 index 0000000..c32268f --- /dev/null +++ b/examples/optimize_geometry/optimize_sim_input.yaml @@ -0,0 +1,18 @@ +asimmodule: geometry_optimization.optimize +workdir: optimize_results +args: + calc_id: lj_Ar + image: + name: Ar + crystalstructure: fcc + a: 4.8 + cubic: true + rattle_stdev: 0.02 + optimizer: BFGS + optimizer_args: {} + fmax: 0.001 + expcellfilter_args: + mask: null + hydrostatic_strain: false + constant_volume: false + scalar_pressure: 0.0 diff --git a/examples/optimize_geometry/run.sh b/examples/optimize_geometry/run.sh new file mode 100644 index 0000000..529621f --- /dev/null +++ b/examples/optimize_geometry/run.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +# These examples explicitly specify a choice of env_input.yaml and calc_input.yaml +# If you want to use the globally set ones instead, it is not necessary to specify +# them. + +asim-execute optimize_sim_input.yaml -c ../calc_input.yaml diff --git a/examples/phonons/phonons_sim_input.yaml b/examples/phonons/phonons_sim_input.yaml new file mode 100644 index 0000000..e53e9c4 --- /dev/null +++ b/examples/phonons/phonons_sim_input.yaml @@ -0,0 +1,11 @@ +asimmodule: phonons.ase_phonons +workdir: phonons_results +args: + calc_id: emt + image: + name: Cu + cubic: true + path: GXWXKGL + delta: 0.02 + kpts: [20, 20, 20] + supercell: [10,10,10] diff --git a/examples/run_all.sh b/examples/run_all.sh new file mode 100644 index 0000000..0fa78e1 --- /dev/null +++ b/examples/run_all.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +# This runs all the examples and might take a while, if any fail, feel free to +# check issues on github or submit a new one + +source clean_all.sh + +RUNFILE="run.sh" + +for d in */; do + cd $d + if test -f ${RUNFILE}; then + source run.sh + fi + cd ../ +done diff --git a/examples/run_all_slurm.sh b/examples/run_all_slurm.sh new file mode 100644 index 0000000..7ed0759 --- /dev/null +++ b/examples/run_all_slurm.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +# This runs all the examples that use slurm and will launch a number of very +# short jobs, if any fail, feel free to check issues on github or +# submit a new one + +source clean_all.sh + +RUNFILE="run.sh" + +for d in */; do + cd $d + if test -f ${RUNFILE}; then + source run_slurm.sh + fi + cd ../ +done diff --git a/examples/sim_array/calc_input.yaml b/examples/sim_array/calc_input.yaml deleted file mode 100644 index 71c59de..0000000 --- a/examples/sim_array/calc_input.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# Cu empirical potentials - -emt: - name: EMT - module: ase.calculators.emt - args: - fixed_cutoff: True -lj: - name: LennardJones - module: ase.calculators.lj - args: - sigma: 2.338 - epsilon: 0.409 - diff --git a/examples/sim_array/run.sh b/examples/sim_array/run.sh new file mode 100644 index 0000000..5080435 --- /dev/null +++ b/examples/sim_array/run.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# These examples explicitly specify a choice of calc_input.yaml +# If you want to use the globally set ones instead, it is not necessary to +# specify them. + +# Example 1: +# This example runs the same calculator on FCC Cu with different +# lattice parameters (a). + +asim-execute sim_array_lattice_parameters_sim_input.yaml -c ../calc_input.yaml + +# Example 2: +# This example runs the same calculator on Cu with different +# crystal structures +asim-execute sim_array_crystalstructure_sim_input.yaml -c ../calc_input.yaml + +# Example 3: +# This example runs the same Cu FCC structure with different calculators. +# Notice that you can individually specify the env_id for each calculator, +# this allows for example to have one be DFT and another be a ML potential +# which can have very different hardware and environment requirements. +# For an alternative way to do this that allows iterating over the internal +# calc_input parameters, see the workflows.calc_array asimmodule +asim-execute sim_array_calc_id_sim_input.yaml -c ../calc_input.yaml -e ../env_input.yaml + +# Example 4: +# This example runs the same calculator on different input structure files. +# It also autmatically names the result directories (ids) corresponding to the +# name of the file. You can do this with any input file, not just structures +asim-execute sim_array_image_file_sim_input.yaml -c ../calc_input.yaml -e ../env_input.yaml diff --git a/examples/sim_array/run_slurm.sh b/examples/sim_array/run_slurm.sh new file mode 100644 index 0000000..c1e948c --- /dev/null +++ b/examples/sim_array/run_slurm.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# These examples explicitly specify a choice of calc_input.yaml +# If you want to use the globally set ones instead, it is not necessary to +# specify them. + +# Example 1: +# This example runs the same calculator on FCC Cu with different +# lattice parameters (a). + +asim-execute sim_array_batch_lattice_parameters_sim_input.yaml -c ../calc_input.yaml + +# Example 2: +# This example runs the same calculator on Cu with different +# crystal structures +asim-execute sim_array_batch_crystalstructure_sim_input.yaml -c ../calc_input.yaml + +# Example 3: +# This example runs the same Cu FCC structure with different calculators. +# Notice that you can individually specify the env_id for each calculator, +# this allows for example to have one be DFT and another be a ML potential +# which can have very different hardware and environment requirements. +# For an alternative way to do this that allows iterating over the internal +# calc_input parameters, see the workflows.calc_array asimmodule +asim-execute sim_array_batch_calc_id_sim_input.yaml -c ../calc_input.yaml -e ../env_input.yaml + +# Example 4: +# This example runs the same calculator on different input structure files. +# It also autmatically names the result directories (ids) corresponding to the +# name of the file. You can do this with any input file, not just structures +asim-execute sim_array_batch_image_file_sim_input.yaml -c ../calc_input.yaml -e ../env_input.yaml diff --git a/examples/sim_array/sim_array_batch_calc_id_sim_input.yaml b/examples/sim_array/sim_array_batch_calc_id_sim_input.yaml new file mode 100644 index 0000000..1ea9e36 --- /dev/null +++ b/examples/sim_array/sim_array_batch_calc_id_sim_input.yaml @@ -0,0 +1,18 @@ +asimmodule: workflows.sim_array +workdir: results +args: + key_sequence: ['args', 'image', 'a'] + array_values: [3.50, 3.60, 3.70] + env_ids: inline + template_sim_input: + asimmodule: singlepoint + args: + calc_id: lj_Cu + image: + builder: bulk + name: Cu + crystalstructure: 'fcc' + a: {} # You can set a placeholder here or not specify. + # I usually put something that would cause an error to make sure + # it is substituted correctly + \ No newline at end of file diff --git a/examples/sim_array/sim_array_batch_crystalstructure_sim_input.yaml b/examples/sim_array/sim_array_batch_crystalstructure_sim_input.yaml new file mode 100644 index 0000000..71baba5 --- /dev/null +++ b/examples/sim_array/sim_array_batch_crystalstructure_sim_input.yaml @@ -0,0 +1,23 @@ +asimmodule: workflows.sim_array +workdir: crystalstructure_results +args: + key_sequence: ['args', 'image'] + labels: [fcc, bcc] # Check what happens in this cas if you don't specify ids + array_values: + - builder: bulk + name: Cu + crystalstructure: fcc + a: 3.59 + - builder: bulk + name: Cu + crystalstructure: bcc + a: 3.655 + env_ids: inline + template_sim_input: + asimmodule: singlepoint + args: + calc_id: lj_Cu + image: {} # You can set a placeholder here or not specify. + # I usually put something that would cause an error to make sure + # it is substituted correctly + \ No newline at end of file diff --git a/examples/sim_array/sim_array_batch_image_file_sim_input.yaml b/examples/sim_array/sim_array_batch_image_file_sim_input.yaml new file mode 100644 index 0000000..f9cecfc --- /dev/null +++ b/examples/sim_array/sim_array_batch_image_file_sim_input.yaml @@ -0,0 +1,14 @@ +asimmodule: workflows.sim_array +workdir: image_file_results +args: + key_sequence: ['args', 'image', 'image_file'] + file_pattern: ../structures/*.cif # Relative to workdir + str_btn_args: [structures/Cu_, .cif] #The substring to label result directories is between the first instances of the these two strings in the file path + labels: str_btn + template_sim_input: + asimmodule: singlepoint + args: + calc_id: lj_Cu + image: + image_file: {} + \ No newline at end of file diff --git a/examples/sim_array/sim_array_batch_lattice_parameters_sim_input.yaml b/examples/sim_array/sim_array_batch_lattice_parameters_sim_input.yaml new file mode 100644 index 0000000..1ea9e36 --- /dev/null +++ b/examples/sim_array/sim_array_batch_lattice_parameters_sim_input.yaml @@ -0,0 +1,18 @@ +asimmodule: workflows.sim_array +workdir: results +args: + key_sequence: ['args', 'image', 'a'] + array_values: [3.50, 3.60, 3.70] + env_ids: inline + template_sim_input: + asimmodule: singlepoint + args: + calc_id: lj_Cu + image: + builder: bulk + name: Cu + crystalstructure: 'fcc' + a: {} # You can set a placeholder here or not specify. + # I usually put something that would cause an error to make sure + # it is substituted correctly + \ No newline at end of file diff --git a/examples/sim_array/sim_array_calc_id_sim_input.yaml b/examples/sim_array/sim_array_calc_id_sim_input.yaml new file mode 100644 index 0000000..02357bf --- /dev/null +++ b/examples/sim_array/sim_array_calc_id_sim_input.yaml @@ -0,0 +1,15 @@ +asimmodule: workflows.sim_array +workdir: calc_id_results +args: + key_sequence: [args, calc_id] + array_values: [emt, lj_Cu] + env_ids: [inline, inline] + template_sim_input: + asimmodule: singlepoint + args: + calc_id: {} + image: + builder: bulk + name: Cu + crystalstructure: fcc + a: 3.59 diff --git a/examples/sim_array/sim_array_crystalstructure_sim_input.yaml b/examples/sim_array/sim_array_crystalstructure_sim_input.yaml new file mode 100644 index 0000000..93634a0 --- /dev/null +++ b/examples/sim_array/sim_array_crystalstructure_sim_input.yaml @@ -0,0 +1,23 @@ +asimmodule: workflows.sim_array +workdir: crystalstructure_results +args: + key_sequence: ['args', 'image'] + labels: [fcc, bcc] # Check what happens in the results dir if you don't specify ids + array_values: + - builder: bulk + name: Cu + crystalstructure: fcc + a: 3.59 + - builder: bulk + name: Cu + crystalstructure: bcc + a: 3.655 + env_ids: inline + template_sim_input: + asimmodule: singlepoint + args: + calc_id: lj_Cu + image: {} # You can set a placeholder here or not specify. + # I usually put something that would cause an error to make sure + # it is substituted correctly + \ No newline at end of file diff --git a/examples/sim_array/sim_array_image_file_sim_input.yaml b/examples/sim_array/sim_array_image_file_sim_input.yaml new file mode 100644 index 0000000..f9cecfc --- /dev/null +++ b/examples/sim_array/sim_array_image_file_sim_input.yaml @@ -0,0 +1,14 @@ +asimmodule: workflows.sim_array +workdir: image_file_results +args: + key_sequence: ['args', 'image', 'image_file'] + file_pattern: ../structures/*.cif # Relative to workdir + str_btn_args: [structures/Cu_, .cif] #The substring to label result directories is between the first instances of the these two strings in the file path + labels: str_btn + template_sim_input: + asimmodule: singlepoint + args: + calc_id: lj_Cu + image: + image_file: {} + \ No newline at end of file diff --git a/examples/sim_array/sim_array_lattice_parameters_sim_input.yaml b/examples/sim_array/sim_array_lattice_parameters_sim_input.yaml new file mode 100644 index 0000000..bc10f1e --- /dev/null +++ b/examples/sim_array/sim_array_lattice_parameters_sim_input.yaml @@ -0,0 +1,18 @@ +asimmodule: workflows.sim_array +workdir: lattice_parameter_results +args: + key_sequence: ['args', 'image', 'a'] + array_values: [3.50, 3.60, 3.70] + env_ids: inline + template_sim_input: + asimmodule: singlepoint + args: + calc_id: lj_Cu + image: + builder: bulk + name: Cu + crystalstructure: 'fcc' + a: {} # You can set a placeholder here or not specify. + # I usually put something that would cause an error to make sure + # it is substituted correctly + \ No newline at end of file diff --git a/examples/sim_array/sim_array_sim_input.yaml b/examples/sim_array/sim_array_sim_input.yaml deleted file mode 100644 index 515d143..0000000 --- a/examples/sim_array/sim_array_sim_input.yaml +++ /dev/null @@ -1,16 +0,0 @@ -script: sim_array -workdir: results -args: - key_sequence: ['args', 'image', 'a'] - array_values: [3.50, 3.60, 3.70] - env_ids: 'batch' - template_sim_input: - script: singlepoint - args: - calc_id: 'lj' - image: - builder: bulk - name: Cu - crystalstructure: 'fcc' - a: 3.50 - \ No newline at end of file diff --git a/examples/sim_array/structures/Cu_bcc.cif b/examples/sim_array/structures/Cu_bcc.cif new file mode 100644 index 0000000..90ad229 --- /dev/null +++ b/examples/sim_array/structures/Cu_bcc.cif @@ -0,0 +1,26 @@ +data_image0 +_chemical_formula_structural Cu +_chemical_formula_sum "Cu1" +_cell_length_a 3.16532 +_cell_length_b 3.16532 +_cell_length_c 3.16532 +_cell_angle_alpha 109.471 +_cell_angle_beta 109.471 +_cell_angle_gamma 109.471 + +_space_group_name_H-M_alt "P 1" +_space_group_IT_number 1 + +loop_ + _space_group_symop_operation_xyz + 'x, y, z' + +loop_ + _atom_site_type_symbol + _atom_site_label + _atom_site_symmetry_multiplicity + _atom_site_fract_x + _atom_site_fract_y + _atom_site_fract_z + _atom_site_occupancy + Cu Cu1 1.0 0.00000 0.00000 0.00000 1.0000 diff --git a/examples/sim_array/structures/Cu_fcc.cif b/examples/sim_array/structures/Cu_fcc.cif new file mode 100644 index 0000000..bbf0ba2 --- /dev/null +++ b/examples/sim_array/structures/Cu_fcc.cif @@ -0,0 +1,26 @@ +data_image0 +_chemical_formula_structural Cu +_chemical_formula_sum "Cu1" +_cell_length_a 2.55266 +_cell_length_b 2.55266 +_cell_length_c 2.55266 +_cell_angle_alpha 60 +_cell_angle_beta 60 +_cell_angle_gamma 60 + +_space_group_name_H-M_alt "P 1" +_space_group_IT_number 1 + +loop_ + _space_group_symop_operation_xyz + 'x, y, z' + +loop_ + _atom_site_type_symbol + _atom_site_label + _atom_site_symmetry_multiplicity + _atom_site_fract_x + _atom_site_fract_y + _atom_site_fract_z + _atom_site_occupancy + Cu Cu1 1.0 0.00000 0.00000 0.00000 1.0000 diff --git a/examples/singlepoint/calc_input.yaml b/examples/singlepoint/calc_input.yaml index 2155520..f065d96 100644 --- a/examples/singlepoint/calc_input.yaml +++ b/examples/singlepoint/calc_input.yaml @@ -1,31 +1,10 @@ -lj: +lj_Ar: name: LennardJones module: ase.calculators.lj args: sigma: 3.54 epsilon: 0.00802236 -qe: - name: Espresso - module: ase.calculators.espresso - args: - kspacing: 0.08 - pseudopotentials: - Li: Li.pbe-s-kjpaw_psl.1.0.0.UPF - input_data: - ecutwfc: 60 - calculation: scf - tstress: True - tprnfor: True - occupations: smearing - smearing: mp - degauss: 0.03 - outdir: ./out - command: srun --mpi=pmix pw.x -in PREFIX.pwi > PREFIX.pwo -gpaw: - name: GPAW - module: gpaw.calculator - args: - kpts: [2,2,2] - h: 0.1 - xc: PBE - txt: gpaw_output.txt +emt: + name: EMT + module: ase.calculators.emt + args: {} # EMT calculator does not need any arguments diff --git a/examples/singlepoint/env_input.yaml b/examples/singlepoint/env_input.yaml index f23467d..5ff6cf5 100644 --- a/examples/singlepoint/env_input.yaml +++ b/examples/singlepoint/env_input.yaml @@ -4,23 +4,25 @@ batch: interactive: false slurm: flags: - - -n 2 - - -p idle - - --gres:gpu=0 + - -n 1 + - -N 1 + - -J my_batch_job + # You can do things like activate environments, set environment variables + # etc. before running the asimmodule using precommands using bash commands precommands: - - source ~/.bashrc - - conda activate asimtools + - export MY_ASIMTOOLS_ENV_VAR="Hello" + - echo $MY_ASIMTOOLS_ENV_VAR + # You can do things like delete files that take up space etc. after the + # asimmodule is run using postcommands using bash commands + postcommands: + - echo "Bye" srun: mode: use_slurm: true interactive: true slurm: flags: - - -n 2 - - -p idle - precommands: - - source ~/.bashrc - - conda activate asimtools + - -n 1 inline: mode: use_slurm: false diff --git a/examples/singlepoint/gpaw/gpaw_calc_input.yaml b/examples/singlepoint/gpaw_calc_input.yaml similarity index 77% rename from examples/singlepoint/gpaw/gpaw_calc_input.yaml rename to examples/singlepoint/gpaw_calc_input.yaml index 15468d0..c3a7df6 100644 --- a/examples/singlepoint/gpaw/gpaw_calc_input.yaml +++ b/examples/singlepoint/gpaw_calc_input.yaml @@ -1,4 +1,4 @@ -lj: +lj_Ar: name: LennardJones module: ase.calculators.lj args: @@ -15,5 +15,4 @@ gpaw: run_prefix: srun --mpi=pmix precommands: - source ~/.bashrc - - conda activate py-gpaw - - alias python="gpaw python" + - conda activate py-gpaw # You probably want to do something like this! diff --git a/examples/singlepoint/gpaw_sim_input.yaml b/examples/singlepoint/gpaw_sim_input.yaml new file mode 100644 index 0000000..5526cc9 --- /dev/null +++ b/examples/singlepoint/gpaw_sim_input.yaml @@ -0,0 +1,12 @@ +asimmodule: singlepoint +env_id: batch +workdir: gpaw_results +overwrite: true +args: + calc_id: gpaw + image: + name: Ar + builder: bulk + crystalstructure: fcc + a: 3.0 + properties: [energy, forces] diff --git a/examples/singlepoint/run.sh b/examples/singlepoint/run.sh new file mode 100644 index 0000000..e9cceba --- /dev/null +++ b/examples/singlepoint/run.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +# These examples explicitly specify a choice of env_input.yaml and calc_input.yaml +# If you want to use the globally set ones instead, it is not necessary to specify +# them. + +asim-execute singlepoint_sim_input.yaml -c calc_input.yaml -e env_input.yaml diff --git a/examples/singlepoint/run_slurm.sh b/examples/singlepoint/run_slurm.sh new file mode 100644 index 0000000..cbde62f --- /dev/null +++ b/examples/singlepoint/run_slurm.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +# These examples explicitly specify a choice of env_input.yaml and calc_input.yaml +# If you want to use the globally set ones instead, it is not necessary to specify +# them. + +# Run the asimmodule in an interactive job +asim-execute singlepoint_srun_sim_input.yaml -c calc_input.yaml -e env_input.yaml + +# Run the asimmodule in a batch job +asim-execute singlepoint_batch_sim_input.yaml -c calc_input.yaml -e env_input.yaml diff --git a/examples/do_nothing/sim_input.yaml b/examples/singlepoint/singlepoint_batch_sim_input.yaml similarity index 66% rename from examples/do_nothing/sim_input.yaml rename to examples/singlepoint/singlepoint_batch_sim_input.yaml index 9ea399d..dc34214 100644 --- a/examples/do_nothing/sim_input.yaml +++ b/examples/singlepoint/singlepoint_batch_sim_input.yaml @@ -1,9 +1,8 @@ -script: singlepoint +asimmodule: singlepoint +workdir: batch_results env_id: batch -workdir: 'results' -overwrite: true args: - calc_id: lj + calc_id: lj_Ar image: name: Ar builder: bulk diff --git a/examples/singlepoint/singlepoint_sim_input.yaml b/examples/singlepoint/singlepoint_sim_input.yaml index 9ea399d..df4329e 100644 --- a/examples/singlepoint/singlepoint_sim_input.yaml +++ b/examples/singlepoint/singlepoint_sim_input.yaml @@ -1,9 +1,7 @@ -script: singlepoint -env_id: batch -workdir: 'results' -overwrite: true +asimmodule: singlepoint +workdir: inline_results args: - calc_id: lj + calc_id: lj_Ar image: name: Ar builder: bulk diff --git a/examples/singlepoint/gpaw/gpaw_sim_input.yaml b/examples/singlepoint/singlepoint_srun_sim_input.yaml similarity index 58% rename from examples/singlepoint/gpaw/gpaw_sim_input.yaml rename to examples/singlepoint/singlepoint_srun_sim_input.yaml index cc25574..f9973f1 100644 --- a/examples/singlepoint/gpaw/gpaw_sim_input.yaml +++ b/examples/singlepoint/singlepoint_srun_sim_input.yaml @@ -1,9 +1,8 @@ -script: singlepoint -env_id: batch -workdir: 'results' -overwrite: true +asimmodule: singlepoint +workdir: srun_results +env_id: srun args: - calc_id: gpaw + calc_id: lj_Ar image: name: Ar builder: bulk diff --git a/examples/strong_scaling/sim_input.yaml b/examples/strong_scaling/sim_input.yaml index c987d8f..0b9a271 100644 --- a/examples/strong_scaling/sim_input.yaml +++ b/examples/strong_scaling/sim_input.yaml @@ -1,10 +1,10 @@ -script: strong_scaling.strong_scaling.py +asimmodule: strong_scaling.strong_scaling.py overwrite: true workdir: results args: ntasks: [1,2,4,8,16] - subscript_input: - script: singlepoint + subasimmodule_input: + asimmodule: singlepoint overwrite: true args: image: @@ -14,7 +14,7 @@ args: rattle_stdev: 0.01 calc_id: qe_li properties: ['energy', 'forces'] - subscript_env_input: + subasimmodule_env_input: mode: use_slurm: true interactive: false diff --git a/examples/surface_energies/surface_energies_sim_input.yaml b/examples/surface_energies/surface_energies_sim_input.yaml new file mode 100644 index 0000000..55645a6 --- /dev/null +++ b/examples/surface_energies/surface_energies_sim_input.yaml @@ -0,0 +1,15 @@ +asimmodule: surface_energies.surface_energies +workdir: surface_energies_results +args: + calc_id: emt + image: + name: Cu + cubic: true # This is really important! You must use the conventional cell! + millers: ['100', '110', '111'] + generate_all_slabs_args: + max_index: 2 + min_slab_size: 15 + min_vacuum_size: 10 + atom_relax_args: + optimizer: BFGS + fmax: 0.01 diff --git a/examples/symmetric_cell_relax/run.sh b/examples/symmetric_cell_relax/run.sh new file mode 100644 index 0000000..3a74832 --- /dev/null +++ b/examples/symmetric_cell_relax/run.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +# These examples explicitly specify a choice of calc_input.yaml +# If you want to use the globally set one instead, it is not necessary to +# specify it. + +# Example 1: This example relaxes the cell to a hydrostatic external pressure +# of 0.05eV/Ang^3 +asim-execute symmetric_cell_relax_sim_input.yaml -c ../calc_input.yaml + diff --git a/examples/symmetric_cell_relax/symmetric_cell_relax_sim_input.yaml b/examples/symmetric_cell_relax/symmetric_cell_relax_sim_input.yaml index 849f27c..6fe30e6 100644 --- a/examples/symmetric_cell_relax/symmetric_cell_relax_sim_input.yaml +++ b/examples/symmetric_cell_relax/symmetric_cell_relax_sim_input.yaml @@ -1,10 +1,12 @@ -script: symmetric_cell_relax -workdir: yh -env_id: n16 +asimmodule: geometry_optimization.symmetric_cell_relax +workdir: symmetric_cell_relax_results args: - calc_id: yh + calc_id: lj_Ar image: - image_file: input_image.xyz + name: Ar + crystalstructure: sc + a: 3 + cubic: true optimizer: BFGS fixsymmetry_args: symprec: 0.01 @@ -15,4 +17,3 @@ args: hydrostatic_strain: false constant_volume: false scalar_pressure: 0.05 - diff --git a/examples/vacancy_formation_energy/vfe_sim_input.yaml b/examples/vacancy_formation_energy/vfe_sim_input.yaml new file mode 100644 index 0000000..efa55fb --- /dev/null +++ b/examples/vacancy_formation_energy/vfe_sim_input.yaml @@ -0,0 +1,12 @@ +asimmodule: vacancy_formation_energy.vacancy_formation_energy +workdir: vfe_results +args: + calc_id: emt + image: # Does not need to be relaxed, will use energy minimum volume from eos + name: Cu + a: 3.59 + cubic: true + atom_relax_args: + fmax: 0.01 + repeat: [5,5,5] +vacancy_index: 1 diff --git a/paper.md b/paper.md index 4b60d9f..f4de2d2 100644 --- a/paper.md +++ b/paper.md @@ -35,31 +35,31 @@ Atomic simulations are a key component of modern day materials science in both a # Statement of need -Atomic SIMulation Tools (`ASIMTools`) is a lightweight workflow and simulation manager for reproducible atomistic simulations that can be transferred across environments, calculators and structures implemented in Python. By using in-built or user-defined scripts and utilities, users can run/build their own simulation recipes and automatically scale them on slurm based clusters or locally on their console. The core idea is to separate the dependence of the atomistic potential/calculator, the simulation environment and the simulation protocol thereby allowing the same simulation to be run with different calculators, structures or on different computers with just a change of parameter.Input and output yaml files follow a standard format based providing a simple interface that also acts as a record of the parameters used in a simulation without having to edit python scripts. +Atomic SIMulation Tools (`ASIMTools`) is a lightweight workflow and simulation manager for reproducible atomistic simulations that can be transferred across environments, calculators and structures implemented in Python. By using in-built or user-defined asimmodules and utilities, users can run/build their own simulation recipes and automatically scale them on slurm based clusters or locally on their console. The core idea is to separate the dependence of the atomistic potential/calculator, the simulation environment and the simulation protocol thereby allowing the same simulation to be run with different calculators, structures or on different computers with just a change of parameter.Input and output yaml files follow a standard format based providing a simple interface that also acts as a record of the parameters used in a simulation without having to edit python asimmodules. `ASIMTools` is for users interested in performing atomistic calculations on UNIX-like operating systems and/or on slurm based High Performance Computing -clusters. By defining simulation protocols as functions in "scripts", -simulation protocols can be easily added to the library of provided scripts and +clusters. By defining simulation protocols as functions in "asimmodules", +simulation protocols can be easily added to the library of provided asimmodules and iterated on. This will allow the community to develop a robust set of shareable simulation protocols. The flexibility of ASIMTools allows integration of any kind of simulation tools such as pymatgen, LAMMPS etc. with examples provided. -With the scripts defined, users only need to provide a set of inputs in the form of yaml files that define the parameters used for each simulation and are therefore a record. +With the asimmodules defined, users only need to provide a set of inputs in the form of yaml files that define the parameters used for each simulation and are therefore a record. # State of the Field There exist a number of popular workflow tools for atomistic simulations such as Aiida `[@author:2001]`, Fireworks `[@author:2001]` and many more. These tools provide frameworks for constructing complex workflows with different underlying principles. Some managers enforce strict rules that ensure that data obeys FAIR principles and emphasize data provenance and reproducibility. These methods however tend to be fairly large packages with steep learning curves. ASIMTools provides a simple interface as a starting point that can transform any code into ASIMTools compatible code by simply wrapping it in a function that returns a Python dictionary. Any such code can work in ASIMTools and with a few extra steps, the protocol can be made to support an arbitrary calculator and input structure. -In some workflow managters, such as Atomic Simulation Recipes `[@author:2001]`. Once workflows are built, it can often be difficult to quickly change and iterate over key parameters such as the choice of atomistic calculator or structure as they are intrinsically built into the code. This is particularly challening in an age where machine learning models are becoming more popular. Workflows involving machine learning interaction potentials tend to require the ability to repeat the same calculations on different examples, using different calculators on different hardware iteratively. This is where the value of ASIMTools lies in contrast to more established workflows. ASIMTools is not designed to replace the more powerful workflow managers but rather to supplement them. This is achieved by providing unified inputs that can be easily integrated into, for example, Aiida as Python functions/scripts while also being a stand-alone lightweight workflow manager. +In some workflow managters, such as Atomic Simulation Recipes `[@author:2001]`. Once workflows are built, it can often be difficult to quickly change and iterate over key parameters such as the choice of atomistic calculator or structure as they are intrinsically built into the code. This is particularly challening in an age where machine learning models are becoming more popular. Workflows involving machine learning interaction potentials tend to require the ability to repeat the same calculations on different examples, using different calculators on different hardware iteratively. This is where the value of ASIMTools lies in contrast to more established workflows. ASIMTools is not designed to replace the more powerful workflow managers but rather to supplement them. This is achieved by providing unified inputs that can be easily integrated into, for example, Aiida as Python functions/asimmodules while also being a stand-alone lightweight workflow manager. # Example We present two examples of simulation protocols, more can be found in the ASIMTools documentation. ## Calculating the energy and forces of an atomic configuration -Most atomic simulations invlolve evaluations of energies, forces, dipoles etc. of an atomic configuration. In Figure. \autoref{fig:singlepoint} we show how the `singlepoint` script, provided in -ASIMTools can be used and the input files needed to run the script with arbitrary input structure, calculator or environment. +Most atomic simulations invlolve evaluations of energies, forces, dipoles etc. of an atomic configuration. In Figure. \autoref{fig:singlepoint} we show how the `singlepoint` asimmodule, provided in +ASIMTools can be used and the input files needed to run the asimmodule with arbitrary input structure, calculator or environment. -![Schematic showing the connection between the modular input yaml files. The sim_input.yaml is the main imput file which specifies the environment, calculator (if used) and script to be run.\label{fig:singlepoint}](figures/singlepoint.pdf){ width=100% } +![Schematic showing the connection between the modular input yaml files. The sim_input.yaml is the main imput file which specifies the environment, calculator (if used) and asimmodule to be run.\label{fig:singlepoint}](figures/singlepoint.pdf){ width=100% } # Mathematics @@ -87,7 +87,7 @@ Conceptualization by Keith Phuthi. Coding and development by Keith Phuthi and Em # Acknowledgements diff --git a/setup.py b/setup.py index 6bb28d0..8e0ee8a 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ "pandas", "pyyaml", "pymatgen", - "ase", + "ase<=3.22.1", # ASE redid how io calculators work beyond this version "colorama", "myst-parser", "sphinx", diff --git a/tests/asimmodules/test_distributed.py b/tests/asimmodules/test_distributed.py new file mode 100644 index 0000000..9f10469 --- /dev/null +++ b/tests/asimmodules/test_distributed.py @@ -0,0 +1,33 @@ +""" +Tests for running asimmodules using asim_run.py +""" +import os +from glob import glob +from pathlib import Path +import pytest +from asimtools.utils import write_yaml +from asimtools.job import create_unitjob +from asimtools.job import load_job_from_directory + +@pytest.mark.parametrize("calc_input",["lj_argon_calc_input"]) +@pytest.mark.parametrize("env_input",["inline_env_input"]) +@pytest.mark.parametrize("sim_input",["lj_distributed_sim_input"]) +def test_distributed(env_input, calc_input, sim_input, tmp_path, request): + env_input = request.getfixturevalue(env_input) + calc_input = request.getfixturevalue(calc_input) + sim_input = request.getfixturevalue(sim_input) + wdir = tmp_path / 'wdir' + unitjob = create_unitjob(sim_input, env_input, wdir, calc_input=calc_input) + unitjob.submit() + + assert load_job_from_directory(wdir).get_status()[1] == 'complete' + dirs = glob(str(wdir / 'id*')) + assert len(dirs) == len(sim_input['args']['subasimmodules']) + + for d in dirs: + assert str(d).rsplit('/', maxsplit=1)[-1].startswith('id-') + + uj = load_job_from_directory(d) + assert uj.get_status()[1] == 'complete' + + assert uj.get_sim_input()['workdir'] == './' diff --git a/tests/conftest.py b/tests/conftest.py index 02b1fa8..a0a51e0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -68,7 +68,7 @@ def lj_argon_calc_input(): def singlepoint_argon_sim_input(): ''' Sim input for singlepoint calculation ''' sim_input = { - 'script': 'singlepoint', + 'asimmodule': 'singlepoint', 'prefix': 'test_', 'image': { 'name': 'Ar', @@ -82,10 +82,10 @@ def singlepoint_argon_sim_input(): @pytest.fixture def do_nothing_sim_input(): ''' - Sim imput for a script that just completes without doing anything + Sim imput for a asimmodule that just completes without doing anything ''' sim_input = { - 'script': './data/do_nothing.py', + 'asimmodule': './data/do_nothing.py', 'prefix': 'test_', 'args': { 'duration': 5, @@ -94,12 +94,12 @@ def do_nothing_sim_input(): return sim_input @pytest.fixture -def do_nothing_distributed_sim_input(): +def lj_distributed_sim_input(): ''' - Sim imput for a script that just completes without doing anything + Sim input for a distributed job that does some lj calculations ''' - subscript_input = { - 'script': 'singlepoint', + subasimmodule_input = { + 'asimmodule': 'singlepoint', 'env_id': 'inline', 'args': { 'calc_id': 'lj', @@ -110,23 +110,13 @@ def do_nothing_distributed_sim_input(): } } sim_input = { - 'script': 'distributed', + 'asimmodule': 'workflows.distributed', 'env_id': 'inline', 'args': { - 'subscripts': { - 'id-00': subscript_input, - 'id-01': subscript_input, - 'id-02': subscript_input, - 'id-03': subscript_input, - 'id-04': subscript_input, - 'id-05': subscript_input, - 'id-06': subscript_input, - 'id-07': subscript_input, - 'id-08': subscript_input, - 'id-09': subscript_input, - 'id-10': subscript_input, - 'id-11': subscript_input, - 'id-12': subscript_input, + 'subasimmodules': { + 'id-0000': subasimmodule_input, + 'id-0001': subasimmodule_input, + 'id-0002': subasimmodule_input, } } } @@ -134,12 +124,12 @@ def do_nothing_distributed_sim_input(): return sim_input @pytest.fixture -def do_nothing_distributed_custom_name_sim_input(): +def lj_distributed_custom_name_sim_input(): ''' - Sim imput for a script that just completes without doing anything + Sim input for a distributed job that does some lj calculations ''' - subscript_input = { - 'script': 'singlepoint', + subasimmodule_input = { + 'asimmodule': 'singlepoint', 'env_id': 'inline', 'args': { 'calc_id': 'lj', @@ -150,22 +140,14 @@ def do_nothing_distributed_custom_name_sim_input(): } } sim_input = { - 'script': 'distributed', + 'asimmodule': 'workflows.distributed', 'env_id': 'inline', 'args': { - 'subscripts': { - 'first': subscript_input, - 'second': subscript_input, - 'third': subscript_input,'id-03': subscript_input, - 'id-04': subscript_input, - 'id-05': subscript_input, - 'id-06': subscript_input, - 'id-07': subscript_input, - 'id-08': subscript_input, - 'id-09': subscript_input, - 'id-10': subscript_input, - 'id-11': subscript_input, - 'id-12': subscript_input, + 'subasimmodules': { + 'first': subasimmodule_input, + 'second': subasimmodule_input, + 'third': subasimmodule_input, + 'id-03': subasimmodule_input, } } } @@ -173,12 +155,12 @@ def do_nothing_distributed_custom_name_sim_input(): return sim_input @pytest.fixture -def do_nothing_distributed_batch_sim_input(): +def lj_distributed_batch_sim_input(): ''' - Sim imput for a script that just completes without doing anything + Sim input for a distributed job that does some lj calculations ''' - subscript_input = { - 'script': 'singlepoint', + subasimmodule_input = { + 'asimmodule': 'singlepoint', 'env_id': 'inline', 'args': { 'calc_id': 'lj', @@ -189,23 +171,14 @@ def do_nothing_distributed_batch_sim_input(): } } sim_input = { - 'script': 'distributed', + 'asimmodule': 'workflows.distributed', 'env_id': 'batch', 'args': { - 'subscripts': { - 'id-00': subscript_input, - 'id-01': subscript_input, - 'id-02': subscript_input, - 'id-03': subscript_input, - 'id-04': subscript_input, - 'id-05': subscript_input, - 'id-06': subscript_input, - 'id-07': subscript_input, - 'id-08': subscript_input, - 'id-09': subscript_input, - 'id-10': subscript_input, - 'id-11': subscript_input, - 'id-12': subscript_input, + 'subasimmodules': { + 'id-0000': subasimmodule_input, + 'id-0001': subasimmodule_input, + 'id-0002': subasimmodule_input, + 'id-0003': subasimmodule_input, } } } diff --git a/tests/data/scripts/do_nothing_script.py b/tests/data/scripts/do_nothing_script.py index 4f408d1..352377e 100644 --- a/tests/data/scripts/do_nothing_script.py +++ b/tests/data/scripts/do_nothing_script.py @@ -1,6 +1,6 @@ from asimtools.job import Job -def do_nothing_script(calc_input, **kwargs): +def do_nothing_asimmodule(calc_input, **kwargs): ''' Does nothing but creates input and output files ''' job = Job(calc_input, {}) job.start() diff --git a/tests/data/structures/bad_Li.xyz b/tests/data/structures/bad_Li.xyz new file mode 100644 index 0000000..e69de29 diff --git a/tests/scripts/test_asim_check.py b/tests/scripts/test_asim_check.py new file mode 100644 index 0000000..1bee387 --- /dev/null +++ b/tests/scripts/test_asim_check.py @@ -0,0 +1,65 @@ +""" +Tests for running asimmodules using asim_run.py +""" +from pathlib import Path +from glob import glob +import pytest +from asimtools.utils import write_yaml +from asimtools.job import create_unitjob +from asimtools.job import load_job_from_directory +from asimtools.scripts.asim_check import ( + get_subjobs, + load_job_tree, +) +from asimtools.scripts.asim_check import parse_command_line + +@pytest.mark.parametrize("sim_input",["do_nothing_sim_input"]) +def test_parse_command_line(sim_input, tmp_path): + ''' Tests commandline argument parsing ''' + sim_input_file = str(tmp_path / 'sim_input.yaml') + write_yaml(sim_input_file, sim_input) + + sim_config_parse, rootdir = parse_command_line( + [sim_input_file] + ) + + assert sim_input == sim_config_parse + assert Path(rootdir) == Path(tmp_path) + +@pytest.mark.parametrize("calc_input",["lj_argon_calc_input"]) +@pytest.mark.parametrize("env_input",["inline_env_input"]) +@pytest.mark.parametrize("sim_input",["lj_distributed_sim_input"]) +def test_get_subjobs(env_input, calc_input, sim_input, tmp_path, request): + env_input = request.getfixturevalue(env_input) + calc_input = request.getfixturevalue(calc_input) + sim_input = request.getfixturevalue(sim_input) + wdir = tmp_path / 'wdir' + unitjob = create_unitjob(sim_input, env_input, wdir, calc_input=calc_input) + unitjob.submit() + + dirs = glob(str(wdir / 'id*')) + assert len(dirs) == len(sim_input['args']['subasimmodules']) + + ac_dirs = get_subjobs(wdir) + assert len(ac_dirs) == len(sim_input['args']['subasimmodules']) + for d in ac_dirs: + assert load_job_from_directory(d).get_status()[1] == 'complete' + +@pytest.mark.parametrize("calc_input",["lj_argon_calc_input"]) +@pytest.mark.parametrize("env_input",["inline_env_input"]) +@pytest.mark.parametrize("sim_input",["lj_distributed_sim_input"]) +def test_load_job_tree(env_input, calc_input, sim_input, tmp_path, request): + env_input = request.getfixturevalue(env_input) + calc_input = request.getfixturevalue(calc_input) + sim_input = request.getfixturevalue(sim_input) + wdir = tmp_path / 'wdir' + unitjob = create_unitjob(sim_input, env_input, wdir, calc_input=calc_input) + unitjob.submit() + + jd = load_job_tree(wdir) + print(jd) + assert jd['workdir_name'] == 'wdir' + assert len(jd['subjobs']) == len(sim_input['args']['subasimmodules']) + for sj in jd['subjobs']: + assert jd['subjobs'][sj]['subjobs'] is None + assert jd['subjobs'][sj].get('workdir_name', False) diff --git a/tests/scripts/test_asim_execute.py b/tests/scripts/test_asim_execute.py index 3d3e6ab..7632f1e 100644 --- a/tests/scripts/test_asim_execute.py +++ b/tests/scripts/test_asim_execute.py @@ -1,3 +1,3 @@ """ -Tests for running scripts using asim_run.py +Tests for running asimmodules using asim_run.py """ diff --git a/tests/scripts/test_asim_run.py b/tests/scripts/test_asim_run.py index 3fc82cb..4fa8c8f 100644 --- a/tests/scripts/test_asim_run.py +++ b/tests/scripts/test_asim_run.py @@ -1,10 +1,11 @@ """ -Tests for running scripts using asim_run.py +Tests for running asimmodules using asim_run.py """ import os from pathlib import Path import pytest from asimtools.utils import write_yaml, read_yaml +from asimtools.job import create_unitjob from asimtools.scripts.asim_run import main as asim_run from asimtools.scripts.asim_run import parse_command_line @@ -30,7 +31,7 @@ def test_parse_command_line(sim_input, calc_input, tmp_path): ]) def test_asim_run(inputs, tmp_path, request): ''' - Test asim_run ability to launch and complete a script using LJ + Test asim_run ability to launch and complete a asimmodule using LJ ''' os.chdir(tmp_path) sim_input = request.getfixturevalue(inputs[0]) @@ -55,7 +56,7 @@ def test_asim_run(inputs, tmp_path, request): ]) def test_asim_run_commands(inputs, tmp_path, request): ''' - Test asim_run ability to launch and complete a script using LJ and running + Test asim_run ability to launch and complete a asimmodule using LJ and running commands before and after running the function ''' os.chdir(tmp_path) diff --git a/tests/scripts/test_distributed.py b/tests/scripts/test_distributed.py deleted file mode 100644 index c66b950..0000000 --- a/tests/scripts/test_distributed.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -Tests for running scripts using asim_run.py -""" -import os -import glob -from pathlib import Path -import pytest -from asimtools.utils import write_yaml -from asimtools.scripts.asim_execute import main as asim_execute - -@pytest.mark.parametrize("inputs",[ - ['do_nothing_distributed_sim_input', None], -]) -def test_distributed(inputs, tmp_path, request): - ''' - Test distributed ability to launch and complete distributed jobs - ''' - os.chdir(tmp_path) - sim_input = request.getfixturevalue(inputs[0]) - sim_input_file = 'sim_input.yaml' - write_yaml(sim_input_file, sim_input) - args = [sim_input_file] - - if inputs[1] is not None: - calc_input_file = 'calc_input.yaml' - calc_input = request.getfixturevalue(inputs[1]) - write_yaml(calc_input_file, calc_input) - args += ['-c', calc_input_file] - - asim_execute(args) - print(str(Path(tmp_path) / 'id-00')+'*') - print(glob.glob(str(Path(tmp_path))+'/*')) - - assert len(glob.glob(str(Path(tmp_path) / 'id-00')+'*')) == 1 diff --git a/tests/unit/test_job.py b/tests/unit/test_job.py index 36736fa..7a490f1 100644 --- a/tests/unit/test_job.py +++ b/tests/unit/test_job.py @@ -4,8 +4,10 @@ #pylint: disable=missing-function-docstring #pylint: disable=redefined-outer-name +from pathlib import Path import pytest from asimtools.job import UnitJob +from asimtools.utils import read_yaml def create_unitjob(sim_input, env_input, workdir, calc_input=None): """Helper for making a generic UnitJob object""" @@ -23,21 +25,32 @@ def create_unitjob(sim_input, env_input, workdir, calc_input=None): return unitjob @pytest.mark.parametrize("env_input",[ - "inline_env_input", "batch_env_input", "salloc_env_input" + "inline_env_input", + "batch_env_input", + "salloc_env_input", +]) +@pytest.mark.parametrize("sim_input",[ + "do_nothing_sim_input", "singlepoint_argon_sim_input" ]) -@pytest.mark.parametrize("sim_input",["do_nothing_sim_input"]) def test_gen_input_files(env_input, sim_input, tmp_path, request): env_input = request.getfixturevalue(env_input) sim_input = request.getfixturevalue(sim_input) wdir = tmp_path / 'wdir' unitjob = create_unitjob(sim_input, env_input, wdir) - unitjob.gen_input_files() + unitjob.gen_input_files(write_env_input=True, write_calc_input=True) assert wdir.exists() assert (wdir / 'sim_input.yaml').exists() assert (wdir / 'calc_input.yaml').exists() assert (wdir / 'env_input.yaml').exists() assert (wdir / 'output.yaml').exists() + assert Path(read_yaml(wdir / 'sim_input.yaml')['workdir']) == Path('./') + + # Test that we always write the input atomic structures + if "image" in sim_input.get('args', {}).get('image', []): + assert (wdir / 'image_input.xyz').exists() + if "images" in sim_input.get('args', {}).get('images', []): + assert (wdir / 'images_input.xyz').exists() @pytest.mark.parametrize("env_input",[ "inline_env_input", "batch_env_input", "salloc_env_input" @@ -109,10 +122,10 @@ def test_submit( else: assert new_start_time == old_start_time -def test_slurm_script(tmp_path): +def test_slurm_asimmodule(tmp_path): wdir = tmp_path / 'results' sim_input = { - 'script': 'do_nothing', + 'asimmodule': 'do_nothing', 'env_id': 'test_batch', 'workdir': wdir, 'args': {'calc_id': 'test_calc_id'}, @@ -145,19 +158,19 @@ def test_slurm_script(tmp_path): with open(wdir / 'job.sh', 'r', encoding='utf-8') as f: lines = f.readlines() - jscript_txt = ' '.join(lines) - - assert '#SBATCH -N 1' in jscript_txt - assert 'test_env_precommand1' in jscript_txt - assert 'test_env_precommand2' in jscript_txt - assert 'test_env_postcommand1' in jscript_txt - assert 'test_env_postcommand2' in jscript_txt - assert 'test_calc_precommand1' in jscript_txt - assert 'test_calc_precommand2' in jscript_txt - assert 'test_calc_postcommand1' in jscript_txt - assert 'test_calc_postcommand2' in jscript_txt - assert 'test_run_prefix' in jscript_txt - assert 'test_run_suffix' in jscript_txt + jasimmodule_txt = ' '.join(lines) + + assert '#SBATCH -N 1' in jasimmodule_txt + assert 'test_env_precommand1' in jasimmodule_txt + assert 'test_env_precommand2' in jasimmodule_txt + assert 'test_env_postcommand1' in jasimmodule_txt + assert 'test_env_postcommand2' in jasimmodule_txt + assert 'test_calc_precommand1' in jasimmodule_txt + assert 'test_calc_precommand2' in jasimmodule_txt + assert 'test_calc_postcommand1' in jasimmodule_txt + assert 'test_calc_postcommand2' in jasimmodule_txt + assert 'test_run_prefix' in jasimmodule_txt + assert 'test_run_suffix' in jasimmodule_txt for line in lines: if 'test_run_prefix' in line: diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index c556d22..1ae2c7f 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -4,12 +4,20 @@ from pathlib import Path import os import pytest +from ase.io import read from asimtools.utils import ( join_names, get_atoms, get_images, change_dict_value, + change_dict_values, parse_slice, + write_yaml, + read_yaml, + get_env_input, + get_calc_input, + find_nth, + get_nth_label, ) import ase.build @@ -20,12 +28,12 @@ )) @pytest.mark.parametrize("test_input, expected",[ - (['a', 'b'],'a_b'), - (['a', 'b', 'c'],'a_b_c'), - (['_a', 'b.c'],'a_b.c'), - (['a', '-b.--'],'a_b'), - ([' ', '-b', 'c '],'b_c'), - (['', 'b'],'b'), + (['a', 'b'],'a__b__'), + (['a', 'b', 'c'],'a__b__c__'), + (['_a', 'b.c'],'a__b.c__'), + (['a', '-b.--'],'a__b__'), + ([' ', '-b', 'c '],'b__c__'), + (['', 'b'],'b__'), ]) def test_join_names(test_input, expected): ''' Test join_names ''' @@ -59,19 +67,41 @@ def test_get_atoms(test_input, expected): @pytest.mark.parametrize("test_input, expected",[ ({'image_file': str(STRUCT_DIR / 'images.xyz')}, [ase.build.bulk('Ar'), ase.build.bulk('Cu'), ase.build.bulk('Fe')]), + ({'image_file': str(STRUCT_DIR / 'images.xyz'), 'index': ':2'}, + [ase.build.bulk('Ar'), ase.build.bulk('Cu')]), ({'image_file': str(STRUCT_DIR / 'Li1.xyz')}, [ase.build.bulk('Li').repeat((1,1,1))]), ({'pattern': str(STRUCT_DIR / 'Li*.xyz')}, - [ase.build.bulk('Li').repeat((1,1,1)), - ase.build.bulk('Li').repeat((2,2,2)), - ase.build.bulk('Li').repeat((3,3,3))]), + [read(str(STRUCT_DIR / 'Li1.xyz')), + read(str(STRUCT_DIR / 'Li2.xyz')), + read(str(STRUCT_DIR / 'Li3.xyz'))]), + ({'pattern': str(STRUCT_DIR / '*Li*.xyz'), 'skip_failed': True}, + [read(str(STRUCT_DIR / 'Li1.xyz')), + read(str(STRUCT_DIR / 'Li2.xyz')), + read(str(STRUCT_DIR / 'Li3.xyz'))]), + ({'patterns': [str(STRUCT_DIR / 'Li1.xyz'), str(STRUCT_DIR / 'images.xyz')]}, + [read(str(STRUCT_DIR / 'Li1.xyz')), + ase.build.bulk('Ar'), + ase.build.bulk('Cu'), + ase.build.bulk('Fe')]), + ({'patterns': [ + str(STRUCT_DIR / 'bad_Li.xyz'), str(STRUCT_DIR / 'images.xyz') + ], 'skip_failed': True}, + [ase.build.bulk('Ar'), ase.build.bulk('Cu'), ase.build.bulk('Fe')]), + ({'patterns': [ + str(STRUCT_DIR / 'Li1.xyz'), str(STRUCT_DIR / 'images.xyz') + ], 'index': -1}, + [read(str(STRUCT_DIR / 'Li1.xyz')), ase.build.bulk('Fe')]), ({'images': [ase.build.bulk('Ar'), ase.build.bulk('Fe')]}, [ase.build.bulk('Ar'), ase.build.bulk('Fe')]), ]) def test_get_images(test_input, expected): ''' Test getting iterable of atoms from different inputs ''' print(get_images(**test_input), expected, '*********') - assert get_images(**test_input) == expected + input_images = get_images(**test_input) + assert len(input_images) == len(expected) + for image in input_images: + assert image in expected @pytest.mark.parametrize("test_input, expected",[ (['l1', 'l2', 'l3'], {'l1': {'l2': {'l3': 'new_value'}}}), @@ -87,6 +117,23 @@ def test_change_dict_value(test_input, expected): assert new_d == expected assert new_d != d +@pytest.mark.parametrize("test_input, expected",[ + ([['l1', 'l21', 'l31'], ['l1', 'l21', 'l32']], + {'l1': {'l21': {'l31': 'v1', 'l32': 'v2'}, 'l22': 'l22v'}}), + ([['l1', 'l21', 'l31'], ['l1', 'l22']], + {'l1': {'l21': {'l31': 'v1'}, 'l22': 'v2'}}), + ([['l1', 'l21'], ['l1', 'l22']], + {'l1': {'l21': 'v1', 'l22': 'v2'}}), +]) +def test_change_dict_values(test_input, expected): + ''' Test getting iterable of atoms from different inputs ''' + d = {'l1': {'l21': {'l31': 'l31v'}, 'l22': 'l22v'}} + new_d = change_dict_values( + d, ['v1', 'v2'], test_input, return_copy=True + ) + assert new_d == expected + assert new_d != d + @pytest.mark.parametrize("test_input, expected",[ (':', slice(None, None, None)), ('1:', slice(1, None, None)), @@ -97,6 +144,53 @@ def test_change_dict_value(test_input, expected): ]) def test_parse_slice(test_input, expected): ''' Test parsing a slice ''' - assert parse_slice(test_input) == expected +def test_write_and_read_yaml(tmp_path): + d = {'test_key': 'test_value'} + yaml_file = tmp_path / 'test.yaml' + write_yaml(yaml_file, d) + assert yaml_file.exists() + read_d = read_yaml(yaml_file) + assert read_d == d + +def test_get_env_input(tmp_path): + cur_env_input = os.environ.get('ASIMTOOLS_ENV_INPUT', None) + test_env_input = tmp_path / 'test_env_input.yaml' + d = {'test_env': {'mode': {'slurm': False, 'interactive': True}}} + write_yaml(test_env_input, d) + os.environ['ASIMTOOLS_ENV_INPUT'] = str(test_env_input) + env_input = get_env_input() + os.environ['ASIMTOOLS_ENV_INPUT'] = cur_env_input + assert env_input == d + +def test_get_calc_input(tmp_path): + cur_calc_input = os.environ.get('ASIMTOOLS_CALC_INPUT', None) + test_calc_input = tmp_path / 'test_calc_input.yaml' + d = {'test_calc': { + 'name': 'EMT', 'module': 'ase.calculators.emt', 'args': {} + }} + write_yaml(test_calc_input, d) + os.environ['ASIMTOOLS_CALC_INPUT'] = str(test_calc_input) + calc_input = get_calc_input() + os.environ['ASIMTOOLS_CALC_INPUT'] = cur_calc_input + assert calc_input == d + +@pytest.mark.parametrize("test_input, expected",[ + (1, 0), + (2, 7), + (3, 13), +]) +def test_find_nth(test_input, expected): + ''' Find nth occurence of substring in string ''' + s = 'measdfamesdfamebtnrtyh' + assert find_nth(s, 'me', test_input) == expected + +@pytest.mark.parametrize("test_input, expected",[ + (1, 'label2'), + (0, 'label1'), +]) +def test_get_nth_label(test_input, expected): + ''' Find nth label in string ''' + s = '/path/to/array/id-00__label1__/id-0045__label2__' + assert get_nth_label(s, test_input) == expected