Code Documentation

Here you can browse RIIGID’s code.

riigid.riigid module

riigid.structure module

class riigid.structure.Structure(atoms)[source]

Bases: object

Structure containing all the atoms and defined fragments.

In RIIGID a structure is a set of atoms separated into disjunctive subsets called fragments. The fragments are treated as rigid bodies, that is, the bonds between all atoms belonging to the same fragment are frozen. As already said, all these fragments together then form the structure.

Via the Structure class, fragments can be defined, forces and torques on all fragments can be calculated and the fragments can be moved accordingly.

atoms

All atoms forming the structure, i.e, sum of all fragments.atoms (incl. rest_fragment).

Type:

ase.atoms.Atoms

fragments

All fragments that have been defined.

Type:

list of riigid.Fragment

rest_fragment

All atoms that haven’t been assigned a specific fragment together form the rest_fragment.

Type:

riigid.Fragment

calculate_energy_and_forces(calculator)[source]

Calculate forces on all atoms and total energy.

Parameters:

calculator (ase.calculators.calculator.Calculator) – The used ASE calculator object

Returns:

  • number – The total energy; [eV]

  • numpy.ndarray of shape (n_atoms, 3) – Forces acting on the atoms in Structure.atoms; [eV/Å]

define_fragment_by_adding_atoms(atoms, position, orientation, allowed_translation='', allowed_rotation='')[source]

Define fragments by adding additional atoms to Structure.atoms.

Not implemented yet!

Things to consider: How to treat cells? How to position and orient the new fragment? If user wants to first use this fct and then use define_fragment_by_indices, shall user define indices relative to original Structure.atoms?

define_fragment_by_indices(indices, allowed_translation, allowed_rotation)[source]

Define a RIIGID fragment by its indices.

Define a new fragment by telling RIIGID the indices of the atoms (indices relative to Structure.atoms) that shall form the new fragment.

Note

Each atom has to belong to exactly one fragment! All atoms that the user never manually assigned to a specific fragment form another fragment together, called the rest_fragment.

Parameters:
  • indices (list of int) – The indices of the atoms forming the new fragment

  • allowed_translation (str) – How shall the fragment be allowed to translate? If the string contains an “x”, translation in x-direction is allowed, etc. E.g., to allow only translation in x- and y-direction, set allowed_translation=”xy” To completely forbid any translation, use an empty string.

  • allowed_rotation (str) – Allows the user to set constraints on the rotation axis of a fragment. Generally, the rotation axis (for a rigid body) is given my the matrix-vector product of the fragment’s inverse inertia matrix with the torque acting on (the center of) the fragment. The rotation angle is then determined by the norm of the resulting vector. Using allowed_rotation, the user can apply the same logic as above to define, which components of the rotation axis shall be dropped. Examples: ‘’ forbids any rotation ‘z’ allows only rotation of the fragment around the (space-fixed) z-axis ‘xyz’ allows for unrestricted rotation of the fragment

Raises:

RuntimeError – If an atom couldn’t be found in the rest_fragment.

move(forces, stepsize)[source]

Move the fragments according to the forces.

Given the forces on all individual atoms and a stepsize, move the fragments.

Note

DOES enforce allowed_translations and allowed_rotations of fragments.

Parameters:
  • forces (numpy.ndarray of shape (n_atoms, 3)) – Forces acting on the atoms in Structure.atoms; [eV/Å]

  • stepsize (number) – Timestep; [Da*Å**2/eV]

Returns:

  • list of numpy.ndarray of shape (3,) – The rotation axis (normalized, if angle!=0) of each fragment;

  • list of float – The rotation angle of each fragment; [°]

  • list of numpy.ndarray of shape (3,) – The translation vector of each fragment; [Å]

move_random_step(displacement, angle, respect_restrictions, seed=1234)[source]

Randomly rotate and translate the fragments.

Useful to escape saddle points, especially when starting a new optimization.

Parameters:
  • displacement (number) – How far shall the fragments be translated; [Å]

  • angle (number) – How much shall the fragments be rotated; [°]

  • respect_restrictions (bool) – If True, fragment.allowed_translation/rotation is respected. If False, rotation and translation in arbitrary directions is allowed temporarily. (After the random step, the restrictions are respected again.)

  • seed (int, default:1234) – The random seed used to generate the translation directions and rotation axes

