Filters#
Filters act as a wrapper around an Atoms object in order to modify
its degrees of freedom during structure optimization.
A typical use case will look like this:
------- -------- ----------
| | | | | |
| Atoms |<----| Filter |<----| Dynamics |
| | | | | |
------- -------- ----------
and in Python this would be:
>>> atoms = Atoms(...)
>>> filter = Filter(atoms, ...)
>>> dyn = Dynamics(filter, ...)
The following filters are available:
fix positions of specified atoms |
|
relax cell shape with fixing scaled positions of atoms |
|
relax both atomic positions and cell shape |
|
relax both atomic positions and cell shape |
|
relax both atomic positions and cell shape |
- class ase.filters.Filter(atoms: Atoms, indices=None, mask: np.ndarray | None = None)[source]#
Filter “hiding” specified atoms.
This filter “hides” some atoms in the ASE
Atomsobject and thus fixes their positions during structure optimization.This can be used as an alternative to the
FixAtomsconstraint.- Parameters:
Notes
You must supply either
indicesormask.If
Trajectorytries to save this object, it will instead save the underlyingAtomsobject. To prevent this, override theiterimages()method.Examples
>>> from ase.build import molecule >>> from ase.filters import Filter >>> atoms=molecule('H2O') >>> list(atoms.symbols) ['O', 'H', 'H'] >>> atoms.get_positions() array([[ 0. , 0. , 0.119262], [ 0. , 0.763239, -0.477047], [ 0. , -0.763239, -0.477047]]) >>> f1 = Filter(atoms, indices=[1, 2]) >>> f2 = Filter(atoms, mask=[0, 1, 1]) >>> f3 = Filter(atoms, mask=[a.z == 1 for a in atoms]) >>> f1.get_positions() array([[ 0. , 0.763239, -0.477047], [ 0. , -0.763239, -0.477047]])
In all three filters, only the hydrogen atoms are made visible. When asking for the positions, only the positions of the hydrogen atoms are returned.
- class ase.filters.StrainFilter(atoms: Atoms, mask: np.ndarray | None = None, include_ideal_gas: bool = False)[source]#
Filter applying a homogeneous strain to a list of atoms.
This class is used together with an ASE optimizer in order to optimize the cell shape with minimizing the stress while keeping the scaled positions fixed.
This class regards the strain of the cell as the generalized positions and the global stress tensor (times the volume) as the generalized force.
The stress and strain are presented as 6-vectors, the order of the components follow the standard engineering practice:
xx,yy,zz,yz,xz,xy.- Parameters:
mask (np.ndarray | None, default: None) – Array of six booleans, indicating which of the six independent components of the strain that are allowed to become non-zero. The default (
None) relaxes all the six components, i.e.,[True, True, True, True, True, True].include_ideal_gas (bool, default: False) – If True, the kinetic contribution of the stress is also considered.
Notes
If this filter is used together with
MDMin, the timestep (dt) to be used depends on the system size. 0.01/x where x is a typical dimension seems like a good choice.
- class ase.filters.UnitCellFilter(atoms: Atoms, mask: np.ndarray | None = None, cell_factor: float | None = None, hydrostatic_strain: bool = False, constant_volume: bool = False, orig_cell: np.ndarray | None = None, scalar_pressure: float = 0.0)[source]#
Filter returning the atomic forces and the cell stresses together.
This class is used together with an ASE optimizer in order to minimize both the atomic forces and the cell stresses simultaneously.
Degrees of freedom are the positions in the original undeformed cell, plus the deformation gradient \(\mathbf{F}\) (extra 3 “atoms”). If \(\mathbf{r}_i\) denotes a Cartesian atomic position, the corresponding atomic degree of freedom is
\[\mathbf{q}_i = \mathbf{F}^{-1}\mathbf{r}_i.\]Thus, when \(\mathbf{F}\) changes while \(\mathbf{q}_i\) is kept fixed, the atoms follow the cell deformation affinely, \(\mathbf{r}_i = \mathbf{F}\mathbf{q}_i\). This separates homogeneous cell deformation from internal atomic displacements. The cell forces are the negative derivatives of the energy with respect to \(\mathbf{F}\). For full details, see [1].
- Parameters:
mask (np.ndarray | None, default: None) –
Mask indicating which of the six independent components of the strain are relaxed.
cell_factor (float, default: float(len(atoms))) – Factor by which deformation gradient is multiplied to put it on the same scale as the positions when assembling the combined position/cell vector. The stress contribution to the forces is scaled down by the same factor. This can be thought of as a very simple preconditioners. Default is number of atoms which gives approximately the correct scaling.
hydrostatic_strain (bool, default: False) –
Constrain the cell by only allowing hydrostatic deformation. The virial tensor is replaced by
np.diag([np.trace(virial)] * 3).Added in version 3.17.0.
constant_volume (bool, default: False) –
Project out the diagonal elements of the virial tensor to allow relaxations at constant volume, e.g. for mapping out an energy-volume curve. Note: this only approximately conserves the volume and breaks energy/force consistency so can only be used with optimizers that do require do a line minimisation (e.g.
FIRE).Added in version 3.17.0.
scalar_pressure (float, default: 0.0) –
Applied pressure in eV/Å3 to use for enthalpy pV term. (Note: 1 eV/Å3 ≈ 160.2 GPa). For example, set
1.0 * ase.units.GPato provide 1 GPa. As above, this breaks energy/force consistency.Added in version 3.17.0.
Notes
FrechetCellFilterwill probably perform better.Examples
You can still use constraints on the atoms, e.g.
FixAtoms, to control the relaxation of the atoms.>>> from ase.build import bulk >>> from ase.constraints import FixAtoms >>> atoms = bulk('BN', 'wurtzite', a=2.55, c=4.23) >>> # this should be equivalent to the StrainFilter >>> atoms.set_constraint(FixAtoms(mask=[True for atom in atoms])) >>> ucf = UnitCellFilter(atoms)
You should not attach
UnitCellFilterto a trajectory. Instead, create a trajectory for the atoms, and attach it to an optimizer like this:>>> from ase.io import Trajectory >>> from ase.optimize import BFGS >>> atoms = bulk('BN', 'wurtzite', a=2.55, c=4.23) >>> # atoms.calc = ... >>> ucf = UnitCellFilter(atoms) >>> opt = BFGS(ucf) >>> traj = Trajectory('BN.traj', 'w', atoms) >>> opt.attach(traj) >>> # opt.run(fmax=0.05)
- class ase.filters.ExpCellFilter(atoms, mask=None, cell_factor=None, *args, **kwargs)[source]#
Filter returning the atomic forces and the cell stresses together.
Added in version 3.17.0.
Deprecated since version 3.23.0: Use
FrechetCellFilterfor better convergence w.r.t. cell variables.This class is used together with an ASE optimizer in order to minimize both the atomic forces and the cell stresses simultaneously.
Degrees of freedom are the positions in the original undeformed cell, plus the log of the deformation tensor (extra 3 “atoms”).
- Parameters:
cell_factor (float(DEPRECATED)) –
Retained for backwards compatibility, but no longer used.
Deprecated since version 3.23.0.
*args – Additional arguments passed to
UnitCellFilter.**kwargs – Additional arguments passed to
UnitCellFilter.
Notes
The implementation is based on that of Christoph Ortner in JuLIP.jl: libAtoms/JuLIP.jl
In
ExpCellFilter, we express the deformation gradient \(\mathbf{F} \in \mathbb{R}^{3 \times 3}\) as\[\mathbf{F} = \exp(\mathbf{U}),\]where \(\mathbf{U} = \log(\mathbf{F})\) is used as the cell optimization variable.
For a perturbation \(\mathbf{V} \in \mathbb{R}^{3 \times 3}\) of \(\mathbf{U}\), the corresponding first-order change of \(\mathbf{F}\) is given by the Fréchet derivative of the matrix exponential,
\[L_{\exp}(\mathbf{U}, \mathbf{V}) \equiv \left. \frac{\mathrm{d}}{\mathrm{d}t} \exp(\mathbf{U} + t\mathbf{V}) \right|_{t=0}.\]Let \(\mathbf{S}\) denote the virial tensor, defined here as \(\mathbf{S} = -\Omega \boldsymbol\sigma\), where \(\Omega\) is the current cell volume and \(\boldsymbol\sigma\) is the Cauchy stress tensor. Since the energy gradient with respect to \(\mathbf{F}\) is \(-\mathbf{S}\mathbf{F}^{-\top}\), the directional derivative of the energy with respect to \(\mathbf{U}\) is
\[\left. \frac{\mathrm{d}}{\mathrm{d}t} E(\mathbf{U} + t\mathbf{V}) \right|_{t=0} = [-\mathbf{S}\exp(-\mathbf{U}^{\top})] : L_{\exp}(\mathbf{U},\mathbf{V}).\]Here \(:\) denotes the Frobenius inner product, \(\mathbf{A}:\mathbf{B} = \operatorname{tr}(\mathbf{A}^{\!\top}\mathbf{B})\).
Using the integral representation of the Fréchet derivative and the cyclic property of the trace, this can be rewritten as
\[[-\mathbf{S}\exp(-\mathbf{U}^{\top})] : L_{\exp}(\mathbf{U},\mathbf{V}) = L_{\exp}\left( \mathbf{U}^{\top}, -\mathbf{S}\exp(-\mathbf{U}^{\top}) \right) : \mathbf{V}.\]Therefore,
\[\left. \frac{\mathrm{d}}{\mathrm{d}t} E(\mathbf{U} + t\mathbf{V}) \right|_{t=0} = L_{\exp}\left( \mathbf{U}^{\top}, -\mathbf{S}\exp(-\mathbf{U}^{\top}) \right) : \mathbf{V}.\]If \(\mathbf{U}\) is symmetric, an assumption that does not hold in general, this reduces to
\[\left. \frac{\mathrm{d}}{\mathrm{d}t} E(\mathbf{U} + t\mathbf{V}) \right|_{t=0} = L_{\exp}\left( \mathbf{U}, -\mathbf{S}\exp(-\mathbf{U}) \right) : \mathbf{V}.\]Thus, under this assumption, the generalized cell force with respect to \(\mathbf{U}\), i.e. the negative energy gradient, is
\[-\frac{\partial E}{\partial \mathbf{U}} = -L_{\exp}\left(\mathbf{U}, -\mathbf{S}\exp(-\mathbf{U})\right).\]
- class ase.filters.FrechetCellFilter(atoms, mask=None, exp_cell_factor=None, *args, **kwargs)[source]#
Filter returning the atomic forces and the cell stresses together.
This class is used together with an ASE optimizer in order to minimize both the atomic forces and the cell stresses simultaneously.
Added in version 3.23.0.
Degrees of freedom are the positions in the original undeformed cell, plus the matrix logarithm \(\mathbf{U} = \log(\mathbf{F})\) of the deformation gradient (extra 3 “atoms”). The cell forces are computed using the Fréchet derivative of the matrix exponential, giving the negative derivatives of the energy with respect to \(\mathbf{U}\).
- Parameters:
exp_cell_factor (float, default: float(len(atoms))) – Scaling factor for cell variables. The cell gradients in
get_forces()is divided byexp_cell_factor. By default, set the number of atoms. We recommend to set an extensive value for this parameter.*args – Additional arguments passed to
UnitCellFilter.**kwargs – Additional arguments passed to
UnitCellFilter.
Notes
The original
ExpCellFilterimplementation also uses the matrix logarithm of the deformation gradient as the cell variable, but its cell forces are not in general consistent with numerical energy derivatives.FrechetCellFilterinstead evaluates the required Fréchet derivatives explicitly. If you would like to keep the previous behavior, please useExpCellFilter.The derivation of gradients of energy w.r.t positions and the log of the deformation tensor is given in lan496/lan496.github.io