The AtomsModel

Querying Atoms

You can perform complex queries involving atomic structures and their elements. For example, to find all atomic structures containing Hydrogen (H) atoms where the count of H atoms is at least 2, you can use the following query:

from asedb import AtomsModel, Element

results = (
    session.query(AtomsModel)
    .join(Element)
    .where((Element.symbol == 'H') & (Element.count >= 2))
    .all()
)

for atom_model in results:
    print(atom_model.to_atoms())

This query joins the AtomsModel with the Element model, filters for structures containing at least two Hydrogen atoms, and retrieves the resulting atomic structures.

Another example, where we query the total number of atoms:

(
    session.query(AtomsModel)
    .where(AtomsModel.natoms>2)
    .all()
)

Or if you want a particular Atoms object with a previously known ID:

my_id = 2  # Example ID
session.query(AtomsModel).filter_by(id=my_id).scalar()

Working with AtomsModel

The AtomsModel class provides methods to convert between ASE’s Atoms objects and the database models:

  • to_atoms(): Converts the database model to an ASE Atoms object.

  • from_atoms(atoms, import_calculation=True): Converts an ASE Atoms object to the database model, optionally importing calculation results.

Here’s an example of converting an ASE Atoms object to an AtomsModel and saving it to the database:

from ase import Atoms
from asedb import AtomsModel

# Create an ASE Atoms object
atoms = Atoms('H2O', positions=[[0, 0, 0], [0, 0, 1], [1, 0, 0]])

# Convert to AtomsModel and save to database
atoms_model = AtomsModel.from_atoms(atoms)
session.add(atoms_model)
session.commit()

print("Model ID:", atoms_model.id)  # The ID assigned to the model after the commit.

# Retrieve and convert back to ASE Atoms
retrieved_atoms_model = session.query(AtomsModel).first()
atoms = retrieved_atoms_model.to_atoms()
print(atoms)

Updating Atomic Structures

Atomic structures represented by the AtomsModel can be updated to reflect changes in their corresponding ASE Atoms objects. This is particularly useful when an atomic structure has been modified, such as changing atom positions, adding or removing atoms, or updating simulation parameters, and these changes need to be persisted in the database.

The set_atoms Method

The set_atoms method of the AtomsModel class provides a mechanism to update the model with a new or modified ASE Atoms object. This method overwrites the existing atomic structure information in the AtomsModel with the data from the provided Atoms object, including any changes made to the atomic configuration or associated calculation results.

Example Usage

Consider an existing AtomsModel instance that needs to be updated due to changes in the atomic structure or simulation results. The following steps demonstrate how to apply these updates:

  1. Retrieve the existing AtomsModel from the database.

  2. Modify the ASE Atoms object as required by your simulation or analysis workflow.

  3. Use the set_atoms method on the existing AtomsModel to apply the updates.

  4. Commit the changes to the database.

from ase import Atom
from asedb import AtomsModel

# Retrieve an existing AtomsModel from the database
atoms_model = session.query(AtomsModel).first()

# Get the ASE Atoms object from the model
atoms = atoms_model.to_atoms()

# Modify the Atoms object (example: add a new atom)
atoms += Atom('O', position=[1.2, 0, 0])

# Update the AtomsModel with the modified Atoms object
atoms_model.set_atoms(atoms)

# Commit the changes to the database
session.add(atoms_model)
session.commit()

This process ensures that the AtomsModel in the database accurately reflects the updated atomic structure. The set_atoms method allows for flexible and dynamic updates to atomic structures, facilitating iterative workflows and adjustments to simulation parameters or configurations.

class asedb.atoms_model.AtomsModel(**kwargs)[source]

The primary class representing the SQLAlchemy model for an ASE Atoms object.

This class facilitates the storage and retrieval of atomic structures within a relational database, utilizing the SQLAlchemy ORM. It intends to seamlessly serialize and deserialize ASE Atoms objects, including their associated calculator results when available.

The main usage will be something like

atoms = ase.Atoms(...)
model = AtomsModel.from_atoms(atoms)
session.add(model)
session.commit()

# Load the model from the database
loaded = session.query(AtomsModel).first().to_atoms()

The AtomsModel will also serialize a calculator object into a Calculation if a calculator exists.

classmethod from_atoms(atoms: Atoms, import_calculation: bool = True, project: None | str = None) AtomsModel[source]