Returns:

  • list of numpy.ndarray of shape (3,) – The rotation axis (normalized, if angle!=0) of each fragment;

  • list of float – The rotation angle of each fragment; [°]

  • list of numpy.ndarray of shape (3,) – The translation vector of each fragment; [Å]

Note

  • The different fragments are rotated/translated around different axes/

in different directions. - The rest_fragment is not moved!

shift_and_rotate_a_fragment(fragment_index, shift, angle, axis)[source]

Shift and rotate a fragment from Structure.fragments.

Can be useful for optimizers, e.g. GPR.

Note

DOES NOT enforce allowed_translations and allowed_rotations of fragments.

Parameters:
  • fragment_index (int) – Structure.fragments[fragment_index] will be shifted and rotated

  • shift (numpy.ndarray of shape (3,) or equivalent list) – The vector to shift the fragment by; [Å]

  • angle (number) – How much shall the fragments be rotated; [°]

  • axis (list of length 3 or numpy.ndarray of shape (3,)) – The rotation axis

Returns:

The positions of the structure’s atoms after the transformation; [Å]

Return type:

numpy.ndarray of shape (n_atoms_in_structure,3)

update_atoms_attribute_from_fragments()[source]

Update Structure.atoms after movement of fragments.

When individual fragments are moved, the atoms object of the structure must also be updated accordingly.

The function also makes sure that the order of the atoms in Structure.atoms is not changed!

riigid.fragment module

class riigid.fragment.Fragment(atoms: Atoms, indices_in_structure, allowed_translation, allowed_rotation)[source]

Bases: object

A collection of atoms with frozen bonds between them.

In RIIGID a structure is a set of atoms separated into disjunctive subsets called fragments. The fragments are treated as rigid bodies, that is, the bonds between all atoms belonging to the same fragment are frozen. As already said, all these fragments together then form the structure.

The orientation of a fragment can be defined using Euler angles and its position can be defined by its center of mass.

atoms

The atoms forming the fragment.

Type:

ase.atoms.Atoms

indices_in_structure

Indices of the Fragment’s atoms, relative to the Structure, that the Fragment is a part of.

Type:

list of int

allowed_translation

How shall the fragment be allowed to translate? See docstring of __init__ for more details.

Type:

str

allowed_rotation

Allows the user to set constraints on the rotation axis of a fragment. See docstring of __init__ for more details.

Type:

str

body_fixed_axis_x/y/z

The body-fixed axis system’s xyz vectors (given in space-fixed coordinates)

Type:

numpy.ndarray of shape (3,)

euler_angles

The Euler angles of the fragment (alpha, beta, gamma); [°]

Type:

list of length 3

inertia_matrix/_inv

The (inverse) inertia matrix of the fragment; [(Da*Å**2)]; (inverse:[1/(Da*Å**2)])

Type:

numpy.ndarray of shape (3,3)

apply_boundaries(xmin, xmax, ymin, ymax)[source]

Needs to be fixed/redone from scratch

calculate_net_force_on_fragment(forces_structure)[source]

Get the net force acting on the fragment.

Parameters:

forces_structure (numpy.ndarray of shape (n_atoms_structure, 3)) – Forces acting on the atoms in Structure that the fragment belongs to; [eV/Å]

Returns:

  • numpy.ndarray of shape (3,) – Allowed net force acting on the fragment; [eV/Å] This is calculated by removing parts from the full/raw net force, such that self.allowed_translation is fulfilled.

  • numpy.ndarray of shape (3,) – Raw/Full net force acting on the fragment; [eV/Å]

calculate_torque_on_fragment(forces_structure)[source]

Get the net torque acting on the fragment (relative to its center of mass).

Parameters:

forces_structure (numpy.ndarray of shape (n_atoms_structure, 3)) – Forces acting on the atoms in Structure that the fragment belongs to; [eV/Å]

Returns:

  • numpy.ndarray of shape (3,) – Allowed torque acting on the fragment (relative to center of mass of fragment); [eV] This is calculated by removing parts from the full/raw torque, such that self.allowed_rotation is fulfilled.

  • numpy.ndarray of shape (3,) – Raw/Full torque acting on the fragment (relative to center of mass of fragment); [eV]

move_by_forces(forces_structure, stepsize)[source]

Rotate and translate the fragment.

Rotates and translates the fragment and updates the rotation properties (Euler angles, body-fixed axes, inertia matrix) automatically

Note

DOES enforce self.allowed_translations and self.allowed_rotations.

Parameters:
  • forces_structure (numpy.ndarray of shape (n_atoms_structure, 3)) – Forces acting on the atoms in Structure that the fragment belongs to; [eV/Å]

  • stepsize (number) – Timestep; [Da*Å**2/eV]

Returns:

  • numpy.ndarray of shape (3,) – The rotation axis (normalized, if angle!=0);

  • float – The rotation angle; [°]

  • numpy.ndarray of shape (3,) – The translation vector; [Å]

move_random_step(displacement, angle, respect_restrictions, seed=1234)[source]

Randomly rotate and translate the fragment.

Useful to escape saddle points, especially when starting a new optimization.

Parameters:
  • displacement (number) – How far shall the fragment be translated; [Å]

  • angle (number) – How much shall the fragment be rotated; [°]

  • respect_restrictions (bool) – If True, self.allowed_translation/rotation is enforced. If False, rotation and translation in arbitrary directions is allowed temporarily. (After the random step, the restrictions are enforced again.)

  • seed (int, default:1234) – The random seed used to generate the translation direction and rotation axis

Returns:

  • numpy.ndarray of shape (3,) – The rotation axis (normalized, if angle!=0);

  • float – The rotation angle; [°]

  • numpy.ndarray of shape (3,) – The translation vector; [Å]

rotate_by_angle_and_axis(angle, axis)[source]

Rotate fragment around its center of mass with given axis and angle.

Note

DOES NOT enforce self.allowed_translations and self.allowed_rotations.

Parameters:
  • axis (list of length 3 or numpy.ndarray of shape (3,)) – The rotation axis

  • angle (number) – The rotation angle; [°]

Returns:

  • numpy.ndarray of shape (3,) – The rotation axis (normalized, if angle!=0);

  • float – The rotation angle; [°]

rotate_by_euler_angles(alpha, beta, gamma)[source]

Rotate fragment around its center of mass with given Euler angles to rotate by.

Note

DOES NOT enforce self.allowed_translations and self.allowed_rotations.

Note

This method rotates the fragment by alpha, beta, gamma, relative to current body-fixed axes! I.e., the final euler angles of the fragment, relative to the space-fixed axes (=self.euler_angles) will usually be different than alpha, beta and gamma. (Unless, self.euler_angles was [0,0,0] before calling this method.)

Parameters:
  • alpha (float (0-360), float (0-180), float (0-360)) – The Euler angles; [°]

  • beta (float (0-360), float (0-180), float (0-360)) – The Euler angles; [°]

  • gamma (float (0-360), float (0-180), float (0-360)) – The Euler angles; [°]

Returns:

The positions of the fragment’s atoms after the transformation; [Å]

Return type:

numpy.ndarray of shape (n_atoms_in_fragment,3)

Raises:

ValueError – If the given angles are not within the boundaries specified above.

rotate_by_torque(torque_on_center, stepsize)[source]

Rotate fragment around its center of mass following the applied torque.

Rotates the fragment and updates the rotation properties (Euler angles, body-fixed axes, inertia matrix) automatically.

Note

DOES NOT enforce self.allowed_translations and self.allowed_rotations.

Parameters:
  • torque_on_fragment (numpy.ndarray of shape (3,)) – Torque acting on the fragment (relative to center of mass of fragment); [eV]

  • stepsize (number) – Timestep; [Da*Å**2/eV]

Returns:

  • numpy.ndarray of shape (3,) – The rotation axis (normalized, if angle!=0);

  • float – The rotation angle; [°]

set_to_euler_angles(alpha, beta, gamma)[source]

tbd

translate_by_force(force_on_center, stepsize)[source]

Translate fragment following the applied net force.

Note