Helper method to instantiate an AtomsModel instance from an ASE Atoms object.

Parameters:
  • atoms (ase.Atoms) – The ASE Atoms instance.

  • import_calculation (bool, optional) – Whether the calculator should be imported, if it exists. Defaults to True.

  • project (None | str, optional) – An optional project name. Defaults to None.

Returns:

The newly instantiated AtomsModel.

Return type:

AtomsModel

get_element_counts() dict[str, int][source]

Get the number of times a particular element occurs in the model.

Returns:

A dictionary with element symbols as keys and their counts as values.

Return type:

dict[str, int]

property has_calc: bool

Indicates whether the atoms object represented by the model has an associated calculator.

property pbc: ndarray

The full period boundary conditions.

set_atoms(atoms: Atoms, import_calculation: bool = True) None[source]

Read the current Atoms configurations, including the calculator, and save the state in the current AtomsModel instance.

If import_calculation is True, then the calculator object will also be serialized into a Calculation object, otherwise the calculator will be ignored.

to_atoms() Atoms[source]

Export the SQL Alchemy object as an ASE Atoms object. If a corresponding Calculation object exists, a SinglePointCalculator will be attached to the constructed Atoms object.

class asedb.atoms_model.Calculation(**kwargs)[source]

Represents the serialization of an ASE Calculator, encapsulating the results of computational chemistry calculations. This class stores various properties such as energy, free energy, magnetic moment, and the maximum force acting on atoms alongside arbitrary arrays of data like forces or stresses that are results of these calculations.

id

The primary key in the database.

Type:

int

atoms_id

A foreign key linking to the AtomsModel this calculation is associated with.

Type:

int

energy

The total energy from the calculation.

Type:

float, optional

free_energy

The free energy from the calculation, if available.

Type:

float, optional

magmom

The total magnetic moment from the calculation.

Type:

float, optional

fmax

The maximum force acting on any atom in the structure, derived from the forces array.

Type:

float, optional

arrays

A relationship to a collection of CalcArray instances that store arbitrary array results from the calculation.

Type:

list[CalcArray]

The class provides methods to construct a Calculation instance from an ASE Calculator object, manage result arrays, and retrieve calculation results for re-creation of an ASE Calculator object for further analysis.

Example usage:

from ase.calculators.emt import EMT
from ase.build import molecule
from asedb import Calculation, AtomsModel

atoms = molecule('H2O')
atoms.calc = EMT()
energy = atoms.get_potential_energy()

model = AtomsModel.from_atoms(atoms)
# The calc object now contains the calculation results and can be associated with an AtomsModel
assert energy == model.calculation.energy
add_array(name: str, array: ndarray) None[source]

Adds an array of calculation results to the Calculation object.

This method is used to store additional arrays of results, such as forces or stress tensors, that come from the calculation. If adding forces, the maximum force (fmax) is automatically updated.

Parameters:
  • name (str) – The name of the array (e.g., “forces”, “stress”).

  • array (np.ndarray) – The numpy array containing the calculation results.

drop_array(name: str) None[source]

Removes an array of calculation results from the Calculation object.

This method allows for the removal of specific arrays of results, useful for correcting or updating calculation data.

Parameters:

name (str) – The name of the array to remove (e.g., “forces”, “stress”).

Raises:

ValueError – If the specified array name does not exist within the Calculation object.

classmethod from_calc(calc: Calculator) Calculation[source]

Constructs a Calculation instance from an ASE Calculator object, extracting relevant calculation results and arrays.

Parameters:

calc (AseCalculator) – The ASE Calculator object from which to extract calculation results.

Returns:

An instance of Calculation populated with results from the ASE Calculator.

Return type:

Calculation

This method automatically extracts properties like energy, free energy, and magnetic moment as floats, and results like forces, stress, and magnetic moments as arrays, storing them for later reconstruction.

get_calc_kwargs() Mapping[str, float | ndarray][source]

Retrieves calculation results stored in the Calculation object, formatted for re-creation of an ASE Calculator object.

This method facilitates the reconstruction of an ASE Calculator object from stored calculation results.

Returns:

A dictionary of calculation properties and results,

ready to be passed to an ASE Calculator constructor.

Return type:

Mapping[str, float | np.ndarray]

class asedb.atoms_model.Element(**kwargs)[source]