DOES NOT enforce self.allowed_translations and self.allowed_rotations.

Parameters:
  • force_on_fragment (numpy.ndarray of shape (3,)) – The net force acting on the fragment; [eV/Å]

  • stepsize (number) – Timestep; [Da*Å**2/eV]

Returns:

The translation vector; [Å]

Return type:

numpy.ndarray of shape (3,)

translate_by_shift(shift)[source]

Translate fragment by simply shifting all atoms.

Note

DOES NOT enforce self.allowed_translations and self.allowed_rotations.

Parameters:

shift (numpy.ndarray of shape (3,) or equivalent list) – The vector to shift the fragment by; [Å]

Returns:

The translation vector; [Å]

Return type:

numpy.ndarray of shape (3,)

update_body_fixed_axes(angle, axis)[source]

Updates the body-fixed axis system.

After each rotation of the fragment, the body-fixed axis system must be updated, in order to calculate the Euler angles of the fragment.

Parameters:
  • axis (list of length 3 or numpy.ndarray of shape (3,)) – The rotation axis

  • angle (number) – The rotation angle; [°]

update_euler_angles(space_fixed_axis_x=[1, 0, 0], space_fixed_axis_y=[0, 1, 0], space_fixed_axis_z=[0, 0, 1])[source]

Updates the Euler angles of the fragment.

Via the orientation of a body-fixed axis system relative to a space-fixed axis system, the Euler angles of a fragment can be defined. After each update step, the body fixed axes change and the Euler angles must be updated.

Convention: if z-axes of the two axis systems are parallel, set space_fixed_axis_x=N (line of nodes)

(Body-fixed axes are given in space-fixed-coordinates.)

See: https://en.wikipedia.org/wiki/Euler_angles

Parameters:

space_fixed_axis_x/y/z (list of length 3 or numpy.ndarray of shape (3,)) – The space-fixed axis system, relative to which the Euler angles are defined. Usually, the default values should be used.

Returns:

The three Euler angles; [°]

Return type:

list of length 3

update_inertia_matrix()[source]

Get inertia matrix (and its inverse) of a fragment.

The inertia matrix is defined relative to the fragment’s center of mass.

Note

The inertia matrix must be updated after every rotation!

Returns:

  • numpy.ndarray of shape (3,3) – The inertia matrix of the fragment; [Da*Å**2]

  • numpy.ndarray of shape (3,3) – The inverse inertia matrix of the fragment; [1/(Da*Å**2)]

update_rotation_properties(angle, axis)[source]

Updates the body-fixed axis system, the Euler angles and the inertia matrix and its inverse.

After each rotation of the fragment, these properties must be updated!

Parameters:
  • axis (list of length 3 or numpy.ndarray of shape (3,)) – The rotation axis

  • angle (number) – The rotation angle; [°]

riigid.optimization_step module

class riigid.optimization_step.OptimizationStep(structure, forces_on_atoms, energy, updated_structure=None)[source]

Bases: object

Each instantiation of this class corresponds to a step in the optimization process.

structure

The initial structure of the optimization step

Type:

riigid.Structure

forces_on_atoms

Forces acting on structure.atoms; [eV/Å]

Type:

numpy.ndarray of shape (n_atoms_in_structure, 3)

energy

The energy of structure; [eV]

Type:

number

update_structure

The updated structure (generated by letting the forces act on structure)

Type:

riigid.Structure

forces_allowed, forces_raw

Allowed (after restrictions) and raw force on each fragment in self.structure; [eV/Å]

Type:

each a list of numpy.ndarrays of shape (3,)

torques_allowed, torques_raw

Allowed (after restrictions) and raw torque on each fragment in self.structure; [eV]

Type:

each a list of numpy.ndarrays of shape (3,)

Note

The stored forces and torques and the energy belong to “structure”, not to “updated_structure”.

add_force_and_torque_on_fragments()[source]

Calculate force and torque on fragments of self.structure.

add_updated_structure(updated_structure)[source]

Add/change the updated structure of the optimization step.

Parameters:

update_structure (riigid.Structure) – The updated structure (generated by letting the forces act on structure)

remove_updated_structure()[source]

Remove the updated structure from the optimization step.

Subpackages