From 5195eb52ab9a7e5a331ec02cf6ad7d3d73292c30 Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Wed, 30 Oct 2024 14:53:25 +0000 Subject: [PATCH] build based on a1ee913 --- dev/.documenter-siteinfo.json | 2 +- dev/api/index.html | 141 ++++++++++++++++++---------------- dev/development/index.html | 2 +- dev/differentiable/index.html | 48 +++++------- dev/documentation/index.html | 99 +++++++++++++++++------- dev/examples/index.html | 80 +++++++------------ dev/exercises/index.html | 2 +- dev/index.html | 2 +- dev/objects.inv | Bin 3742 -> 3795 bytes dev/publications/index.html | 2 +- dev/related/index.html | 2 +- dev/search_index.js | 2 +- 12 files changed, 202 insertions(+), 180 deletions(-) diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 858f3dff..0ef39c55 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.11.1","generation_timestamp":"2024-10-30T11:27:34","documenter_version":"1.7.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.11.1","generation_timestamp":"2024-10-30T14:53:08","documenter_version":"1.7.0"}} \ No newline at end of file diff --git a/dev/api/index.html b/dev/api/index.html index 67e88c07..6c696c28 100644 --- a/dev/api/index.html +++ b/dev/api/index.html @@ -1,10 +1,11 @@ -API · Molly.jl

Molly API

The API reference can be found here.

Molly also re-exports StaticArrays.jl and Unitful.jl, making the likes of SVector and 1.0u"nm" available when you call using Molly.

The visualize function is in a package extension and is only available once you have called using GLMakie. The ASECalculator code is in a package extension and is only available once you have called using PythonCall.

Molly.ASECalculatorType
ASECalculator(; <keyword arguments>)

A Python ASE calculator.

This calculator is only available when PythonCall is imported. It is the user's responsibility to have the required Python packages installed. This includes ASE and any packages providing the calculator.

Contrary to the rest of Molly, unitless quantities are assumed to have ASE units: Å for length, eV for energy, u for mass, and Å sqrt(u/eV) for time. Unitful quantities will be converted as appropriate.

Not currently compatible with TriclinicBoundary.

Arguments

  • ase_calc: the ASE calculator created with PythonCall.
  • atoms: the atoms, or atom equivalents, in the system.
  • coords: the coordinates of the atoms in the system. Typically a vector of SVectors of 2 or 3 dimensions.
  • boundary: the bounding box in which the simulation takes place.
  • elements=nothing: vector of atom elements as a string, either elements or atoms_data (which contains element data) must be provided.
  • atoms_data=nothing: other data associated with the atoms.
  • velocities=nothing: the velocities of the atoms in the system, only required if the velocities contribute to the potential energy or forces.
source
Molly.AbstractGBSAType

Generalized Born (GB) implicit solvent models augmented with the hydrophobic solvent accessible surface area (SA) term.

Custom GBSA methods should sub-type this abstract type.

source
Molly.AndersenThermostatType
AndersenThermostat(temperature, coupling_const)

The Andersen thermostat for controlling temperature.

The velocity of each atom is randomly changed each time step with probability dt / coupling_const to a velocity drawn from the Maxwell-Boltzmann distribution. See Andersen 1980.

source
Molly.AtomType
Atom(; <keyword arguments>)

An atom and its associated information.

Properties unused in the simulation or in analysis can be left with their default values. The types used should be bits types if the GPU is going to be used.

Arguments

  • index::Int: the index of the atom in the system.
  • charge::C=0.0: the charge of the atom, used for electrostatic interactions.
  • mass::M=1.0u"g/mol": the mass of the atom.
  • σ::S=0.0u"nm": the Lennard-Jones finite distance at which the inter-particle potential is zero.
  • ϵ::E=0.0u"kJ * mol^-1": the Lennard-Jones depth of the potential well.
  • solute::Bool=false: whether the atom is part of the solute.
source
Molly.AtomDataType
AtomData(atom_type, atom_name, res_number, res_name)

Data associated with an atom.

Storing this separately allows the Atom types to be bits types and hence work on the GPU.

source
Molly.AverageObservableLoggerType
AverageObservableLogger(observable::Function, T::DataType, n_steps::Integer;
-                        n_blocks::Integer=1024)

A logger that periodically records observations of a system and keeps a running empirical average.

While GeneralObservableLogger holds a full record of observations, AverageObservableLogger does not. In addition, calling values(logger::AverageObservableLogger; std::Bool=true) returns two values: the current running average, and an estimate of the standard deviation for this average based on the block averaging method described in Flyvbjerg and Petersen 1989.

Arguments

  • observable::Function: the observable whose mean is recorded, must support the method observable(s::System, neighbors; n_threads::Integer).
  • T::DataType: the type returned by observable.
  • n_steps::Integer: number of simulation steps between observations.
  • n_blocks::Integer=1024: the number of blocks used in the block averaging method, should be an even number.
source
Molly.BerendsenThermostatType
BerendsenThermostat(temperature, coupling_const)

The Berendsen thermostat for controlling temperature.

The scaling factor for the velocities each step is

\[\lambda^2 = 1 + \frac{\delta t}{\tau} \left( \frac{T_0}{T} - 1 \right)\]

This thermostat should be used with caution as it can lead to simulation artifacts.

source
Molly.BuckinghamType
Buckingham(; cutoff, use_neighbors, weight_special, force_units, energy_units)

The Buckingham interaction between two atoms.

The potential energy is defined as

\[V(r_{ij}) = A_{ij} \exp(-B_{ij} r_{ij}) - \frac{C_{ij}}{r_{ij}^6}\]

and the force on each atom by

\[\vec{F}_i = \left( A_{ij} B_{ij} \exp(-B_{ij} r_{ij}) - 6 \frac{C_{ij}}{r_{ij}^7} \right) \frac{\vec{r}_{ij}}{r_{ij}}\]

The parameters are derived from the atom parameters according to

\[\begin{aligned} +API · Molly.jl

Molly API

The API reference can be found here.

Molly re-exports StaticArrays.jl and Unitful.jl, making the likes of SVector and 1.0u"nm" available when you call using Molly.

Package extensions are used in order to reduce the number of dependencies:

Exported names

Docstrings

Molly.ASECalculatorType
ASECalculator(; <keyword arguments>)

A Python ASE calculator.

This calculator is only available when PythonCall is imported. It is the user's responsibility to have the required Python packages installed. This includes ASE and any packages providing the calculator.

Contrary to the rest of Molly, unitless quantities are assumed to have ASE units: Å for length, eV for energy, u for mass, and Å sqrt(u/eV) for time. Unitful quantities will be converted as appropriate.

Not currently compatible with TriclinicBoundary.

Arguments

  • ase_calc: the ASE calculator created with PythonCall.
  • atoms: the atoms, or atom equivalents, in the system.
  • coords: the coordinates of the atoms in the system. Typically a vector of SVectors of 2 or 3 dimensions.
  • boundary: the bounding box in which the simulation takes place.
  • elements=nothing: vector of atom elements as a string, either elements or atoms_data (which contains element data) must be provided.
  • atoms_data=nothing: other data associated with the atoms.
  • velocities=nothing: the velocities of the atoms in the system, only required if the velocities contribute to the potential energy or forces.
source
Molly.AbstractGBSAType

Generalized Born (GB) implicit solvent models augmented with the hydrophobic solvent accessible surface area (SA) term.

Custom GBSA methods should sub-type this abstract type.

source
Molly.AndersenThermostatType
AndersenThermostat(temperature, coupling_const)

The Andersen thermostat for controlling temperature.

The velocity of each atom is randomly changed each time step with probability dt / coupling_const to a velocity drawn from the Maxwell-Boltzmann distribution. See Andersen 1980.

source
Molly.AtomType
Atom(; <keyword arguments>)

An atom and its associated information.

Properties unused in the simulation or in analysis can be left with their default values. The types used should be bits types if the GPU is going to be used.

Arguments

  • index::Int: the index of the atom in the system.
  • atom_type::T: the type of the atom.
  • mass::M=1.0u"g/mol": the mass of the atom.
  • charge::C=0.0: the charge of the atom, used for electrostatic interactions.
  • σ::S=0.0u"nm": the Lennard-Jones finite distance at which the inter-particle potential is zero.
  • ϵ::E=0.0u"kJ * mol^-1": the Lennard-Jones depth of the potential well.
source
Molly.AtomDataType
AtomData(atom_type, atom_name, res_number, res_name)

Data associated with an atom.

Storing this separately allows the Atom types to be bits types and hence work on the GPU.

source
Molly.AverageObservableLoggerType
AverageObservableLogger(observable::Function, T::DataType, n_steps::Integer;
+                        n_blocks::Integer=1024)

A logger that periodically records observations of a system and keeps a running empirical average.

While GeneralObservableLogger holds a full record of observations, AverageObservableLogger does not. In addition, calling values(logger::AverageObservableLogger; std::Bool=true) returns two values: the current running average, and an estimate of the standard deviation for this average based on the block averaging method described in Flyvbjerg and Petersen 1989.

Arguments

  • observable::Function: the observable whose mean is recorded, must support the method observable(s::System, neighbors; n_threads::Integer).
  • T::DataType: the type returned by observable.
  • n_steps::Integer: number of simulation steps between observations.
  • n_blocks::Integer=1024: the number of blocks used in the block averaging method, should be an even number.
source
Molly.BerendsenThermostatType
BerendsenThermostat(temperature, coupling_const)

The Berendsen thermostat for controlling temperature.

The scaling factor for the velocities each step is

\[\lambda^2 = 1 + \frac{\delta t}{\tau} \left( \frac{T_0}{T} - 1 \right)\]

This thermostat should be used with caution as it can lead to simulation artifacts.

source
Molly.BuckinghamType
Buckingham(; cutoff, use_neighbors, shortcut, A_mixing, B_mixing,
+           C_mixing, weight_special)

The Buckingham interaction between two atoms.

The potential energy is defined as

\[V(r_{ij}) = A_{ij} \exp(-B_{ij} r_{ij}) - \frac{C_{ij}}{r_{ij}^6}\]

and the force on each atom by

\[\vec{F}_i = \left( A_{ij} B_{ij} \exp(-B_{ij} r_{ij}) - 6 \frac{C_{ij}}{r_{ij}^7} \right) \frac{\vec{r}_{ij}}{r_{ij}}\]

The parameters are derived from the atom parameters according to

\[\begin{aligned} A_{ij} &= (A_{ii} A_{jj})^{1/2} \\ B_{ij} &= \frac{2}{\frac{1}{B_{ii}} + \frac{1}{B_{jj}}} \\ C_{ij} &= (C_{ii} C_{jj})^{1/2} -\end{aligned}\]

so atoms that use this interaction should have fields A, B and C available.

source
Molly.CellListMapNeighborFinderType
CellListMapNeighborFinder(; eligible, dist_cutoff, special, n_steps, x0, unit_cell)

Find close atoms by distance using a cell list algorithm from CellListMap.jl.

x0 and unit_cell are optional initial coordinates and system unit cell that improve the first approximation of the cell list structure. Can not be used if one or more dimensions has infinite boundaries.

Example

julia> coords
+\end{aligned}\]

so atoms that use this interaction should have fields A, B and C available.

source
Molly.CellListMapNeighborFinderType
CellListMapNeighborFinder(; eligible, dist_cutoff, special, n_steps, x0, unit_cell)

Find close atoms by distance using a cell list algorithm from CellListMap.jl.

x0 and unit_cell are optional initial coordinates and system unit cell that improve the first approximation of the cell list structure. Can not be used if one or more dimensions has infinite boundaries.

Example

julia> coords
 15954-element Vector{SVector{3, Quantity{Float64, 𝐋, Unitful.FreeUnits{(nm,), 𝐋, nothing}}}}:
  [2.5193063341012127 nm, 3.907448346081021 nm, 4.694954671434135 nm]
  [2.4173958848835233 nm, 3.916034913604175 nm, 4.699661024574953 nm]
@@ -24,100 +25,104 @@
   Size of eligible matrix = (15954, 15954)
   n_steps = 10
   dist_cutoff = 1.2 nm
-
source
Molly.CosineAngleType
CosineAngle(; k, θ0)

A cosine bond angle between three atoms.

θ0 is in radians. The potential energy is defined as

\[V(\theta) = k(1 + \cos(\theta - \theta_0))\]

source
Molly.CoulombType
Coulomb(; cutoff, use_neighbors, weight_special, coulomb_const, force_units, energy_units)

The Coulomb electrostatic interaction between two atoms.

The potential energy is defined as

\[V(r_{ij}) = \frac{q_i q_j}{4 \pi \varepsilon_0 r_{ij}}\]

source
Molly.CoulombReactionFieldType
CoulombReactionField(; dist_cutoff, solvent_dielectric, use_neighbors, weight_special,
-                        coulomb_const, force_units, energy_units)

The Coulomb electrostatic interaction modified using the reaction field approximation between two atoms.

source
Molly.CoulombSoftCoreType
CoulombSoftCore(; cutoff, α, λ, p, use_neighbors, lorentz_mixing, weight_special,
-                coulomb_const, force_units, energy_units)

The Coulomb electrostatic interaction between two atoms with a soft core.

The potential energy is defined as

\[V(r_{ij}) = \frac{q_i q_j}{4 \pi \varepsilon_0 (r_{ij}^6 + \alpha \sigma_{ij}^6 \lambda^p)^{\frac{1}{6}}}\]

If $\alpha$ or $\lambda$ are zero this gives the standard Coulomb potential.

source
Molly.CubicBoundaryType
CubicBoundary(x, y, z)
-CubicBoundary(x)

Cubic 3D bounding box defined by three side lengths.

If one length is given then all three sides will have that length. Setting one or more values to Inf gives no boundary in that dimension.

source
Molly.CubicSplineCutoffType
CubicSplineCutoff(dist_activation, dist_cutoff)

Cutoff that interpolates the true potential and zero between an activation point and a cutoff point, using a cubic Hermite spline.

source
Molly.DistanceConstraintType
DistanceConstraint(i, j, dist)

Constraint between two atoms that maintains a fixed distance between the two atoms.

source
Molly.DistanceCutoffType
DistanceCutoff(dist_cutoff)

Cutoff that sets the potential and force to be zero past a specified cutoff point.

source
Molly.FENEBondType
FENEBond(; k, r0, σ, ϵ)

A finitely extensible non-linear elastic (FENE) bond between two atoms, see Kremer and Grest 1990.

The potential energy is defined as

\[V(r) = -\frac{1}{2} k r^2_0 \ln \left( 1 - \left( \frac{r}{r_0} \right) ^2 \right) + V_{\text{WCA}}(r)\]

where the WCA contribution is given by

\[V_{\text{WCA}}(r) = +

source
Molly.CosineAngleType
CosineAngle(; k, θ0)

A cosine bond angle between three atoms.

θ0 is in radians. The potential energy is defined as

\[V(\theta) = k(1 + \cos(\theta - \theta_0))\]

source
Molly.CoulombType
Coulomb(; cutoff, use_neighbors, weight_special, coulomb_const)

The Coulomb electrostatic interaction between two atoms.

The potential energy is defined as

\[V(r_{ij}) = \frac{q_i q_j}{4 \pi \varepsilon_0 r_{ij}}\]

source
Molly.CoulombReactionFieldType
CoulombReactionField(; dist_cutoff, solvent_dielectric, use_neighbors, weight_special,
+                        coulomb_const)

The Coulomb electrostatic interaction modified using the reaction field approximation between two atoms.

source
Molly.CoulombSoftCoreType
CoulombSoftCore(; cutoff, α, λ, p, use_neighbors, σ_mixing, weight_special, coulomb_const)

The Coulomb electrostatic interaction between two atoms with a soft core.

The potential energy is defined as

\[V(r_{ij}) = \frac{q_i q_j}{4 \pi \varepsilon_0 (r_{ij}^6 + \alpha \sigma_{ij}^6 \lambda^p)^{\frac{1}{6}}}\]

If $\alpha$ or $\lambda$ are zero this gives the standard Coulomb potential.

source
Molly.CubicBoundaryType
CubicBoundary(x, y, z)
+CubicBoundary(x)

Cubic 3D bounding box defined by three side lengths.

If one length is given then all three sides will have that length. Setting one or more values to Inf gives no boundary in that dimension.

source
Molly.CubicSplineCutoffType
CubicSplineCutoff(dist_activation, dist_cutoff)

Cutoff that interpolates the true potential and zero between an activation point and a cutoff point, using a cubic Hermite spline.

source
Molly.DistanceConstraintType
DistanceConstraint(i, j, dist)

Constraint between two atoms that maintains a fixed distance between the two atoms.

source
Molly.DistanceCutoffType
DistanceCutoff(dist_cutoff)

Cutoff that sets the potential and force to be zero past a specified cutoff point.

source
Molly.FENEBondType
FENEBond(; k, r0, σ, ϵ)

A finitely extensible non-linear elastic (FENE) bond between two atoms, see Kremer and Grest 1990.

The potential energy is defined as

\[V(r) = -\frac{1}{2} k r^2_0 \ln \left( 1 - \left( \frac{r}{r_0} \right) ^2 \right) + V_{\text{WCA}}(r)\]

where the WCA contribution is given by

\[V_{\text{WCA}}(r) = \begin{cases} 4\varepsilon \left[ \left( \frac{\sigma}{r} \right) ^{12} - \left( \frac{\sigma}{r} \right) ^6 \right] + \varepsilon & r < 2^{1/6}\sigma\\ 0 & r \geq 2^{1/6}\sigma\\ - \end{cases}\]

source
Molly.GeneralObservableLoggerType
GeneralObservableLogger(observable::Function, T, n_steps)

A logger which holds a record of regularly sampled observations of a system.

observable should return an object of type T and support the method observable(s::System, neighbors; n_threads::Integer)::T.

source
Molly.GravityType
Gravity(; G, use_neighbors)

The gravitational interaction between two atoms.

The potential energy is defined as

\[V(r_{ij}) = -\frac{G m_i m_j}{r_{ij}}\]

source
Molly.HamiltonianREMDType
HamiltonianREMD(; <keyword arguments>)

A simulator for a parallel Hamiltonian replica exchange MD (H-REMD) simulation on a ReplicaSystem.

The replicas are expected to have different Hamiltonians, i.e. different interactions. When calling simulate!, the assign_velocities keyword argument determines whether to assign random velocities at the appropriate temperature for each replica.

Not currently compatible with automatic differentiation using Zygote.

Arguments

  • dt::DT: the time step of the simulation.
  • temperature::T: the temperatures of the simulation.
  • simulators::ST: individual simulators for simulating each replica.
  • exchange_time::ET: the time interval between replica exchange attempts.
source
Molly.HarmonicAngleType
HarmonicAngle(; k, θ0)

A harmonic bond angle between three atoms.

θ0 is in radians. The second atom is the middle atom. The potential energy is defined as

\[V(\theta) = \frac{1}{2} k (\theta - \theta_0)^2\]

source
Molly.HarmonicBondType
HarmonicBond(; k, r0)

A harmonic bond between two atoms.

The potential energy is defined as

\[V(r) = \frac{1}{2} k (r - r_0)^2\]

source
Molly.HarmonicPositionRestraintType
HarmonicPositionRestraint(; k, x0)

A harmonic position restraint on an atom to coordinate x0.

The potential energy is defined as

\[V(\boldsymbol{x}) = \frac{1}{2} k |\boldsymbol{x} - \boldsymbol{x}_0|^2\]

source
Molly.ImplicitSolventOBCType
ImplicitSolventOBC(atoms, atoms_data, bonds)

Onufriev-Bashford-Case GBSA model implemented as an AtomsCalculators.jl calculator.

Should be used along with a Coulomb or CoulombReactionField interaction. The keyword argument use_OBC2 determines whether to use parameter set I (false, the default) or II (true).

source
Molly.GeneralObservableLoggerType
GeneralObservableLogger(observable::Function, T, n_steps)

A logger which holds a record of regularly sampled observations of a system.

observable should return an object of type T and support the method observable(s::System, neighbors; n_threads::Integer)::T.

source
Molly.GravityType
Gravity(; G, use_neighbors)

The gravitational interaction between two atoms.

The potential energy is defined as

\[V(r_{ij}) = -\frac{G m_i m_j}{r_{ij}}\]

source
Molly.HamiltonianREMDType
HamiltonianREMD(; <keyword arguments>)

A simulator for a parallel Hamiltonian replica exchange MD (H-REMD) simulation on a ReplicaSystem.

The replicas are expected to have different Hamiltonians, i.e. different interactions. When calling simulate!, the assign_velocities keyword argument determines whether to assign random velocities at the appropriate temperature for each replica.

Arguments

  • dt::DT: the time step of the simulation.
  • temperature::T: the temperatures of the simulation.
  • simulators::ST: individual simulators for simulating each replica.
  • exchange_time::ET: the time interval between replica exchange attempts.
source
Molly.HarmonicAngleType
HarmonicAngle(; k, θ0)

A harmonic bond angle between three atoms.

θ0 is in radians. The second atom is the middle atom. The potential energy is defined as

\[V(\theta) = \frac{1}{2} k (\theta - \theta_0)^2\]

source
Molly.HarmonicBondType
HarmonicBond(; k, r0)

A harmonic bond between two atoms.

The potential energy is defined as

\[V(r) = \frac{1}{2} k (r - r_0)^2\]

source
Molly.HarmonicPositionRestraintType
HarmonicPositionRestraint(; k, x0)

A harmonic position restraint on an atom to coordinate x0.

The potential energy is defined as

\[V(\boldsymbol{x}) = \frac{1}{2} k |\boldsymbol{x} - \boldsymbol{x}_0|^2\]

source
Molly.ImplicitSolventOBCType
ImplicitSolventOBC(atoms, atoms_data, bonds)

Onufriev-Bashford-Case GBSA model implemented as an AtomsCalculators.jl calculator.

Should be used along with a Coulomb or CoulombReactionField interaction. The keyword argument use_OBC2 determines whether to use parameter set I (false, the default) or II (true).

source
Molly.InteractionList1AtomsType
InteractionList1Atoms(is, inters)
 InteractionList1Atoms(is, inters, types)
-InteractionList1Atoms(inter_type)

A list of specific interactions that involve one atom such as position restraints.

source
Molly.InteractionList2AtomsType
InteractionList2Atoms(is, js, inters)
+InteractionList1Atoms(inter_type)

A list of specific interactions that involve one atom such as position restraints.

source
Molly.InteractionList2AtomsType
InteractionList2Atoms(is, js, inters)
 InteractionList2Atoms(is, js, inters, types)
-InteractionList2Atoms(inter_type)

A list of specific interactions that involve two atoms such as bond potentials.

source
Molly.InteractionList3AtomsType
InteractionList3Atoms(is, js, ks, inters)
+InteractionList2Atoms(inter_type)

A list of specific interactions that involve two atoms such as bond potentials.

source
Molly.InteractionList3AtomsType
InteractionList3Atoms(is, js, ks, inters)
 InteractionList3Atoms(is, js, ks, inters, types)
-InteractionList3Atoms(inter_type)

A list of specific interactions that involve three atoms such as bond angle potentials.

source
Molly.InteractionList4AtomsType
InteractionList4Atoms(is, js, ks, ls, inters)
+InteractionList3Atoms(inter_type)

A list of specific interactions that involve three atoms such as bond angle potentials.

source
Molly.InteractionList4AtomsType
InteractionList4Atoms(is, js, ks, ls, inters)
 InteractionList4Atoms(is, js, ks, ls, inters, types)
-InteractionList4Atoms(inter_type)

A list of specific interactions that involve four atoms such as torsion potentials.

source
Molly.LangevinType
Langevin(; <keyword arguments>)

The Langevin integrator, based on the Langevin Middle Integrator in OpenMM.

This is a leapfrog integrator, so the velocities are offset by half a time step behind the positions.

Arguments

  • dt::S: the time step of the simulation.
  • temperature::K: the equilibrium temperature of the simulation.
  • friction::F: the friction coefficient of the simulation.
  • coupling::C=NoCoupling(): the coupling which applies during the simulation.
  • remove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.
source
Molly.LangevinSplittingType
LangevinSplitting(; <keyword arguments>)

The Langevin simulator using a general splitting scheme.

This consists of a succession of A, B and O steps, corresponding respectively to updates in position, velocity for the potential part, and velocity for the thermal fluctuation-dissipation part. The Langevin and VelocityVerlet simulators without coupling correspond to the BAOA and BAB schemes respectively. For more information on the sampling properties of splitting schemes, see Fass et al. 2018.

Not currently compatible with constraints, will print a warning and continue without applying constraints. Not currently compatible with automatic differentiation using Zygote.

Arguments

  • dt::S: the time step of the simulation.
  • temperature::K: the equilibrium temperature of the simulation.
  • friction::F: the friction coefficient. If units are used, it should have a dimensionality of mass per time.
  • splitting::W: the splitting specifier. Should be a string consisting of the characters A, B and O. Strings with no Os reduce to deterministic symplectic schemes.
  • remove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.
source
Molly.LennardJonesType
LennardJones(; cutoff, use_neighbors, lorentz_mixing, weight_special, weight_solute_solvent,
-             force_units, energy_units, skip_shortcut)

The Lennard-Jones 6-12 interaction between two atoms.

The potential energy is defined as

\[V(r_{ij}) = 4\varepsilon_{ij} \left[\left(\frac{\sigma_{ij}}{r_{ij}}\right)^{12} - \left(\frac{\sigma_{ij}}{r_{ij}}\right)^{6}\right]\]

and the force on each atom by

\[\begin{aligned} +InteractionList4Atoms(inter_type)

A list of specific interactions that involve four atoms such as torsion potentials.

source
Molly.LangevinType
Langevin(; <keyword arguments>)

The Langevin integrator, based on the Langevin Middle Integrator in OpenMM.

This is a leapfrog integrator, so the velocities are offset by half a time step behind the positions.

Arguments

  • dt::S: the time step of the simulation.
  • temperature::K: the equilibrium temperature of the simulation.
  • friction::F: the friction coefficient of the simulation.
  • coupling::C=NoCoupling(): the coupling which applies during the simulation.
  • remove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.
source
Molly.LangevinSplittingType
LangevinSplitting(; <keyword arguments>)

The Langevin simulator using a general splitting scheme.

This consists of a succession of A, B and O steps, corresponding respectively to updates in position, velocity for the potential part, and velocity for the thermal fluctuation-dissipation part. The Langevin and VelocityVerlet simulators without coupling correspond to the BAOA and BAB schemes respectively. For more information on the sampling properties of splitting schemes, see Fass et al. 2018.

Not currently compatible with constraints, will print a warning and continue without applying constraints.

Arguments

  • dt::S: the time step of the simulation.
  • temperature::K: the equilibrium temperature of the simulation.
  • friction::F: the friction coefficient. If units are used, it should have a dimensionality of mass per time.
  • splitting::W: the splitting specifier. Should be a string consisting of the characters A, B and O. Strings with no Os reduce to deterministic symplectic schemes.
  • remove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.
source
Molly.LennardJonesType
LennardJones(; cutoff, use_neighbors, shortcut, σ_mixing, ϵ_mixing, weight_special)

The Lennard-Jones 6-12 interaction between two atoms.

The potential energy is defined as

\[V(r_{ij}) = 4\varepsilon_{ij} \left[\left(\frac{\sigma_{ij}}{r_{ij}}\right)^{12} - \left(\frac{\sigma_{ij}}{r_{ij}}\right)^{6}\right]\]

and the force on each atom by

\[\begin{aligned} \vec{F}_i &= 24\varepsilon_{ij} \left(2\frac{\sigma_{ij}^{12}}{r_{ij}^{13}} - \frac{\sigma_{ij}^6}{r_{ij}^{7}}\right) \frac{\vec{r}_{ij}}{r_{ij}} \\ &= \frac{24\varepsilon_{ij}}{r_{ij}^2} \left[2\left(\frac{\sigma_{ij}^{6}}{r_{ij}^{6}}\right)^2 -\left(\frac{\sigma_{ij}}{r_{ij}}\right)^{6}\right] \vec{r}_{ij} -\end{aligned}\]

source
Molly.LennardJonesSoftCoreType
LennardJonesSoftCore(; cutoff, α, λ, p, use_neighbors, lorentz_mixing, weight_special,
-                     weight_solute_solvent, force_units, energy_units, skip_shortcut)

The Lennard-Jones 6-12 interaction between two atoms with a soft core.

The potential energy is defined as

\[V(r_{ij}) = 4\varepsilon_{ij} \left[\left(\frac{\sigma_{ij}}{r_{ij}^{\text{sc}}}\right)^{12} - \left(\frac{\sigma_{ij}}{r_{ij}^{\text{sc}}}\right)^{6}\right]\]

and the force on each atom by

\[\vec{F}_i = 24\varepsilon_{ij} \left(2\frac{\sigma_{ij}^{12}}{(r_{ij}^{\text{sc}})^{13}} - \frac{\sigma_{ij}^6}{(r_{ij}^{\text{sc}})^{7}}\right) \left(\frac{r_{ij}}{r_{ij}^{\text{sc}}}\right)^5 \frac{\vec{r}_{ij}}{r_{ij}}\]

where

\[r_{ij}^{\text{sc}} = \left(r_{ij}^6 + \alpha \sigma_{ij}^6 \lambda^p \right)^{1/6}\]

If $\alpha$ or $\lambda$ are zero this gives the standard LennardJones potential.

source
Molly.MetropolisMonteCarloType
MetropolisMonteCarlo(; <keyword arguments>)

A Monte Carlo simulator that uses the Metropolis algorithm to sample the configuration space.

Arguments

  • temperature::T: the temperature of the system.
  • trial_moves::M: a function that performs the trial moves.
  • trial_args::Dict: a dictionary of arguments to be passed to the trial move function.
source
Molly.MieType
Mie(; m, n, cutoff, use_neighbors, lorentz_mixing, force_units, energy_units, skip_shortcut)

The Mie generalized interaction between two atoms.

When m equals 6 and n equals 12 this is equivalent to the Lennard-Jones interaction. The potential energy is defined as

\[V(r_{ij}) = C \varepsilon_{ij} \left[\left(\frac{\sigma_{ij}}{r_{ij}}\right)^n - \left(\frac{\sigma_{ij}}{r_{ij}}\right)^m\right]\]

where

\[C = \frac{n}{n - m} \left( \frac{n}{m} \right) ^\frac{m}{n - m}\]

source
Molly.LennardJonesSoftCoreType
LennardJonesSoftCore(; cutoff, α, λ, p, use_neighbors, shortcut, σ_mixing, ϵ_mixing,
+                     weight_special)

The Lennard-Jones 6-12 interaction between two atoms with a soft core.

The potential energy is defined as

\[V(r_{ij}) = 4\varepsilon_{ij} \left[\left(\frac{\sigma_{ij}}{r_{ij}^{\text{sc}}}\right)^{12} - \left(\frac{\sigma_{ij}}{r_{ij}^{\text{sc}}}\right)^{6}\right]\]

and the force on each atom by

\[\vec{F}_i = 24\varepsilon_{ij} \left(2\frac{\sigma_{ij}^{12}}{(r_{ij}^{\text{sc}})^{13}} - \frac{\sigma_{ij}^6}{(r_{ij}^{\text{sc}})^{7}}\right) \left(\frac{r_{ij}}{r_{ij}^{\text{sc}}}\right)^5 \frac{\vec{r}_{ij}}{r_{ij}}\]

where

\[r_{ij}^{\text{sc}} = \left(r_{ij}^6 + \alpha \sigma_{ij}^6 \lambda^p \right)^{1/6}\]

If $\alpha$ or $\lambda$ are zero this gives the standard LennardJones potential.

source
Molly.MetropolisMonteCarloType
MetropolisMonteCarlo(; <keyword arguments>)

A Monte Carlo simulator that uses the Metropolis algorithm to sample the configuration space.

Arguments

  • temperature::T: the temperature of the system.
  • trial_moves::M: a function that performs the trial moves.
  • trial_args::Dict: a dictionary of arguments to be passed to the trial move function.
source
Molly.MieType
Mie(; m, n, cutoff, use_neighbors, shortcut, σ_mixing, ϵ_mixing)

The Mie generalized interaction between two atoms.

When m equals 6 and n equals 12 this is equivalent to the Lennard-Jones interaction. The potential energy is defined as

\[V(r_{ij}) = C \varepsilon_{ij} \left[\left(\frac{\sigma_{ij}}{r_{ij}}\right)^n - \left(\frac{\sigma_{ij}}{r_{ij}}\right)^m\right]\]

where

\[C = \frac{n}{n - m} \left( \frac{n}{m} \right) ^\frac{m}{n - m}\]

source
Molly.MolecularForceFieldType
MolecularForceField(ff_files...; units=true)
 MolecularForceField(T, ff_files...; units=true)
 MolecularForceField(atom_types, residue_types, bond_types, angle_types,
                     torsion_types, torsion_order, weight_14_coulomb,
-                    weight_14_lj, attributes_from_residue)

A molecular force field.

Read one or more OpenMM force field XML files by passing them to the constructor.

source
Molly.MolecularTopologyType
MolecularTopology(bond_is, bond_js, n_atoms)
-MolecularTopology(atom_molecule_inds, molecule_atom_counts)

Topology information for a system.

Stores the index of the molecule each atom belongs to and the number of atoms in each molecule.

source
Molly.MollyCalculatorType
MollyCalculator(; <keyword arguments>)

A calculator for use with the AtomsCalculators.jl interface.

neighbors can optionally be given as a keyword argument when calling the calculation functions to save on computation when the neighbors are the same for multiple calls. In a similar way, n_threads can be given to determine the number of threads to use when running the calculation function. Note that this calculator is designed for using Molly in other contexts; if you want to use another calculator in Molly it can be given as general_inters when creating a System.

Not currently compatible with virial calculation. Not currently compatible with using atom properties such as σ and ϵ.

Arguments

  • pairwise_inters::PI=(): the pairwise interactions in the system, i.e. interactions between all or most atom pairs such as electrostatics. Typically a Tuple.
  • specific_inter_lists::SI=(): the specific interactions in the system, i.e. interactions between specific atoms such as bonds or angles. Typically a Tuple.
  • general_inters::GI=(): the general interactions in the system, i.e. interactions involving all atoms such as implicit solvent. Each should implement the AtomsCalculators.jl interface. Typically a Tuple.
  • neighbor_finder::NF=NoNeighborFinder(): the neighbor finder used to find close atoms and save on computation.
  • force_units::F=u"kJ * mol^-1 * nm^-1": the units of force of the system. Should be set to NoUnits if units are not being used.
  • energy_units::E=u"kJ * mol^-1": the units of energy of the system. Should be set to NoUnits if units are not being used.
  • k::K=Unitful.k or Unitful.k * Unitful.Na: the Boltzmann constant, which may be modified in some simulations. k is chosen based on the energy_units given.
  • dims::Integer=3: the number of dimensions in the system.
source
Molly.MonteCarloAnisotropicBarostatType
MonteCarloAnisotropicBarostat(pressure, temperature, boundary; n_steps=30,
+                    weight_14_lj, attributes_from_residue)

A molecular force field.

Read one or more OpenMM force field XML files by passing them to the constructor.

source
Molly.MolecularTopologyType
MolecularTopology(bond_is, bond_js, n_atoms)
+MolecularTopology(atom_molecule_inds, molecule_atom_counts)

Topology information for a system.

Stores the index of the molecule each atom belongs to and the number of atoms in each molecule.

source
Molly.MollyCalculatorType
MollyCalculator(; <keyword arguments>)

A calculator for use with the AtomsCalculators.jl interface.

neighbors can optionally be given as a keyword argument when calling the calculation functions to save on computation when the neighbors are the same for multiple calls. In a similar way, n_threads can be given to determine the number of threads to use when running the calculation function. Note that this calculator is designed for using Molly in other contexts; if you want to use another calculator in Molly it can be given as general_inters when creating a System.

Not currently compatible with virial calculation. Not currently compatible with using atom properties such as σ and ϵ.

Arguments

  • pairwise_inters::PI=(): the pairwise interactions in the system, i.e. interactions between all or most atom pairs such as electrostatics. Typically a Tuple.
  • specific_inter_lists::SI=(): the specific interactions in the system, i.e. interactions between specific atoms such as bonds or angles. Typically a Tuple.
  • general_inters::GI=(): the general interactions in the system, i.e. interactions involving all atoms such as implicit solvent. Each should implement the AtomsCalculators.jl interface. Typically a Tuple.
  • neighbor_finder::NF=NoNeighborFinder(): the neighbor finder used to find close atoms and save on computation.
  • force_units::F=u"kJ * mol^-1 * nm^-1": the units of force of the system. Should be set to NoUnits if units are not being used.
  • energy_units::E=u"kJ * mol^-1": the units of energy of the system. Should be set to NoUnits if units are not being used.
  • k::K=Unitful.k or Unitful.k * Unitful.Na: the Boltzmann constant, which may be modified in some simulations. k is chosen based on the energy_units given.
  • dims::Integer=3: the number of dimensions in the system.
source
Molly.MonteCarloAnisotropicBarostatType
MonteCarloAnisotropicBarostat(pressure, temperature, boundary; n_steps=30,
                    n_iterations=1, scale_factor=0.01, scale_increment=1.1,
-                   max_volume_frac=0.3, trial_find_neighbors=false)

The Monte Carlo anisotropic barostat for controlling pressure.

For 3D systems, pressure is a SVector of length 3 with components pressX, pressY, and pressZ representing the target pressure in each axis. For 2D systems, pressure is a SVector of length 2 with components pressX and pressY. To keep an axis fixed, set the corresponding pressure to nothing.

See Chow and Ferguson 1995, Åqvist et al. 2004 and the OpenMM source code. At regular intervals a Monte Carlo step is attempted by scaling the coordinates and the bounding box by a randomly chosen amount in a randomly selected axis. The step is accepted or rejected based on

\[\Delta W = \Delta E + P \Delta V - N k_B T \ln \left( \frac{V + \Delta V}{V} \right)\]

where ΔE is the change in potential energy, P is the equilibrium pressure along the selected axis, ΔV is the change in volume, N is the number of molecules in the system, T is the equilibrium temperature and V is the system volume. If ΔW ≤ 0 the step is always accepted, if ΔW > 0 the step is accepted with probability exp(-ΔW/kT).

The scale factor is modified over time to maintain an acceptance rate of around half. If the topology of the system is set then molecules are moved as a unit so properties such as bond lengths do not change.

The barostat assumes that the simulation is being run at a constant temperature but does not actively control the temperature. It should be used alongside a temperature coupling method such as the Langevin simulator or AndersenThermostat coupling. The neighbor list is not updated when making trial moves or after accepted moves. Note that the barostat can change the bounding box of the system.

Not currently compatible with automatic differentiation using Zygote.

source
Molly.MonteCarloBarostatType
MonteCarloBarostat(pressure, temperature, boundary; n_steps=30, n_iterations=1,
+                   max_volume_frac=0.3, trial_find_neighbors=false)

The Monte Carlo anisotropic barostat for controlling pressure.

For 3D systems, pressure is a SVector of length 3 with components pressX, pressY, and pressZ representing the target pressure in each axis. For 2D systems, pressure is a SVector of length 2 with components pressX and pressY. To keep an axis fixed, set the corresponding pressure to nothing.

See Chow and Ferguson 1995, Åqvist et al. 2004 and the OpenMM source code. At regular intervals a Monte Carlo step is attempted by scaling the coordinates and the bounding box by a randomly chosen amount in a randomly selected axis. The step is accepted or rejected based on

\[\Delta W = \Delta E + P \Delta V - N k_B T \ln \left( \frac{V + \Delta V}{V} \right)\]

where ΔE is the change in potential energy, P is the equilibrium pressure along the selected axis, ΔV is the change in volume, N is the number of molecules in the system, T is the equilibrium temperature and V is the system volume. If ΔW ≤ 0 the step is always accepted, if ΔW > 0 the step is accepted with probability exp(-ΔW/kT).

The scale factor is modified over time to maintain an acceptance rate of around half. If the topology of the system is set then molecules are moved as a unit so properties such as bond lengths do not change.

The barostat assumes that the simulation is being run at a constant temperature but does not actively control the temperature. It should be used alongside a temperature coupling method such as the Langevin simulator or AndersenThermostat coupling. The neighbor list is not updated when making trial moves or after accepted moves. Note that the barostat can change the bounding box of the system.

source
Molly.MonteCarloBarostatType
MonteCarloBarostat(pressure, temperature, boundary; n_steps=30, n_iterations=1,
                    scale_factor=0.01, scale_increment=1.1, max_volume_frac=0.3,
-                   trial_find_neighbors=false)

The Monte Carlo barostat for controlling pressure.

See Chow and Ferguson 1995, Åqvist et al. 2004 and the OpenMM source code. At regular intervals a Monte Carlo step is attempted by scaling the coordinates and the bounding box by a randomly chosen amount. The step is accepted or rejected based on

\[\Delta W = \Delta E + P \Delta V - N k_B T \ln \left( \frac{V + \Delta V}{V} \right)\]

where ΔE is the change in potential energy, P is the equilibrium pressure, ΔV is the change in volume, N is the number of molecules in the system, T is the equilibrium temperature and V is the system volume. If ΔW ≤ 0 the step is always accepted, if ΔW > 0 the step is accepted with probability exp(-ΔW/kT).

The scale factor is modified over time to maintain an acceptance rate of around half. If the topology of the system is set then molecules are moved as a unit so properties such as bond lengths do not change.

The barostat assumes that the simulation is being run at a constant temperature but does not actively control the temperature. It should be used alongside a temperature coupling method such as the Langevin simulator or AndersenThermostat coupling. The neighbor list is not updated when making trial moves or after accepted moves. Note that the barostat can change the bounding box of the system.

Not currently compatible with automatic differentiation using Zygote.

source
Molly.MonteCarloLoggerType
MonteCarloLogger()
-MonteCarloLogger(T)

A logger that records acceptances in a Monte Carlo simulation.

The logged quantities include the number of new selections (n_select), the number of successful acceptances (n_accept), an array named energy_rates which stores the value of $\frac{E}{k_B T}$ i.e. the argument of the Boltzmann factor for the states, and a BitVector named state_changed that stores whether a new state was accepted for the logged step.

source
Molly.MonteCarloMembraneBarostatType
MonteCarloMembraneBarostat(pressure, tension, temperature, boundary; n_steps=30,
+                   trial_find_neighbors=false)

The Monte Carlo barostat for controlling pressure.

See Chow and Ferguson 1995, Åqvist et al. 2004 and the OpenMM source code. At regular intervals a Monte Carlo step is attempted by scaling the coordinates and the bounding box by a randomly chosen amount. The step is accepted or rejected based on

\[\Delta W = \Delta E + P \Delta V - N k_B T \ln \left( \frac{V + \Delta V}{V} \right)\]

where ΔE is the change in potential energy, P is the equilibrium pressure, ΔV is the change in volume, N is the number of molecules in the system, T is the equilibrium temperature and V is the system volume. If ΔW ≤ 0 the step is always accepted, if ΔW > 0 the step is accepted with probability exp(-ΔW/kT).

The scale factor is modified over time to maintain an acceptance rate of around half. If the topology of the system is set then molecules are moved as a unit so properties such as bond lengths do not change.

The barostat assumes that the simulation is being run at a constant temperature but does not actively control the temperature. It should be used alongside a temperature coupling method such as the Langevin simulator or AndersenThermostat coupling. The neighbor list is not updated when making trial moves or after accepted moves. Note that the barostat can change the bounding box of the system.

source
Molly.MonteCarloLoggerType
MonteCarloLogger()
+MonteCarloLogger(T)

A logger that records acceptances in a Monte Carlo simulation.

The logged quantities include the number of new selections (n_select), the number of successful acceptances (n_accept), an array named energy_rates which stores the value of $\frac{E}{k_B T}$ i.e. the argument of the Boltzmann factor for the states, and a BitVector named state_changed that stores whether a new state was accepted for the logged step.

source
Molly.MonteCarloMembraneBarostatType
MonteCarloMembraneBarostat(pressure, tension, temperature, boundary; n_steps=30,
                    n_iterations=1, scale_factor=0.01, scale_increment=1.1,
                    max_volume_frac=0.3, trial_find_neighbors=false,
-                   xy_isotropy=false, z_axis_fixed=false, constant_volume=false)

The Monte Carlo membrane barostat for controlling pressure.

Set the xy_isotropy flag to true to scale the x and y axes isotropically. Set the z_axis_fixed flag to true to uncouple the z-axis and keep it fixed. Set the constant_volume flag to true to keep the system volume constant by scaling the z-axis accordingly. The z_axis_fixed and constant_volume flags cannot be true simultaneously.

See Chow and Ferguson 1995, Åqvist et al. 2004 and the OpenMM source code. At regular intervals a Monte Carlo step is attempted by scaling the coordinates and the bounding box by a randomly chosen amount in a randomly selected axis. The step is accepted or rejected based on

\[\Delta W = \Delta E + P \Delta V - \gamma \Delta A - N k_B T \ln \left( \frac{V + \Delta V}{V} \right)\]

where ΔE is the change in potential energy, P is the equilibrium pressure along the selected axis, ΔV is the change in volume, γ is the surface tension, ΔA is the change in surface area, N is the number of molecules in the system, T is the equilibrium temperature and V is the system volume. If ΔW ≤ 0 the step is always accepted, if ΔW > 0 the step is accepted with probability exp(-ΔW/kT).

The scale factor is modified over time to maintain an acceptance rate of around half. If the topology of the system is set then molecules are moved as a unit so properties such as bond lengths do not change.

The barostat assumes that the simulation is being run at a constant temperature but does not actively control the temperature. It should be used alongside a temperature coupling method such as the Langevin simulator or AndersenThermostat coupling. The neighbor list is not updated when making trial moves or after accepted moves. Note that the barostat can change the bounding box of the system.

This barostat is only available for 3D systems. Not currently compatible with automatic differentiation using Zygote.

source
Molly.MorseBondType
MorseBond(; D, a, r0)

A Morse potential bond between two atoms.

The potential energy is defined as

\[V(r) = D(1 - e^{-a(r - r_0)})^2\]

source
Molly.MullerBrownType
MullerBrown(; A, a, b, c, x0, y0, force_units, energy_units)

The Müller-Brown potential energy surface implemented as an AtomsCalculators.jl calculator.

The potential energy is defined as

\[V(x,y) = \sum_{n=1}^{4} A_k \exp[a_k(x-x_k^0)^2 + b_k(x-x_k^0)(y-y_k^0) + c_k(y-y_k^0)^2]\]

where A, a, b, c, x0, y0 are 4-element SVectors with standard defaults.

This potential is only compatible with 2D systems. It is often used for testing algorithms that find transition states or explore minimum energy pathways. There are 3 minima and 2 saddle points with the default parameters.

source
Molly.NoseHooverType
NoseHoover(; <keyword arguments>)

The Nosé-Hoover integrator, a NVT simulator that extends velocity Verlet to control the temperature of the system.

See Evans and Holian 1985. The current implementation is limited to ergodic systems.

Not currently compatible with constraints, will print a warning and continue without applying constraints.

Arguments

  • dt::T: the time step of the simulation.
  • temperature::K: the equilibrium temperature of the simulation.
  • damping::D=100*dt: the temperature damping time scale.
  • coupling::C=NoCoupling(): the coupling which applies during the simulation.
  • remove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.
source
Molly.OverdampedLangevinType
OverdampedLangevin(; <keyword arguments>)

Simulates the overdamped Langevin equation using the Euler-Maruyama method.

Not currently compatible with constraints, will print a warning and continue without applying constraints.

Arguments

  • dt::S: the time step of the simulation.
  • temperature::K: the equilibrium temperature of the simulation.
  • friction::F: the friction coefficient of the simulation.
  • remove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.
source
Molly.PairwiseInteractionType

A pairwise interaction that will apply to all or most atom pairs.

Custom pairwise interactions should sub-type this abstract type.

source
Molly.PeriodicTorsionType
PeriodicTorsion(; periodicities, phases, ks, proper)

A periodic torsion angle between four atoms.

phases are in radians. The potential energy is defined as

\[V(\phi) = \sum_{n=1}^N k_n (1 + \cos(n \phi - \phi_{s,n}))\]

source
Molly.RBTorsionType
RBTorsion(; f1, f2, f3, f4)

A Ryckaert-Bellemans torsion angle between four atoms.

source
Molly.RectangularBoundaryType
RectangularBoundary(x, y)
-RectangularBoundary(x)

Rectangular 2D bounding box defined by two side lengths.

If one length is given then both sides will have that length. Setting one or more values to Inf gives no boundary in that dimension.

source
Molly.ReplicaExchangeLoggerType
ReplicaExchangeLogger(n_replicas)
-ReplicaExchangeLogger(T, n_replicas)

A logger that records exchanges in a replica exchange simulation.

The logged quantities include the number of exchange attempts (n_attempts), number of successful exchanges (n_exchanges), exchanged replica indices (indices), exchange steps (steps) and the value of Δ i.e. the argument of Metropolis rate for the exchanges (deltas).

source
Molly.ReplicaSystemType
ReplicaSystem(; <keyword arguments>)

A wrapper for replicas in a replica exchange simulation.

Each individual replica is a System. Properties unused in the simulation or in analysis can be left with their default values. The minimal required arguments are atoms, replica_coords, boundary and n_replicas. atoms and the elements in replica_coords should have the same length, along with atoms_data and the elements in replica_velocities if these are provided. The number of elements in replica_coords, replica_velocities, replica_loggers and the interaction arguments replica_pairwise_inters, replica_specific_inter_lists, replica_general_inters and replica_constraints should be equal to n_replicas. This is a sub-type of AbstractSystem from AtomsBase.jl and implements the interface described there.

When using ReplicaSystem with CellListMapNeighborFinder, the number of threads used for both the simulation of replicas and the neighbor finder should be set to be the same. This can be done by passing nbatches=(min(n, 8), n) to CellListMapNeighborFinder during construction where n is the number of threads to be used per replica.

Arguments

  • atoms::A: the atoms, or atom equivalents, in the system. Can be of any type but should be a bits type if the GPU is used.
  • replica_coords: the coordinates of the atoms in each replica.
  • boundary::B: the bounding box in which the simulation takes place.
  • n_replicas::Integer: the number of replicas of the system.
  • replica_velocities=[zero(replica_coords[1]) * u"ps^-1" for _ in 1:n_replicas]: the velocities of the atoms in each replica.
  • atoms_data::AD: other data associated with the atoms, allowing the atoms to be bits types and hence work on the GPU.
  • topology::TO=nothing: topological information about the system such as which atoms are in the same molecule (to be used if the same for all replicas). This is only used if no value is passed to the argument replica_topology.
  • replica_topology=[nothing for _ in 1:n_replicas]: the topological information for each replica.
  • pairwise_inters=(): the pairwise interactions in the system, i.e. interactions between all or most atom pairs such as electrostatics (to be used if the same for all replicas). Typically a Tuple. This is only used if no value is passed to the argument replica_pairwise_inters.
  • replica_pairwise_inters=[() for _ in 1:n_replicas]: the pairwise interactions for each replica.
  • specific_inter_lists=(): the specific interactions in the system, i.e. interactions between specific atoms such as bonds or angles (to be used if the same for all replicas). Typically a Tuple. This is only used if no value is passed to the argument replica_specific_inter_lists.
  • replica_specific_inter_lists=[() for _ in 1:n_replicas]: the specific interactions in each replica.
  • general_inters=(): the general interactions in the system, i.e. interactions involving all atoms such as implicit solvent (to be used if the same for all replicas). Each should implement the AtomsCalculators.jl interface. Typically a Tuple. This is only used if no value is passed to the argument replica_general_inters.
  • replica_general_inters=[() for _ in 1:n_replicas]: the general interactions for each replica.
  • constraints::CN=(): the constraints for bonds and angles in the system (to be used if the same for all replicas). Typically a Tuple. This is only used if no value is passed to the argument replica_constraints.
  • replica_constraints=[() for _ in 1:n_replicas]: the constraints for bonds and angles in each replica.
  • neighbor_finder::NF=NoNeighborFinder(): the neighbor finder used to find close atoms and save on computation. It is duplicated for each replica.
  • replica_loggers=[() for _ in 1:n_replicas]: the loggers for each replica that record properties of interest during a simulation.
  • exchange_logger::EL=ReplicaExchangeLogger(n_replicas): the logger used to record the exchange of replicas.
  • force_units::F=u"kJ * mol^-1 * nm^-1": the units of force of the system. Should be set to NoUnits if units are not being used.
  • energy_units::E=u"kJ * mol^-1": the units of energy of the system. Should be set to NoUnits if units are not being used.
  • k::K=Unitful.k or Unitful.k * Unitful.Na: the Boltzmann constant, which may be modified in some simulations. k is chosen based on the energy_units given.
  • data::DA=nothing: arbitrary data associated with the replica system.
source
Molly.RescaleThermostatType
RescaleThermostat(temperature)

The velocity rescaling thermostat for controlling temperature.

Velocities are immediately rescaled to match a target temperature. The scaling factor for the velocities each step is

\[\lambda = \sqrt{\frac{T_0}{T}}\]

This thermostat should be used with caution as it can lead to simulation artifacts.

source
Molly.SHAKE_RATTLEType
SHAKE_RATTLE(constraints, n_atoms, dist_tolerance, vel_tolerance)

Constrain distances during a simulation using the SHAKE and RATTLE algorithms.

Velocity constraints will be imposed for simulators that integrate velocities such as VelocityVerlet. See Ryckaert et al. 1977 for SHAKE, Andersen 1983 for RATTLE and Elber et al. 2011 for a derivation of the linear system solved to satisfy the RATTLE algorithm.

Not currently compatible with GPU simulation.

Arguments

  • constraints: a vector of constraints to be imposed on the system.
  • n_atoms::Integer: the number of atoms in the system.
  • dist_tolerance: the tolerance used to end the iterative procedure when calculating position constraints, should have the same units as the coordinates.
  • vel_tolerance: the tolerance used to end the iterative procedure when calculating velocity constraints, should have the same units as the velocities * the coordinates.
source
Molly.SoftSphereType
SoftSphere(; cutoff, use_neighbors, lorentz_mixing, force_units, energy_units, skip_shortcut)

The soft-sphere potential.

The potential energy is defined as

\[V(r_{ij}) = 4\varepsilon_{ij} \left(\frac{\sigma_{ij}}{r_{ij}}\right)^{12}\]

source
Molly.SpecificInteractionType

A specific interaction between sets of specific atoms, e.g. a bond angle.

Custom specific interactions should sub-type this abstract type.

source
Molly.SteepestDescentMinimizerType
SteepestDescentMinimizer(; <keyword arguments>)

Steepest descent energy minimization.

Not currently compatible with automatic differentiation using Zygote.

Arguments

  • step_size::D=0.01u"nm": the initial maximum displacement.
  • max_steps::Int=1000: the maximum number of steps.
  • tol::F=1000.0u"kJ * mol^-1 * nm^-1": the maximum force below which to finish minimization.
  • log_stream::L=devnull: stream to print minimization progress to.
source
Molly.StormerVerletType
StormerVerlet(; <keyword arguments>)

The Störmer-Verlet integrator.

The velocity calculation is accurate to O(dt).

Does not currently work with coupling methods that alter the velocity. Does not currently remove the center of mass motion.

Arguments

  • dt::T: the time step of the simulation.
  • coupling::C=NoCoupling(): the coupling which applies during the simulation.
source
Molly.StructureWriterType
StructureWriter(n_steps, filepath, excluded_res=String[])

Write 3D output structures to a file in the PDB format throughout a simulation.

The System should have atoms_data defined.

source
Molly.SystemType
System(; <keyword arguments>)

A physical system to be simulated.

Properties unused in the simulation or in analysis can be left with their default values. The minimal required arguments are atoms, coords and boundary. atoms and coords should have the same length, along with velocities and atoms_data if these are provided. This is a sub-type of AbstractSystem from AtomsBase.jl and implements the interface described there.

Arguments

  • atoms::A: the atoms, or atom equivalents, in the system. Can be of any type but should be a bits type if the GPU is used.
  • coords::C: the coordinates of the atoms in the system. Typically a vector of SVectors of 2 or 3 dimensions.
  • boundary::B: the bounding box in which the simulation takes place.
  • velocities::V=zero(coords) * u"ps^-1": the velocities of the atoms in the system.
  • atoms_data::AD=[]: other data associated with the atoms, allowing the atoms to be bits types and hence work on the GPU.
  • topology::TO=nothing: topological information about the system such as which atoms are in the same molecule.
  • pairwise_inters::PI=(): the pairwise interactions in the system, i.e. interactions between all or most atom pairs such as electrostatics. Typically a Tuple.
  • specific_inter_lists::SI=(): the specific interactions in the system, i.e. interactions between specific atoms such as bonds or angles. Typically a Tuple.
  • general_inters::GI=(): the general interactions in the system, i.e. interactions involving all atoms such as implicit solvent. Each should implement the AtomsCalculators.jl interface. Typically a Tuple.
  • constraints::CN=(): the constraints for bonds and angles in the system. Typically a Tuple.
  • neighbor_finder::NF=NoNeighborFinder(): the neighbor finder used to find close atoms and save on computation.
  • loggers::L=(): the loggers that record properties of interest during a simulation.
  • force_units::F=u"kJ * mol^-1 * nm^-1": the units of force of the system. Should be set to NoUnits if units are not being used.
  • energy_units::E=u"kJ * mol^-1": the units of energy of the system. Should be set to NoUnits if units are not being used.
  • k::K=Unitful.k or Unitful.k * Unitful.Na: the Boltzmann constant, which may be modified in some simulations. k is chosen based on the energy_units given.
  • data::DA=nothing: arbitrary data associated with the system.
source
Molly.SystemMethod
System(coordinate_file, force_field; <keyword arguments>)

Read a coordinate file in a file format readable by Chemfiles and apply a force field to it.

Atom names should exactly match residue templates - no searching of residue templates is carried out.

System(coordinate_file, topology_file; <keyword arguments>)
-System(T, coordinate_file, topology_file; <keyword arguments>)

Read a Gromacs coordinate file and a Gromacs topology file with all includes collapsed into one file.

Gromacs file reading should be considered experimental. The implicit_solvent, kappa and rename_terminal_res keyword arguments are not available when reading Gromacs files.

Arguments

  • boundary=nothing: the bounding box used for simulation, read from the file by default.
  • velocities=nothing: the velocities of the atoms in the system, set to zero by default.
  • loggers=(): the loggers that record properties of interest during a simulation.
  • units::Bool=true: whether to use Unitful quantities.
  • gpu::Bool=false: whether to move the relevant parts of the system onto the GPU.
  • dist_cutoff=1.0u"nm": cutoff distance for long-range interactions.
  • dist_neighbors=1.2u"nm": cutoff distance for the neighbor list, should be greater than dist_cutoff.
  • center_coords::Bool=true: whether to center the coordinates in the simulation box.
  • use_cell_list::Bool=true: whether to use CellListMapNeighborFinder on CPU. If false, DistanceNeighborFinder is used.
  • data=nothing: arbitrary data associated with the system.
  • implicit_solvent=nothing: specify a string to add an implicit solvent model, options are "obc1", "obc2" and "gbn2".
  • kappa=0.0u"nm^-1": the kappa value for the implicit solvent model if one is used.
  • rename_terminal_res=true: whether to rename the first and last residues to match the appropriate atom templates, for example the first (N-terminal) residue could be changed from "MET" to "NMET".
source
Molly.SystemMethod
System(sys; <keyword arguments>)

Convenience constructor for changing properties in a System.

A copy of the System is returned with the provided keyword arguments modified.

source
Molly.SystemMethod
System(abstract_system; force_units=u"kJ * mol^-1 * nm^-1", energy_units=u"kJ * mol^-1")

Convert an AtomsBase AbstractSystem to a Molly System.

force_units and energy_units should be set as appropriate. To add properties not present in the AtomsBase interface (e.g. pair potentials) use the convenience constructor System(sys::System).

source
Molly.SystemMethod
System(crystal; <keyword arguments>)

Construct a System from a SimpleCrystals.jl Crystal struct.

Properties unused in the simulation or in analysis can be left with their default values. atoms, atoms_data, coords and boundary are automatically calcualted from the Crystal struct. Extra atom paramaters like σ have to be added manually after construction using the convenience constructor System(sys; <keyword arguments>).

source
Molly.TemperatureREMDType
TemperatureREMD(; <keyword arguments>)

A simulator for a parallel temperature replica exchange MD (T-REMD) simulation on a ReplicaSystem.

See Sugita and Okamoto 1999. The corresponding ReplicaSystem should have the same number of replicas as the number of temperatures in the simulator. When calling simulate!, the assign_velocities keyword argument determines whether to assign random velocities at the appropriate temperature for each replica.

Not currently compatible with automatic differentiation using Zygote.

Arguments

  • dt::DT: the time step of the simulation.
  • temperatures::TP: the temperatures corresponding to the replicas.
  • simulators::ST: individual simulators for simulating each replica.
  • exchange_time::ET: the time interval between replica exchange attempts.
source
Molly.TimeCorrelationLoggerType
TimeCorrelationLogger(observableA::Function, observableB::Function,
+                   xy_isotropy=false, z_axis_fixed=false, constant_volume=false)

The Monte Carlo membrane barostat for controlling pressure.

Set the xy_isotropy flag to true to scale the x and y axes isotropically. Set the z_axis_fixed flag to true to uncouple the z-axis and keep it fixed. Set the constant_volume flag to true to keep the system volume constant by scaling the z-axis accordingly. The z_axis_fixed and constant_volume flags cannot be true simultaneously.

See Chow and Ferguson 1995, Åqvist et al. 2004 and the OpenMM source code. At regular intervals a Monte Carlo step is attempted by scaling the coordinates and the bounding box by a randomly chosen amount in a randomly selected axis. The step is accepted or rejected based on

\[\Delta W = \Delta E + P \Delta V - \gamma \Delta A - N k_B T \ln \left( \frac{V + \Delta V}{V} \right)\]

where ΔE is the change in potential energy, P is the equilibrium pressure along the selected axis, ΔV is the change in volume, γ is the surface tension, ΔA is the change in surface area, N is the number of molecules in the system, T is the equilibrium temperature and V is the system volume. If ΔW ≤ 0 the step is always accepted, if ΔW > 0 the step is accepted with probability exp(-ΔW/kT).

The scale factor is modified over time to maintain an acceptance rate of around half. If the topology of the system is set then molecules are moved as a unit so properties such as bond lengths do not change.

The barostat assumes that the simulation is being run at a constant temperature but does not actively control the temperature. It should be used alongside a temperature coupling method such as the Langevin simulator or AndersenThermostat coupling. The neighbor list is not updated when making trial moves or after accepted moves. Note that the barostat can change the bounding box of the system.

This barostat is only available for 3D systems.

source
Molly.MorseBondType
MorseBond(; D, a, r0)

A Morse potential bond between two atoms.

The potential energy is defined as

\[V(r) = D(1 - e^{-a(r - r_0)})^2\]

source
Molly.MullerBrownType
MullerBrown(; A, a, b, c, x0, y0, force_units, energy_units)

The Müller-Brown potential energy surface implemented as an AtomsCalculators.jl calculator.

The potential energy is defined as

\[V(x,y) = \sum_{n=1}^{4} A_k \exp[a_k(x-x_k^0)^2 + b_k(x-x_k^0)(y-y_k^0) + c_k(y-y_k^0)^2]\]

where A, a, b, c, x0, y0 are 4-element SVectors with standard defaults.

This potential is only compatible with 2D systems. It is often used for testing algorithms that find transition states or explore minimum energy pathways. There are 3 minima and 2 saddle points with the default parameters.

source
Molly.NoseHooverType
NoseHoover(; <keyword arguments>)

The Nosé-Hoover integrator, a NVT simulator that extends velocity Verlet to control the temperature of the system.

See Evans and Holian 1985. The current implementation is limited to ergodic systems.

Not currently compatible with constraints, will print a warning and continue without applying constraints.

Arguments

  • dt::T: the time step of the simulation.
  • temperature::K: the equilibrium temperature of the simulation.
  • damping::D=100*dt: the temperature damping time scale.
  • coupling::C=NoCoupling(): the coupling which applies during the simulation.
  • remove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.
source
Molly.OverdampedLangevinType
OverdampedLangevin(; <keyword arguments>)

Simulates the overdamped Langevin equation using the Euler-Maruyama method.

Not currently compatible with constraints, will print a warning and continue without applying constraints.

Arguments

  • dt::S: the time step of the simulation.
  • temperature::K: the equilibrium temperature of the simulation.
  • friction::F: the friction coefficient of the simulation.
  • remove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.
source
Molly.PeriodicTorsionType
PeriodicTorsion(; periodicities, phases, ks, proper)

A periodic torsion angle between four atoms.

phases are in radians. The potential energy is defined as

\[V(\phi) = \sum_{n=1}^N k_n (1 + \cos(n \phi - \phi_{s,n}))\]

source
Molly.RBTorsionType
RBTorsion(; f1, f2, f3, f4)

A Ryckaert-Bellemans torsion angle between four atoms.

source
Molly.RectangularBoundaryType
RectangularBoundary(x, y)
+RectangularBoundary(x)

Rectangular 2D bounding box defined by two side lengths.

If one length is given then both sides will have that length. Setting one or more values to Inf gives no boundary in that dimension.

source
Molly.ReplicaExchangeLoggerType
ReplicaExchangeLogger(n_replicas)
+ReplicaExchangeLogger(T, n_replicas)

A logger that records exchanges in a replica exchange simulation.

The logged quantities include the number of exchange attempts (n_attempts), number of successful exchanges (n_exchanges), exchanged replica indices (indices), exchange steps (steps) and the value of Δ i.e. the argument of Metropolis rate for the exchanges (deltas).

source
Molly.ReplicaSystemType
ReplicaSystem(; <keyword arguments>)

A wrapper for replicas in a replica exchange simulation.

Each individual replica is a System. Properties unused in the simulation or in analysis can be left with their default values. The minimal required arguments are atoms, replica_coords, boundary and n_replicas. atoms and the elements in replica_coords should have the same length, along with atoms_data and the elements in replica_velocities if these are provided. The number of elements in replica_coords, replica_velocities, replica_loggers and the interaction arguments replica_pairwise_inters, replica_specific_inter_lists, replica_general_inters and replica_constraints should be equal to n_replicas. This is a sub-type of AbstractSystem from AtomsBase.jl and implements the interface described there.

When using ReplicaSystem with CellListMapNeighborFinder, the number of threads used for both the simulation of replicas and the neighbor finder should be set to be the same. This can be done by passing nbatches=(min(n, 8), n) to CellListMapNeighborFinder during construction where n is the number of threads to be used per replica.

Arguments

  • atoms::A: the atoms, or atom equivalents, in the system. Can be of any type but should be a bits type if the GPU is used.
  • replica_coords: the coordinates of the atoms in each replica.
  • boundary::B: the bounding box in which the simulation takes place.
  • n_replicas::Integer: the number of replicas of the system.
  • replica_velocities=[zero(replica_coords[1]) * u"ps^-1" for _ in 1:n_replicas]: the velocities of the atoms in each replica.
  • atoms_data::AD: other data associated with the atoms, allowing the atoms to be bits types and hence work on the GPU.
  • topology::TO=nothing: topological information about the system such as which atoms are in the same molecule (to be used if the same for all replicas). This is only used if no value is passed to the argument replica_topology.
  • replica_topology=[nothing for _ in 1:n_replicas]: the topological information for each replica.
  • pairwise_inters=(): the pairwise interactions in the system, i.e. interactions between all or most atom pairs such as electrostatics (to be used if the same for all replicas). Typically a Tuple. This is only used if no value is passed to the argument replica_pairwise_inters.
  • replica_pairwise_inters=[() for _ in 1:n_replicas]: the pairwise interactions for each replica.
  • specific_inter_lists=(): the specific interactions in the system, i.e. interactions between specific atoms such as bonds or angles (to be used if the same for all replicas). Typically a Tuple. This is only used if no value is passed to the argument replica_specific_inter_lists.
  • replica_specific_inter_lists=[() for _ in 1:n_replicas]: the specific interactions in each replica.
  • general_inters=(): the general interactions in the system, i.e. interactions involving all atoms such as implicit solvent (to be used if the same for all replicas). Each should implement the AtomsCalculators.jl interface. Typically a Tuple. This is only used if no value is passed to the argument replica_general_inters.
  • replica_general_inters=[() for _ in 1:n_replicas]: the general interactions for each replica.
  • constraints::CN=(): the constraints for bonds and angles in the system (to be used if the same for all replicas). Typically a Tuple. This is only used if no value is passed to the argument replica_constraints.
  • replica_constraints=[() for _ in 1:n_replicas]: the constraints for bonds and angles in each replica.
  • neighbor_finder::NF=NoNeighborFinder(): the neighbor finder used to find close atoms and save on computation. It is duplicated for each replica.
  • replica_loggers=[() for _ in 1:n_replicas]: the loggers for each replica that record properties of interest during a simulation.
  • exchange_logger::EL=ReplicaExchangeLogger(n_replicas): the logger used to record the exchange of replicas.
  • force_units::F=u"kJ * mol^-1 * nm^-1": the units of force of the system. Should be set to NoUnits if units are not being used.
  • energy_units::E=u"kJ * mol^-1": the units of energy of the system. Should be set to NoUnits if units are not being used.
  • k::K=Unitful.k or Unitful.k * Unitful.Na: the Boltzmann constant, which may be modified in some simulations. k is chosen based on the energy_units given.
  • data::DA=nothing: arbitrary data associated with the replica system.
source
Molly.RescaleThermostatType
RescaleThermostat(temperature)

The velocity rescaling thermostat for controlling temperature.

Velocities are immediately rescaled to match a target temperature. The scaling factor for the velocities each step is

\[\lambda = \sqrt{\frac{T_0}{T}}\]

This thermostat should be used with caution as it can lead to simulation artifacts.

source
Molly.SHAKE_RATTLEType
SHAKE_RATTLE(constraints, n_atoms, dist_tolerance, vel_tolerance)

Constrain distances during a simulation using the SHAKE and RATTLE algorithms.

Velocity constraints will be imposed for simulators that integrate velocities such as VelocityVerlet. See Ryckaert et al. 1977 for SHAKE, Andersen 1983 for RATTLE and Elber et al. 2011 for a derivation of the linear system solved to satisfy the RATTLE algorithm.

Not currently compatible with GPU simulation.

Arguments

  • constraints: a vector of constraints to be imposed on the system.
  • n_atoms::Integer: the number of atoms in the system.
  • dist_tolerance: the tolerance used to end the iterative procedure when calculating position constraints, should have the same units as the coordinates.
  • vel_tolerance: the tolerance used to end the iterative procedure when calculating velocity constraints, should have the same units as the velocities * the coordinates.
source
Molly.SoftSphereType
SoftSphere(; cutoff, use_neighbors, shortcut, σ_mixing, ϵ_mixing)

The soft-sphere potential.

The potential energy is defined as

\[V(r_{ij}) = 4\varepsilon_{ij} \left(\frac{\sigma_{ij}}{r_{ij}}\right)^{12}\]

source
Molly.SteepestDescentMinimizerType
SteepestDescentMinimizer(; <keyword arguments>)

Steepest descent energy minimization.

Arguments

  • step_size::D=0.01u"nm": the initial maximum displacement.
  • max_steps::Int=1000: the maximum number of steps.
  • tol::F=1000.0u"kJ * mol^-1 * nm^-1": the maximum force below which to finish minimization.
  • log_stream::L=devnull: stream to print minimization progress to.
source
Molly.StormerVerletType
StormerVerlet(; <keyword arguments>)

The Störmer-Verlet integrator.

The velocity calculation is accurate to O(dt).

Does not currently work with coupling methods that alter the velocity. Does not currently remove the center of mass motion.

Arguments

  • dt::T: the time step of the simulation.
  • coupling::C=NoCoupling(): the coupling which applies during the simulation.
source
Molly.StructureWriterType
StructureWriter(n_steps, filepath, excluded_res=String[])

Write 3D output structures to a file in the PDB format throughout a simulation.

The System should have atoms_data defined.

source
Molly.SystemType
System(; <keyword arguments>)

A physical system to be simulated.

Properties unused in the simulation or in analysis can be left with their default values. The minimal required arguments are atoms, coords and boundary. atoms and coords should have the same length, along with velocities and atoms_data if these are provided. This is a sub-type of AbstractSystem from AtomsBase.jl and implements the interface described there.

Arguments

  • atoms::A: the atoms, or atom equivalents, in the system. Can be of any type but should be a bits type if the GPU is used.
  • coords::C: the coordinates of the atoms in the system. Typically a vector of SVectors of 2 or 3 dimensions.
  • boundary::B: the bounding box in which the simulation takes place.
  • velocities::V=zero(coords) * u"ps^-1": the velocities of the atoms in the system.
  • atoms_data::AD=[]: other data associated with the atoms, allowing the atoms to be bits types and hence work on the GPU.
  • topology::TO=nothing: topological information about the system such as which atoms are in the same molecule.
  • pairwise_inters::PI=(): the pairwise interactions in the system, i.e. interactions between all or most atom pairs such as electrostatics. Typically a Tuple.
  • specific_inter_lists::SI=(): the specific interactions in the system, i.e. interactions between specific atoms such as bonds or angles. Typically a Tuple.
  • general_inters::GI=(): the general interactions in the system, i.e. interactions involving all atoms such as implicit solvent. Each should implement the AtomsCalculators.jl interface. Typically a Tuple.
  • constraints::CN=(): the constraints for bonds and angles in the system. Typically a Tuple.
  • neighbor_finder::NF=NoNeighborFinder(): the neighbor finder used to find close atoms and save on computation.
  • loggers::L=(): the loggers that record properties of interest during a simulation.
  • force_units::F=u"kJ * mol^-1 * nm^-1": the units of force of the system. Should be set to NoUnits if units are not being used.
  • energy_units::E=u"kJ * mol^-1": the units of energy of the system. Should be set to NoUnits if units are not being used.
  • k::K=Unitful.k or Unitful.k * Unitful.Na: the Boltzmann constant, which may be modified in some simulations. k is chosen based on the energy_units given.
  • data::DA=nothing: arbitrary data associated with the system.
source
Molly.SystemMethod
System(coordinate_file, force_field; <keyword arguments>)

Read a coordinate file in a file format readable by Chemfiles and apply a force field to it.

Atom names should exactly match residue templates - no searching of residue templates is carried out.

System(coordinate_file, topology_file; <keyword arguments>)
+System(T, coordinate_file, topology_file; <keyword arguments>)

Read a Gromacs coordinate file and a Gromacs topology file with all includes collapsed into one file.

Gromacs file reading should be considered experimental. The implicit_solvent, kappa and rename_terminal_res keyword arguments are not available when reading Gromacs files.

Arguments

  • boundary=nothing: the bounding box used for simulation, read from the file by default.
  • velocities=nothing: the velocities of the atoms in the system, set to zero by default.
  • loggers=(): the loggers that record properties of interest during a simulation.
  • units::Bool=true: whether to use Unitful quantities.
  • gpu::Bool=false: whether to move the relevant parts of the system onto the GPU.
  • dist_cutoff=1.0u"nm": cutoff distance for long-range interactions.
  • dist_neighbors=1.2u"nm": cutoff distance for the neighbor list, should be greater than dist_cutoff.
  • center_coords::Bool=true: whether to center the coordinates in the simulation box.
  • use_cell_list::Bool=true: whether to use CellListMapNeighborFinder on CPU. If false, DistanceNeighborFinder is used.
  • data=nothing: arbitrary data associated with the system.
  • implicit_solvent=nothing: specify a string to add an implicit solvent model, options are "obc1", "obc2" and "gbn2".
  • kappa=0.0u"nm^-1": the kappa value for the implicit solvent model if one is used.
  • rename_terminal_res=true: whether to rename the first and last residues to match the appropriate atom templates, for example the first (N-terminal) residue could be changed from "MET" to "NMET".
source
Molly.SystemMethod
System(sys; <keyword arguments>)

Convenience constructor for changing properties in a System.

A copy of the System is returned with the provided keyword arguments modified.

source
Molly.SystemMethod
System(abstract_system; force_units=u"kJ * mol^-1 * nm^-1", energy_units=u"kJ * mol^-1")

Convert an AtomsBase AbstractSystem to a Molly System.

force_units and energy_units should be set as appropriate. To add properties not present in the AtomsBase interface (e.g. pair potentials) use the convenience constructor System(sys::System).

source
Molly.SystemMethod
System(crystal; <keyword arguments>)

Construct a System from a SimpleCrystals.jl Crystal struct.

Properties unused in the simulation or in analysis can be left with their default values. atoms, atoms_data, coords and boundary are automatically calcualted from the Crystal struct. Extra atom paramaters like σ have to be added manually after construction using the convenience constructor System(sys; <keyword arguments>).

source
Molly.TemperatureREMDType
TemperatureREMD(; <keyword arguments>)

A simulator for a parallel temperature replica exchange MD (T-REMD) simulation on a ReplicaSystem.

See Sugita and Okamoto 1999. The corresponding ReplicaSystem should have the same number of replicas as the number of temperatures in the simulator. When calling simulate!, the assign_velocities keyword argument determines whether to assign random velocities at the appropriate temperature for each replica.

Arguments

  • dt::DT: the time step of the simulation.
  • temperatures::TP: the temperatures corresponding to the replicas.
  • simulators::ST: individual simulators for simulating each replica.
  • exchange_time::ET: the time interval between replica exchange attempts.
source
Molly.TimeCorrelationLoggerType
TimeCorrelationLogger(observableA::Function, observableB::Function,
                         TA::DataType, TB::DataType,
-                        observable_length::Integer, n_correlation::Integer)

A time correlation logger.

Estimates statistical correlations of normalized form

\[C(t)=\frac{\langle A_t\cdot B_0\rangle -\langle A\rangle\cdot \langle B\rangle}{\sqrt{\langle |A|^2\rangle\langle |B|^2\rangle}}\]

or unnormalized form

\[C(t)=\langle A_t\cdot B_0\rangle -\langle A \rangle\cdot \langle B\rangle\]

These can be used to estimate statistical error, or to compute transport coefficients from Green-Kubo type formulas. A and B are observables, functions of the form observable(sys::System, neighbors; n_threads::Integer). The return values of A and B can be of scalar or vector type (including Vector{SVector{...}}, like positions or velocities) and must implement dot.

n_correlation should typically be chosen so that dt * n_correlation > t_corr, where dt is the simulation timestep and t_corr is the decorrelation time for the considered system and observables. For the purpose of numerical stability, the logger internally records sums instead of running averages. The normalized and unnormalized form of the correlation function can be retrieved through values(logger::TimeCorrelationLogger; normalize::Bool).

Arguments

  • observableA::Function: the function corresponding to observable A.
  • observableB::Function: the function corresponding to observable B.
  • TA::DataType: the type returned by observableA, supporting zero(TA).
  • TB::DataType: the type returned by observableB, supporting zero(TB).
  • observable_length::Integer: the length of the observables if they are vectors, or 1 if they are scalar-valued.
  • n_correlation::Integer: the length of the computed correlation vector.
source
Molly.TreeNeighborFinderType
TreeNeighborFinder(; eligible, dist_cutoff, special, n_steps)

Find close atoms by distance using a tree search.

Can not be used if one or more dimensions has infinite boundaries. Can not be used with TriclinicBoundary.

source
Molly.TriclinicBoundaryType
TriclinicBoundary(v1, v2, v3; approx_images=true)
+                        observable_length::Integer, n_correlation::Integer)

A time correlation logger.

Estimates statistical correlations of normalized form

\[C(t)=\frac{\langle A_t\cdot B_0\rangle -\langle A\rangle\cdot \langle B\rangle}{\sqrt{\langle |A|^2\rangle\langle |B|^2\rangle}}\]

or unnormalized form

\[C(t)=\langle A_t\cdot B_0\rangle -\langle A \rangle\cdot \langle B\rangle\]

These can be used to estimate statistical error, or to compute transport coefficients from Green-Kubo type formulas. A and B are observables, functions of the form observable(sys::System, neighbors; n_threads::Integer). The return values of A and B can be of scalar or vector type (including Vector{SVector{...}}, like positions or velocities) and must implement dot.

n_correlation should typically be chosen so that dt * n_correlation > t_corr, where dt is the simulation timestep and t_corr is the decorrelation time for the considered system and observables. For the purpose of numerical stability, the logger internally records sums instead of running averages. The normalized and unnormalized form of the correlation function can be retrieved through values(logger::TimeCorrelationLogger; normalize::Bool).

Arguments

  • observableA::Function: the function corresponding to observable A.
  • observableB::Function: the function corresponding to observable B.
  • TA::DataType: the type returned by observableA, supporting zero(TA).
  • TB::DataType: the type returned by observableB, supporting zero(TB).
  • observable_length::Integer: the length of the observables if they are vectors, or 1 if they are scalar-valued.
  • n_correlation::Integer: the length of the computed correlation vector.
source
Molly.TreeNeighborFinderType
TreeNeighborFinder(; eligible, dist_cutoff, special, n_steps)

Find close atoms by distance using a tree search.

Can not be used if one or more dimensions has infinite boundaries. Can not be used with TriclinicBoundary.

source
Molly.TriclinicBoundaryType
TriclinicBoundary(v1, v2, v3; approx_images=true)
 TriclinicBoundary(SVector(v1, v2, v3); approx_images=true)
 TriclinicBoundary(SVector(l1, l2, l3), SVector(α, β, γ); approx_images=true)
-TriclinicBoundary(arr; approx_images=true)

Triclinic 3D bounding box defined by 3 SVector{3} basis vectors or basis vector lengths and angles α/β/γ in radians.

The first basis vector must point along the x-axis and the second must lie in the xy plane. An approximation is used to find the closest periodic image when using the minimum image convention. The approximation is correct for distances shorter than half the shortest box height/width. Setting the keyword argument approx_images to false means the exact closest image is found, which is slower.

Not currently able to simulate a cubic box, use CubicBoundary or small offsets instead. Not currently compatible with infinite boundaries. Not currently compatible with automatic differentiation using Zygote.

source
Molly.VelocityVerletType
VelocityVerlet(; <keyword arguments>)

The velocity Verlet integrator.

Arguments

  • dt::T: the time step of the simulation.
  • coupling::C=NoCoupling(): the coupling which applies during the simulation.
  • remove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.
source
Molly.VerletType
Verlet(; <keyword arguments>)

The leapfrog Verlet integrator.

This is a leapfrog integrator, so the velocities are offset by half a time step behind the positions.

Arguments

  • dt::T: the time step of the simulation.
  • coupling::C=NoCoupling(): the coupling which applies during the simulation.
  • remove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.
source
Base.valuesMethod
values(logger)
+TriclinicBoundary(arr; approx_images=true)

Triclinic 3D bounding box defined by 3 SVector{3} basis vectors or basis vector lengths and angles α/β/γ in radians.

The first basis vector must point along the x-axis and the second must lie in the xy plane. An approximation is used to find the closest periodic image when using the minimum image convention. The approximation is correct for distances shorter than half the shortest box height/width. Setting the keyword argument approx_images to false means the exact closest image is found, which is slower.

Not currently able to simulate a cubic box, use CubicBoundary or small offsets instead. Not currently compatible with infinite boundaries.

source
Molly.VelocityVerletType
VelocityVerlet(; <keyword arguments>)

The velocity Verlet integrator.

Arguments

  • dt::T: the time step of the simulation.
  • coupling::C=NoCoupling(): the coupling which applies during the simulation.
  • remove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.
source
Molly.VerletType
Verlet(; <keyword arguments>)

The leapfrog Verlet integrator.

This is a leapfrog integrator, so the velocities are offset by half a time step behind the positions.

Arguments

  • dt::T: the time step of the simulation.
  • coupling::C=NoCoupling(): the coupling which applies during the simulation.
  • remove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.
source
Base.valuesMethod
values(logger)
 values(logger::TimeCorrelationLogger; normalize::Bool=true)
-values(logger::AverageObservableLogger; std::Bool=true)

Access the stored observations in a logger.

source
Molly.CoordinateLoggerMethod
CoordinateLogger(n_steps; dims=3)
-CoordinateLogger(T, n_steps; dims=3)

Log the coordinates throughout a simulation.

source
Molly.PressureLoggerMethod
PressureLogger(n_steps)
-PressureLogger(T, n_steps)

Log the pressure of a system throughout a simulation.

This should only be used on systems containing just pairwise interactions, or where the specific interactions, general interactions and constraints do not contribute to the pressure.

source
Molly.VelocityLoggerMethod
VelocityLogger(n_steps; dims=3)
-VelocityLogger(T, n_steps; dims=3)

Log the velocities throughout a simulation.

source
Molly.VirialLoggerMethod
VirialLogger(n_steps)
-VirialLogger(T, n_steps)

Log the virial of a system throughout a simulation.

This should only be used on systems containing just pairwise interactions, or where the specific interactions, general interactions and constraints do not contribute to the virial.

source
Molly.accelerationsMethod
accelerations(system, neighbors=find_neighbors(sys); n_threads=Threads.nthreads())

Calculate the accelerations of all atoms in a system using the pairwise, specific and general interactions and Newton's second law of motion.

source
Molly.add_position_restraintsMethod
add_position_restraints(sys, k; atom_selector=is_any_atom, restrain_coords=sys.coords)

Return a copy of a System with HarmonicPositionRestraints added to restrain the atoms.

The force constant k can be a single value or an array of equal length to the number of atoms in the system. The atom_selector function takes in each atom and atom data and determines whether to restrain that atom. For example, is_heavy_atom means non-hydrogen atoms are restrained.

source
Molly.apply_coupling!Method
apply_coupling!(system, coupling, simulator, neighbors=nothing,
-                step_n=0; n_threads=Threads.nthreads())

Apply a coupler to modify a simulation.

Returns whether the coupling has invalidated the currently stored forces, for example by changing the coordinates. This information is useful for some simulators. If coupling is a tuple or named tuple then each coupler will be applied in turn. Custom couplers should implement this function.

source
Molly.apply_position_constraints!Method
apply_position_constraints!(sys, coord_storage; n_threads::Integer=Threads.nthreads())
+values(logger::AverageObservableLogger; std::Bool=true)

Access the stored observations in a logger.

source
Molly.CoordinatesLoggerMethod
CoordinatesLogger(n_steps; dims=3)
+CoordinatesLogger(T, n_steps; dims=3)

Log the coordinates throughout a simulation.

source
Molly.DensityLoggerMethod
DensityLogger(n_steps)
+DensityLogger(T, n_steps)

Log the density of a system throughout a simulation.

Not compatible with infinite boundaries.

source
Molly.PressureLoggerMethod
PressureLogger(n_steps)
+PressureLogger(T, n_steps)

Log the pressure of a system throughout a simulation.

This should only be used on systems containing just pairwise interactions, or where the specific interactions, general interactions and constraints do not contribute to the pressure.

source
Molly.VelocitiesLoggerMethod
VelocitiesLogger(n_steps; dims=3)
+VelocitiesLogger(T, n_steps; dims=3)

Log the velocities throughout a simulation.

source
Molly.VirialLoggerMethod
VirialLogger(n_steps)
+VirialLogger(T, n_steps)

Log the virial of a system throughout a simulation.

This should only be used on systems containing just pairwise interactions, or where the specific interactions, general interactions and constraints do not contribute to the virial.

source
Molly.VolumeLoggerMethod
VolumeLogger(n_steps)
+VolumeLogger(T, n_steps)

Log the volume of a system throughout a simulation.

Not compatible with infinite boundaries.

source
Molly.accelerationsMethod
accelerations(system, neighbors=find_neighbors(sys), step_n=0; n_threads=Threads.nthreads())

Calculate the accelerations of all atoms in a system using the pairwise, specific and general interactions and Newton's second law of motion.

source
Molly.add_position_restraintsMethod
add_position_restraints(sys, k; atom_selector=is_any_atom, restrain_coords=sys.coords)

Return a copy of a System with HarmonicPositionRestraints added to restrain the atoms.

The force constant k can be a single value or an array of equal length to the number of atoms in the system. The atom_selector function takes in each atom and atom data and determines whether to restrain that atom. For example, is_heavy_atom means non-hydrogen atoms are restrained.

source
Molly.apply_coupling!Method
apply_coupling!(system, coupling, simulator, neighbors=nothing,
+                step_n=0; n_threads=Threads.nthreads())

Apply a coupler to modify a simulation.

Returns whether the coupling has invalidated the currently stored forces, for example by changing the coordinates. This information is useful for some simulators. If coupling is a tuple or named tuple then each coupler will be applied in turn. Custom couplers should implement this function.

source
Molly.apply_loggers!Function
apply_loggers!(system, neighbors=nothing, step_n=0, run_loggers=true;
+               n_threads=Threads.nthreads(), kwargs...)

Run the loggers associated with a system.

run_loggers can be true, false or :skipzero, in which case the loggers are not run before the first step. Additional keyword arguments can be passed to the loggers if required. Ignored for gradient calculation during automatic differentiation.

source
Molly.apply_position_constraints!Method
apply_position_constraints!(sys, coord_storage; n_threads::Integer=Threads.nthreads())
 apply_position_constraints!(sys, coord_storage, vel_storage, dt;
-                            n_threads::Integer=Threads.nthreads())

Applies the system constraints to the coordinates.

If vel_storage and dt are provided then velocity corrections are applied as well.

source
Molly.bond_angleMethod
bond_angle(coord_i, coord_j, coord_k, boundary)
-bond_angle(vec_ji, vec_jk)

Calculate the bond or pseudo-bond angle in radians between three coordinates or two vectors.

The angle between j→i and j→k is returned in the range 0 to π.

source
Molly.born_radii_and_gradMethod
born_radii_and_grad(inter, coords, boundary)

Calculate Born radii, gradients of Born radii and surface area overlap with respect to atomic distance.

Custom GBSA methods should implement this function.

source
Molly.box_centerMethod
box_center(boundary)

Calculate the center of a bounding box.

Dimensions with infinite length return zero.

source
Molly.box_volumeMethod
box_volume(boundary)

Calculate the volume of a 3D bounding box or the area of a 2D bounding box.

source
Molly.displacementsMethod
displacements(coords, boundary)

Calculate the pairwise vector displacements of a set of coordinates, accounting for the periodic boundary conditions.

source
Molly.distancesMethod
distances(coords, boundary)

Calculate the pairwise distances of a set of coordinates, accounting for the periodic boundary conditions.

source
Molly.find_neighborsMethod
find_neighbors(system; n_threads=Threads.nthreads())
+                            n_threads::Integer=Threads.nthreads())

Applies the system constraints to the coordinates.

If vel_storage and dt are provided then velocity corrections are applied as well.

source
Molly.bond_angleMethod
bond_angle(coord_i, coord_j, coord_k, boundary)
+bond_angle(vec_ji, vec_jk)

Calculate the bond or pseudo-bond angle in radians between three coordinates or two vectors.

The angle between j→i and j→k is returned in the range 0 to π.

source
Molly.born_radii_and_gradMethod
born_radii_and_grad(inter, coords, boundary)

Calculate Born radii, gradients of Born radii and surface area overlap with respect to atomic distance.

Custom GBSA methods should implement this function.

source
Molly.box_centerMethod
box_center(boundary)

Calculate the center of a bounding box.

Dimensions with infinite length return zero.

source
Molly.dipole_momentMethod
dipole_moment(sys)

The dipole moment μ of a system.

Requires the charges on the atoms to be set.

source
Molly.displacementsMethod
displacements(coords, boundary)

Calculate the pairwise vector displacements of a set of coordinates, accounting for the periodic boundary conditions.

source
Molly.distancesMethod
distances(coords, boundary)

Calculate the pairwise distances of a set of coordinates, accounting for the periodic boundary conditions.

source
Molly.find_neighborsMethod
find_neighbors(system; n_threads=Threads.nthreads())
 find_neighbors(system, neighbor_finder, current_neighbors=nothing, step_n=0,
-               force_recompute=false; n_threads=Threads.nthreads())

Obtain a list of close atoms in a System.

Custom neighbor finders should implement this function.

source
Molly.forceMethod
force(inter::PairwiseInteraction, vec_ij, coord_i, coord_j,
-      atom_i, atom_j, boundary)
-force(inter::PairwiseInteraction, vec_ij, coord_i, coord_j,
-      atom_i, atom_j, boundary, special)
-force(inter::SpecificInteraction, coord_i, coord_j,
-      boundary)
-force(inter::SpecificInteraction, coord_i, coord_j,
-      coord_k, boundary)
-force(inter::SpecificInteraction, coord_i, coord_j,
-      coord_k, coord_l, boundary)

Calculate the force between atoms due to a given interaction type.

For PairwiseInteractions returns a single force vector and for SpecificInteractions returns a type such as SpecificForce2Atoms. Custom pairwise and specific interaction types should implement this function.

source
Molly.forcesMethod
forces(system, neighbors=find_neighbors(sys); n_threads=Threads.nthreads())

Calculate the forces on all atoms in a system using the pairwise, specific and general interactions.

source
Molly.hydrodynamic_radiusMethod
hydrodynamic_radius(coords, boundary)

Calculate the hydrodynamic radius of a set of coordinates.

$R_{hyd}$ is defined by

\[\frac{1}{R_{hyd}} = \frac{1}{2N^2}\sum_{i \neq j} \frac{1}{r_{ij}}\]

source
Molly.inject_gradientsMethod
inject_gradients(sys, params_dic)

Add parameters from a dictionary to a System.

Allows gradients for individual parameters to be tracked. Returns atoms, pairwise interactions, specific interaction lists and general interactions.

source
Molly.log_property!Function
log_property!(logger, system, neighbors=nothing, step_n=0; n_threads=Threads.nthreads(), kwargs...)

Log a property of a system throughout a simulation.

Custom loggers should implement this function. Additional keyword arguments can be passed to the logger if required.

source
Molly.massMethod
mass(atom)

The mass of an Atom.

Custom atom types should implement this function unless they have a mass field defined, which the function accesses by default.

source
Molly.maxwell_boltzmannFunction
maxwell_boltzmann(atom_mass::Unitful.Mass, temp::Unitful.Temperature,
+               force_recompute=false; n_threads=Threads.nthreads())

Obtain a list of close atoms in a System.

Custom neighbor finders should implement this function.

source
Molly.forceFunction
force(inter, vec_ij, atom_i, atom_j, force_units, special, coord_i, coord_j,
+      boundary, velocity_i, velocity_j, step_n)
+force(inter, coord_i, boundary, atom_i, force_units, velocity_i, step_n)
+force(inter, coord_i, coord_j, boundary, atom_i, atom_j, force_units, velocity_i,
+      velocity_j, step_n)
+force(inter, coord_i, coord_j, coord_k, boundary, atom_i, atom_j, atom_k,
+      force_units, velocity_i, velocity_j, velocity_k, step_n)
+force(inter, coord_i, coord_j, coord_k, coord_l, boundary, atom_i, atom_j, atom_k,
+      atom_l, force_units, velocity_i, velocity_j, velocity_k, velocity_l, step_n)

Calculate the force between atoms due to a given interaction type.

For pairwise interactions returns a single force vector and for specific interactions returns a type such as SpecificForce2Atoms. Custom pairwise and specific interaction types should implement this function.

source
Molly.forcesMethod
forces(system, neighbors=find_neighbors(sys), step_n=0; n_threads=Threads.nthreads())

Calculate the forces on all atoms in a system using the pairwise, specific and general interactions.

source
Molly.hydrodynamic_radiusMethod
hydrodynamic_radius(coords, boundary)

Calculate the hydrodynamic radius of a set of coordinates.

$R_{hyd}$ is defined by

\[\frac{1}{R_{hyd}} = \frac{1}{2N^2}\sum_{i \neq j} \frac{1}{r_{ij}}\]

source
Molly.inject_gradientsMethod
inject_gradients(sys, params_dic)

Add parameters from a dictionary to a System.

Allows gradients for individual parameters to be tracked. Returns atoms, pairwise interactions, specific interaction lists and general interactions.

source
Molly.log_property!Function
log_property!(logger, system, neighbors=nothing, step_n=0;
+              n_threads=Threads.nthreads(), kwargs...)

Log a property of a system throughout a simulation.

Custom loggers should implement this function. Additional keyword arguments can be passed to the logger if required.

source
Molly.massMethod
mass(atom)

The mass of an Atom.

Custom atom types should implement this function unless they have a mass field defined, which the function accesses by default.

source
Molly.maxwell_boltzmannFunction
maxwell_boltzmann(atom_mass::Unitful.Mass, temp::Unitful.Temperature,
                   k::BoltzmannConstUnits=Unitful.k; rng=Random.GLOBAL_RNG)
 maxwell_boltzmann(atom_mass::MolarMass, temp::Unitful.Temperature,
                   k_molar::MolarBoltzmannConstUnits=(Unitful.k * Unitful.Na);
                   rng=Random.GLOBAL_RNG)
 maxwell_boltzmann(atom_mass::Real, temperature::Real,
-                  k::Real=ustrip(u"u * nm^2 * ps^-2 * K^-1", Unitful.k); rng=Random.GLOBAL_RNG)

Generate a random velocity along one dimension from the Maxwell-Boltzmann distribution, with optional custom Boltzmann constant.

source
Molly.molecule_centersMethod
molecule_centers(coords, boundary, topology)

Calculate the coordinates of the center of each molecule in a system.

Accounts for periodic boundary conditions by using the circular mean. If topology=nothing then the coordinates are returned.

Not currently compatible with TriclinicBoundary if the topology is set. Not currently compatible with automatic differentiation using Zygote.

source
Molly.place_atomsMethod
place_atoms(n_atoms, boundary; min_dist=nothing, max_attempts=100)

Generate random coordinates.

Obtain n_atoms coordinates in bounding box boundary where no two points are closer than min_dist, accounting for periodic boundary conditions. The keyword argument max_attempts determines the number of failed tries after which to stop placing atoms. Can not be used if one or more dimensions has infinite boundaries.

source
Molly.place_diatomicsMethod
place_diatomics(n_molecules, boundary, bond_length; min_dist=nothing,
-                max_attempts=100, aligned=false)

Generate random diatomic molecule coordinates.

Obtain coordinates for n_molecules diatomics in bounding box boundary where no two points are closer than min_dist and the bond length is bond_length, accounting for periodic boundary conditions. The keyword argument max_attempts determines the number of failed tries after which to stop placing atoms. The keyword argument aligned determines whether the bonds all point the same direction (true) or random directions (false). Can not be used if one or more dimensions has infinite boundaries.

source
Molly.potential_energyMethod
potential_energy(system, neighbors=find_neighbors(sys); n_threads=Threads.nthreads())

Calculate the potential energy of a system using the pairwise, specific and general interactions.

potential_energy(inter::PairwiseInteraction, vec_ij, coord_i, coord_j,
-                 atom_i, atom_j, boundary)
-potential_energy(inter::SpecificInteraction, coords_i, coords_j,
-                 boundary)
-potential_energy(inter::SpecificInteraction, coords_i, coords_j,
-                 coords_k, boundary)
-potential_energy(inter::SpecificInteraction, coords_i, coords_j,
-                 coords_k, coords_l, boundary)

Calculate the potential energy due to a given interaction type.

Custom interaction types should implement this function.

source
Molly.pressureMethod
pressure(sys, neighbors=find_neighbors(sys); n_threads=Threads.nthreads())

Calculate the pressure of a system.

The pressure is defined as

\[P = \frac{1}{V} \left( NkT - \frac{2}{D} \Xi \right)\]

where V is the system volume, N is the number of atoms, k is the Boltzmann constant, T is the system temperature, D is the number of dimensions and Ξ is the virial calculated using virial.

This should only be used on systems containing just pairwise interactions, or where the specific interactions, constraints and general interactions without virial defined do not contribute to the virial. Not compatible with infinite boundaries. Not currently compatible with automatic differentiation using Zygote when using pairwise interactions.

source
Molly.radius_gyrationMethod
radius_gyration(coords, atoms)

Calculate the radius of gyration of a set of coordinates.

Assumes the coordinates do not cross the bounding box, i.e. all coordinates correspond to the same periodic image.

source
Molly.random_coordMethod
random_coord(boundary)

Generate a random coordinate uniformly distributed within a bounding box.

source
Molly.random_normal_translation!Method
random_normal_translation!(sys::System; shift_size=oneunit(eltype(eltype(sys.coords))))

Performs a random translation of the coordinates of a randomly selected atom in a System.

The translation is generated using a uniformly chosen direction and length selected from the standard normal distribution i.e. with mean 0 and standard deviation 1, scaled by shift_size which should have appropriate length units.

source
Molly.random_uniform_translation!Method
random_uniform_translation!(sys::System; shift_size=oneunit(eltype(eltype(sys.coords))))

Performs a random translation of the coordinates of a randomly selected atom in a System.

The translation is generated using a uniformly selected direction and uniformly selected length in range [0, 1) scaled by shift_size which should have appropriate length units.

source
Molly.random_velocityMethod
random_velocity(atom_mass::Union{Unitful.Mass, MolarMass}, temp::Unitful.Temperature;
+                  k::Real=ustrip(u"u * nm^2 * ps^-2 * K^-1", Unitful.k); rng=Random.GLOBAL_RNG)

Generate a random velocity along one dimension from the Maxwell-Boltzmann distribution, with optional custom Boltzmann constant.

source
Molly.molecule_centersMethod
molecule_centers(coords, boundary, topology)

Calculate the coordinates of the center of each molecule in a system.

Accounts for periodic boundary conditions by using the circular mean. If topology=nothing then the coordinates are returned.

Not currently compatible with TriclinicBoundary if the topology is set.

source
Molly.place_atomsMethod
place_atoms(n_atoms, boundary; min_dist=nothing, max_attempts=100, rng=Random.GLOBAL_RNG)

Generate random coordinates.

Obtain n_atoms coordinates in bounding box boundary where no two points are closer than min_dist, accounting for periodic boundary conditions. The keyword argument max_attempts determines the number of failed tries after which to stop placing atoms. Can not be used if one or more dimensions has infinite boundaries.

source
Molly.place_diatomicsMethod
place_diatomics(n_molecules, boundary, bond_length; min_dist=nothing,
+                max_attempts=100, aligned=false, rng=Random.GLOBAL_RNG)

Generate random diatomic molecule coordinates.

Obtain coordinates for n_molecules diatomics in bounding box boundary where no two points are closer than min_dist and the bond length is bond_length, accounting for periodic boundary conditions. The keyword argument max_attempts determines the number of failed tries after which to stop placing atoms. The keyword argument aligned determines whether the bonds all point the same direction (true) or random directions (false). Can not be used if one or more dimensions has infinite boundaries.

source
Molly.potential_energyMethod
potential_energy(system, neighbors=find_neighbors(sys), step_n=0; n_threads=Threads.nthreads())

Calculate the potential energy of a system using the pairwise, specific and general interactions.

potential_energy(inter, vec_ij, atom_i, atom_j, energy_units, special, coord_i, coord_j,
+                 boundary, velocity_i, velocity_j, step_n)
+potential_energy(inter, coord_i, boundary, atom_i, energy_units, velocity_i, step_n)
+potential_energy(inter, coord_i, coord_j, boundary, atom_i, atom_j, energy_units,
+                 velocity_i, velocity_j, step_n)
+potential_energy(inter, coord_i, coord_j, coord_k, boundary, atom_i, atom_j, atom_k,
+                 energy_units, velocity_i, velocity_j, velocity_k, step_n)
+potential_energy(inter, coord_i, coord_j, coord_k, coord_l, boundary, atom_i, atom_j,
+                 atom_k, atom_l, energy_units, velocity_i, velocity_j, velocity_k,
+                 velocity_l, step_n)

Calculate the potential energy due to a given interaction type.

Custom interaction types should implement this function.

source
Molly.pressureMethod
pressure(sys, neighbors=find_neighbors(sys), step_n=0; n_threads=Threads.nthreads())

Calculate the pressure of a system.

The pressure is defined as

\[P = \frac{1}{V} \left( NkT - \frac{2}{D} \Xi \right)\]

where V is the system volume, N is the number of atoms, k is the Boltzmann constant, T is the system temperature, D is the number of dimensions and Ξ is the virial calculated using virial.

This should only be used on systems containing just pairwise interactions, or where the specific interactions, constraints and general interactions without virial defined do not contribute to the virial. Not compatible with infinite boundaries.

source
Molly.radius_gyrationMethod
radius_gyration(coords, atoms)

Calculate the radius of gyration of a set of coordinates.

Assumes the coordinates do not cross the bounding box, i.e. all coordinates correspond to the same periodic image.

source
Molly.random_coordMethod
random_coord(boundary; rng=Random.GLOBAL_RNG)

Generate a random coordinate uniformly distributed within a bounding box.

source
Molly.random_normal_translation!Method
random_normal_translation!(sys::System; shift_size=oneunit(eltype(eltype(sys.coords))))

Performs a random translation of the coordinates of a randomly selected atom in a System.

The translation is generated using a uniformly chosen direction and length selected from the standard normal distribution i.e. with mean 0 and standard deviation 1, scaled by shift_size which should have appropriate length units.

source
Molly.random_uniform_translation!Method
random_uniform_translation!(sys::System; shift_size=oneunit(eltype(eltype(sys.coords))))

Performs a random translation of the coordinates of a randomly selected atom in a System.

The translation is generated using a uniformly selected direction and uniformly selected length in range [0, 1) scaled by shift_size which should have appropriate length units.

source
Molly.random_velocities!Method
random_velocities!(sys, temp)
+random_velocities!(vels, sys, temp)

Set the velocities of a System, or a vector, to random velocities generated from the Maxwell-Boltzmann distribution.

source
Molly.random_velocityMethod
random_velocity(atom_mass::Union{Unitful.Mass, MolarMass}, temp::Unitful.Temperature;
                 dims=3, rng=Random.GLOBAL_RNG)
 random_velocity(atom_mass::Union{Unitful.Mass, MolarMass}, temp::Unitful.Temperature,
                 k::Union{BoltzmannConstUnits, MolarBoltzmannConstUnits};
                 dims=3, rng=Random.GLOBAL_RNG)
 random_velocity(atom_mass::Real, temp::Real, k::Real=ustrip(u"u * nm^2 * ps^-2 * K^-1", Unitful.k);
-                dims=3, rng=Random.GLOBAL_RNG)

Generate a random velocity from the Maxwell-Boltzmann distribution, with optional custom Boltzmann constant.

source
Molly.rdfMethod
rdf(coords, boundary; npoints=200)

Calculate the radial distribution function of a set of coordinates.

This describes how density varies as a function of distance from each atom. Returns a list of distance bin centers and a list of the corresponding densities.

source
Molly.remd_exchange!Method
remd_exchange!(sys, sim, n, m; rng=Random.GLOBAL_RNG, n_threads=Threads.nthreads())

Attempt an exchange of replicas n and m in a ReplicaSystem during a REMD simulation.

Successful exchanges should exchange coordinates and velocities as appropriate. Returns acceptance quantity Δ and a Bool indicating whether the exchange was successful.

source
Molly.rmsdMethod
rmsd(coords_1, coords_2)

Calculate the root-mean-square deviation (RMSD) of two sets of 3D coordinates after superimposition by the Kabsch algorithm.

Assumes the coordinates do not cross the bounding box, i.e. all coordinates in each set correspond to the same periodic image.

source
Molly.run_loggers!Function
run_loggers!(system, neighbors=nothing, step_n=0, run_loggers=true;
-             n_threads=Threads.nthreads(), kwargs...)

Run the loggers associated with a system.

run_loggers can be true, false or :skipzero, in which case the loggers are not run before the first step. Additional keyword arguments can be passed to the loggers if required. Ignored for gradient calculation during automatic differentiation.

source
Molly.scale_boundaryMethod
scale_boundary(boundary, scale_factor)

Scale the sides of a bounding box by a scaling factor.

The scaling factor can be a single number or a SVector of the appropriate number of dimensions corresponding to the scaling factor for each axis. For a 3D bounding box the volume scales as the cube of the scaling factor.

source
Molly.scale_coords!Method
scale_coords!(sys, scale_factor; ignore_molecules=false)

Scale the coordinates and bounding box of a system by a scaling factor.

The scaling factor can be a single number or a SVector of the appropriate number of dimensions corresponding to the scaling factor for each axis. Velocities are not scaled. If the topology of the system is set then atoms in the same molecule will be moved by the same amount according to the center of coordinates of the molecule. This can be disabled with ignore_molecules=true.

Not currently compatible with TriclinicBoundary if the topology is set. Not currently compatible with automatic differentiation using Zygote.

source
Molly.simulate!Method
simulate!(system, simulator, n_steps; n_threads=Threads.nthreads(), run_loggers=true)
-simulate!(system, simulator; n_threads=Threads.nthreads(), run_loggers=true)

Run a simulation on a system according to the rules of the given simulator.

run_loggers can be true, false or :skipzero, in which case the loggers are not run before the first step. run_loggers is true by default except for SteepestDescentMinimizer, where it is false. Custom simulators should implement this function.

source
Molly.simulate_remd!Method
simulate_remd!(sys, remd_sim, n_steps; rng=Random.GLOBAL_RNG,
-               n_threads=Threads.nthreads(), run_loggers=true)

Run a REMD simulation on a ReplicaSystem using a REMD simulator.

source
Molly.temperatureMethod
temperature(system)

Calculate the temperature of a system from the kinetic energy of the atoms.

source
Molly.torsion_angleMethod
torsion_angle(coord_i, coord_j, coord_k, coord_l, boundary)
-torsion_angle(vec_ij, vec_jk, vec_kl)

Calculate the torsion angle in radians defined by four coordinates or three vectors.

The angle between the planes defined by atoms (i, j, k) and (j, k, l) is returned in the range -π to π.

source
Molly.use_neighborsMethod
use_neighbors(inter)

Whether a pairwise interaction uses the neighbor list, default false.

Custom pairwise interactions can define a method for this function. For built-in interactions such as LennardJones this function accesses the use_neighbors field of the struct.

source
Molly.ustrip_vecMethod
ustrip_vec(x)
-ustrip_vec(u, x)

Broadcasted form of ustrip from Unitful.jl, allowing e.g. ustrip_vec.(coords).

source
Molly.vectorMethod
vector(c1, c2, boundary)

Displacement between two coordinate values from c1 to c2, accounting for periodic boundary conditions.

The minimum image convention is used, so the displacement is to the closest version of the coordinates accounting for the periodic boundaries. For the TriclinicBoundary an approximation is used to find the closest version by default.

source
Molly.vector_1DMethod
vector_1D(c1, c2, side_length)

Displacement between two 1D coordinate values from c1 to c2, accounting for periodic boundary conditions in a CubicBoundary or RectangularBoundary.

The minimum image convention is used, so the displacement is to the closest version of the coordinate accounting for the periodic boundaries.

source
Molly.virialMethod
virial(sys, neighbors=find_neighbors(sys); n_threads=Threads.nthreads())
-virial(inter, sys, neighbors; n_threads=Threads.nthreads())

Calculate the virial of a system or the virial resulting from a general interaction.

The virial is defined as

\[\Xi = -\frac{1}{2} \sum_{i,j>i} r_{ij} \cdot F_{ij}\]

Custom general interaction types can implement this function.

This should only be used on systems containing just pairwise interactions, or where the specific interactions, constraints and general interactions without virial defined do not contribute to the virial. Not currently compatible with automatic differentiation using Zygote when using pairwise interactions.

source
Molly.visualizeFunction
visualize(coord_logger, boundary, out_filepath; <keyword arguments>)

Visualize a simulation as an animation.

This function is only available when GLMakie is imported. It can take a while to run, depending on the length of the simulation and the number of atoms.

Arguments

  • connections=Tuple{Int, Int}[]: pairs of atoms indices to link with bonds.
  • connection_frames: the frames in which bonds are shown. Should be a list of the same length as the number of frames, where each item is a list of Bools of the same length as connections. Defaults to always true.
  • trails::Integer=0: the number of preceding frames to show as transparent trails.
  • framerate::Integer=30: the frame rate of the animation.
  • color=:purple: the color of the atoms. Can be a single color or a list of colors of the same length as the number of atoms.
  • connection_color=:orange: the color of the bonds. Can be a single color or a list of colors of the same length as connections.
  • markersize=0.05: the size of the atom markers, in the units of the data.
  • linewidth=2.0: the width of the bond lines.
  • transparency=true: whether transparency is active on the plot.
  • show_boundary::Bool=true: whether to show the bounding box as lines.
  • boundary_linewidth=2.0: the width of the boundary lines.
  • boundary_color=:black: the color of the boundary lines.
  • kwargs...: other keyword arguments are passed to the point plotting function.
source
Molly.wrap_coord_1DMethod
wrap_coord_1D(c, side_length)

Ensure a 1D coordinate is within the bounding box and return the coordinate.

source
Molly.wrap_coordsMethod
wrap_coords(c, boundary)

Ensure a coordinate is within the bounding box and return the coordinate.

source
+ dims=3, rng=Random.GLOBAL_RNG)

Generate a random velocity from the Maxwell-Boltzmann distribution, with optional custom Boltzmann constant.

source
Molly.rdfFunction
rdf(coords, boundary; npoints=200)

Calculate the radial distribution function of a set of coordinates.

This function is only available when KernelDensity is imported. This describes how density varies as a function of distance from each atom. Returns a list of distance bin centers and a list of the corresponding densities.

source
Molly.remd_exchange!Method
remd_exchange!(sys, sim, n, m; rng=Random.GLOBAL_RNG, n_threads=Threads.nthreads())

Attempt an exchange of replicas n and m in a ReplicaSystem during a REMD simulation.

Successful exchanges should exchange coordinates and velocities as appropriate. Returns acceptance quantity Δ and a Bool indicating whether the exchange was successful.

source
Molly.rmsdMethod
rmsd(coords_1, coords_2)

Calculate the root-mean-square deviation (RMSD) of two sets of 3D coordinates after superimposition by the Kabsch algorithm.

Assumes the coordinates do not cross the bounding box, i.e. all coordinates in each set correspond to the same periodic image.

source
Molly.scale_boundaryMethod
scale_boundary(boundary, scale_factor)

Scale the sides of a bounding box by a scaling factor.

The scaling factor can be a single number or a SVector of the appropriate number of dimensions corresponding to the scaling factor for each axis. For a 3D bounding box the volume scales as the cube of the scaling factor.

source
Molly.scale_coords!Method
scale_coords!(sys, scale_factor; ignore_molecules=false)

Scale the coordinates and bounding box of a system by a scaling factor.

The scaling factor can be a single number or a SVector of the appropriate number of dimensions corresponding to the scaling factor for each axis. Velocities are not scaled. If the topology of the system is set then atoms in the same molecule will be moved by the same amount according to the center of coordinates of the molecule. This can be disabled with ignore_molecules=true.

Not currently compatible with TriclinicBoundary if the topology is set.

source
Molly.simulate!Method
simulate!(system, simulator, n_steps; n_threads=Threads.nthreads(), run_loggers=true)
+simulate!(system, simulator; n_threads=Threads.nthreads(), run_loggers=true)

Run a simulation on a system according to the rules of the given simulator.

run_loggers can be true, false or :skipzero, in which case the loggers are not run before the first step. run_loggers is true by default except for SteepestDescentMinimizer, where it is false. Custom simulators should implement this function.

source
Molly.simulate_remd!Method
simulate_remd!(sys, remd_sim, n_steps; rng=Random.GLOBAL_RNG,
+               n_threads=Threads.nthreads(), run_loggers=true)

Run a REMD simulation on a ReplicaSystem using a REMD simulator.

Not currently compatible with interactions that depend on step number.

source
Molly.temperatureMethod
temperature(system)

Calculate the temperature of a system from the kinetic energy of the atoms.

source
Molly.torsion_angleMethod
torsion_angle(coord_i, coord_j, coord_k, coord_l, boundary)
+torsion_angle(vec_ij, vec_jk, vec_kl)

Calculate the torsion angle in radians defined by four coordinates or three vectors.

The angle between the planes defined by atoms (i, j, k) and (j, k, l) is returned in the range -π to π.

source
Molly.use_neighborsMethod
use_neighbors(inter)

Whether a pairwise interaction uses the neighbor list, default false.

Custom pairwise interactions can define a method for this function. For built-in interactions such as LennardJones this function accesses the use_neighbors field of the struct.

source
Molly.ustrip_vecMethod
ustrip_vec(x)
+ustrip_vec(u, x)

Broadcasted form of ustrip from Unitful.jl, allowing e.g. ustrip_vec.(coords).

source
Molly.vectorMethod
vector(c1, c2, boundary)

Displacement between two coordinate values from c1 to c2, accounting for periodic boundary conditions.

The minimum image convention is used, so the displacement is to the closest version of the coordinates accounting for the periodic boundaries. For the TriclinicBoundary an approximation is used to find the closest version by default.

source
Molly.vector_1DMethod
vector_1D(c1, c2, side_length)

Displacement between two 1D coordinate values from c1 to c2, accounting for periodic boundary conditions in a CubicBoundary or RectangularBoundary.

The minimum image convention is used, so the displacement is to the closest version of the coordinate accounting for the periodic boundaries.

source
Molly.virialMethod
virial(sys, neighbors=find_neighbors(sys), step_n=0; n_threads=Threads.nthreads())
+virial(inter, sys, neighbors, step_n; n_threads=Threads.nthreads())

Calculate the virial of a system or the virial resulting from a general interaction.

The virial is defined as

\[\Xi = -\frac{1}{2} \sum_{i,j>i} r_{ij} \cdot F_{ij}\]

Custom general interaction types can implement this function.

This should only be used on systems containing just pairwise interactions, or where the specific interactions, constraints and general interactions without virial defined do not contribute to the virial.

source
Molly.visualizeFunction
visualize(coord_logger, boundary, out_filepath; <keyword arguments>)

Visualize a simulation as an animation.

This function is only available when GLMakie is imported. It can take a while to run, depending on the length of the simulation and the number of atoms.

Arguments

  • connections=Tuple{Int, Int}[]: pairs of atoms indices to link with bonds.
  • connection_frames: the frames in which bonds are shown. Should be a list of the same length as the number of frames, where each item is a list of Bools of the same length as connections. Defaults to always true.
  • trails::Integer=0: the number of preceding frames to show as transparent trails.
  • framerate::Integer=30: the frame rate of the animation.
  • color=:purple: the color of the atoms. Can be a single color or a list of colors of the same length as the number of atoms.
  • connection_color=:orange: the color of the bonds. Can be a single color or a list of colors of the same length as connections.
  • markersize=0.05: the size of the atom markers, in the units of the data.
  • linewidth=2.0: the width of the bond lines.
  • transparency=true: whether transparency is active on the plot.
  • show_boundary::Bool=true: whether to show the bounding box as lines.
  • boundary_linewidth=2.0: the width of the boundary lines.
  • boundary_color=:black: the color of the boundary lines.
  • kwargs...: other keyword arguments are passed to the point plotting function.
source
Molly.volumeMethod
volume(sys)
+volume(boundary)

Calculate the volume (3D) or area (2D) of a System or bounding box.

Returns infinite volume for infinite boundaries.

source
Molly.wrap_coord_1DMethod
wrap_coord_1D(c, side_length)

Ensure a 1D coordinate is within the bounding box and return the coordinate.

source
Molly.wrap_coordsMethod
wrap_coords(c, boundary)

Ensure a coordinate is within the bounding box and return the coordinate.

source
diff --git a/dev/development/index.html b/dev/development/index.html index 44aa66e0..1bf8298f 100644 --- a/dev/development/index.html +++ b/dev/development/index.html @@ -1,2 +1,2 @@ -Development · Molly.jl

Development documentation

Running tests

The tests will automatically include multithreading and/or GPU tests if multiple threads and/or a CUDA-enabled GPU are available. test/runtests.jl does not include all the tests, see the test directory for more, though these extra tests do not need to be run for every change. Various environmental variables can be set to modify the tests:

  • VISTESTS determines whether to run the GLMakie.jl plotting tests which will error on remote systems where a display is not available, default VISTESTS=1.
  • GPUTESTS determines whether to run the GPU tests, default GPUTESTS=1.
  • DEVICE determines which GPU to run the GPU tests on, default DEVICE=0.
  • GROUP can be used to run a subset of the tests, options All/Protein/Zygote/NotZygote, default GROUP=All.

The CI run does not carry out all tests - for example the GPU tests are not run - and this is reflected in the code coverage.

Benchmarks

The benchmark directory contains some benchmarks for the package.

+Development · Molly.jl

Development documentation

Running tests

The tests will automatically include multithreading and/or GPU tests if multiple threads and/or a CUDA-enabled GPU are available. test/runtests.jl does not include all the tests, see the test directory for more, though these extra tests do not need to be run for every change. Various environmental variables can be set to modify the tests:

  • VISTESTS determines whether to run the GLMakie.jl plotting tests which will error on remote systems where a display is not available, default VISTESTS=1.
  • GPUTESTS determines whether to run the GPU tests, default GPUTESTS=1.
  • DEVICE determines which GPU to run the GPU tests on, default DEVICE=0.
  • GROUP can be used to run a subset of the tests, options All/Protein/Gradients/NotGradients, default GROUP=All.

The CI run does not carry out all tests - for example the GPU tests are not run - and this is reflected in the code coverage.

Benchmarks

The benchmark directory contains some benchmarks for the package.

diff --git a/dev/differentiable/index.html b/dev/differentiable/index.html index a40b0950..33910ece 100644 --- a/dev/differentiable/index.html +++ b/dev/differentiable/index.html @@ -1,5 +1,5 @@ -Differentiable simulation · Molly.jl

Differentiable simulation with Molly

Note

There are still many rough edges when taking gradients through simulations. Please open an issue if you run into an error and remember the golden rule of AD: check your gradients against finite differencing if you want to make sure they are correct.

In the last few years, the deep learning revolution has broadened to include the paradigm of differentiable programming. The concept of using automatic differentiation (AD) to obtain exact gradients through physical simulations has many interesting applications, including parameterising force fields and training neural networks to describe atomic potentials.

There are some projects that explore differentiable molecular simulations - see Related software. However Julia provides a strong suite of AD tools, with Zygote.jl and Enzyme.jl allowing source-to-source transformations for much of the language. With Molly you can use the power of Zygote and Enzyme to obtain gradients through molecular simulations, even in the presence of complex interactions such as implicit solvation and stochasticity such as Langevin dynamics or the Andersen thermostat. Reverse mode AD can be used on the CPU with multithreading and on the GPU; performance is typically within an order of magnitude of the primal run. Forward mode AD can also be used on the CPU. Pairwise, specific and general interactions work, along with neighbor lists, and the same abstractions for running simulations are used as in the main package.

Differentiable simulation does not currently work with units and some components of the package. This is mentioned in the relevant docstrings. It is memory intensive on the GPU so using gradient checkpointing will likely be required for larger simulations.

Pairwise interactions

First, we show how taking gradients through a simulation can be used to optimise an atom property in a Lennard-Jones fluid. In this type of simulation each atom has a σ value that determines how close it likes to get to other atoms. We are going to find the σ value that results in a desired distance of each atom to its closest neighbor. First we need a function to obtain the mean distance of each atom to its closest neighbor:

using Molly
+Differentiable simulation · Molly.jl

Differentiable simulation with Molly

Note

There are still many rough edges when taking gradients through simulations. Please open an issue if you run into an error and remember the golden rule of AD: check your gradients against finite differencing if you want to make sure they are correct.

In the last few years, the deep learning revolution has broadened to include the paradigm of differentiable programming. The concept of using automatic differentiation (AD) to obtain exact gradients through physical simulations has many interesting applications, including parameterising force fields and training neural networks to describe atomic potentials.

There are some projects that explore differentiable molecular simulations - see Related software. However Julia provides a strong suite of AD tools, with Enzyme.jl allowing source-to-source transformations for much of the language. With Molly you can use the power of Enzyme to obtain gradients through molecular simulations, even in the presence of complex interactions such as implicit solvation and stochasticity such as Langevin dynamics or the Andersen thermostat. Reverse mode AD can be used on the CPU with multithreading and on the GPU; performance is typically within an order of magnitude of the primal run. Forward mode AD can also be used on the CPU. Pairwise, specific and general interactions work, along with neighbor lists, and the same abstractions for running simulations are used as in the main package.

Differentiable simulation does not currently work with units and some components of the package. This is mentioned in the relevant docstrings. It is memory intensive on the GPU so using gradient checkpointing will likely be required for larger simulations.

Pairwise interactions

First, we show how taking gradients through a simulation can be used to optimise an atom property in a Lennard-Jones fluid. In this type of simulation each atom has a σ value that determines how close it likes to get to other atoms. We are going to find the σ value that results in a desired distance of each atom to its closest neighbor. First we need a function to obtain the mean distance of each atom to its closest neighbor:

using Molly
 
 function mean_min_separation(final_coords, boundary)
     n_atoms = length(final_coords)
@@ -14,7 +14,7 @@
         sum_dists += min_dist
     end
     return sum_dists / n_atoms
-end

Now we can set up and run the simulation in a similar way to that described in the Molly documentation. The difference is that we wrap the simulation in a loss function. This returns a single value that we want to obtain gradients with respect to, in this case the difference between the value of the above function at the end of the simulation and a target distance. The Zygote.ignore() block allows us to ignore code for the purposes of obtaining gradients; you could add the visualize function there for example.

using Zygote
+end

Now we can set up and run the simulation in a similar way to that described in the Molly documentation. The difference is that we wrap the simulation in a loss function. This returns a single value that we want to obtain gradients with respect to, in this case the difference between the value of the above function at the end of the simulation and a target distance.

using Zygote
 using Format
 
 dist_true = 0.5
@@ -34,8 +34,6 @@
 lj = LennardJones(
     cutoff=DistanceCutoff(1.5),
     use_neighbors=true,
-    force_units=NoUnits,
-    energy_units=NoUnits,
 )
 pairwise_inters = (lj,)
 coords = place_atoms(n_atoms, boundary; min_dist=0.6)
@@ -46,8 +44,8 @@
 )
 
 function loss(σ, coords, velocities)
-    atoms = [Atom(0, 0.0, atom_mass, σ, 0.2, false) for i in 1:n_atoms]
-    loggers = (coords=CoordinateLogger(Float64, 10),)
+    atoms = [Atom(0, 0, atom_mass, 0.0, σ, 0.2) for i in 1:n_atoms]
+    loggers = (coords=CoordinatesLogger(Float64, 10),)
 
     sys = System(
         atoms=atoms,
@@ -66,10 +64,8 @@
     mms_end = mean_min_separation(Array(sys.coords), boundary)
     loss_val = abs(mms_end - dist_true)
 
-    Zygote.ignore() do
-        printfmt("σ {:6.3f}  |  Mean min sep expected {:6.3f}  |  Mean min sep end {:6.3f}  |  Loss {:6.3f}  |  ",
-                  σ, σ * (2 ^ (1 / 6)), mms_end, loss_val)
-    end
+    printfmt("σ {:6.3f}  |  Mean min sep expected {:6.3f}  |  Mean min sep end {:6.3f}  |  Loss {:6.3f}  |  ",
+             σ, σ * (2 ^ (1 / 6)), mms_end, loss_val)
 
     return loss_val
 end

We can obtain the gradient of loss with respect to the atom property σ.

grad = gradient(loss, σtrue, coords, velocities)[1]

This gradient can be used in a training loop to optimise σ, starting from an arbitrary value.

function train()
@@ -123,8 +119,8 @@
 )
 
 function loss(θ)
-    atoms = [Atom(0, 0.0, atom_mass, 0.0, 0.0, false) for i in 1:n_atoms]
-    loggers = (coords=CoordinateLogger(Float64, 2),)
+    atoms = [Atom(0, 0, atom_mass, 0.0, 0.0, 0.0) for i in 1:n_atoms]
+    loggers = (coords=CoordinatesLogger(Float64, 2),)
     specific_inter_lists = (
         InteractionList2Atoms(
             [1, 2, 4, 5],
@@ -141,9 +137,9 @@
 
     sys = System(
         atoms=atoms,
-        coords=deepcopy(coords),
+        coords=copy(coords),
         boundary=boundary,
-        velocities=deepcopy(velocities),
+        velocities=copy(velocities),
         specific_inter_lists=specific_inter_lists,
         loggers=loggers,
         force_units=NoUnits,
@@ -157,10 +153,8 @@
     dist_end = 0.5 * (d1 + d2)
     loss_val = abs(dist_end - dist_true)
 
-    Zygote.ignore() do
-        printfmt("θ {:5.1f}°  |  Final dist {:4.2f}  |  Loss {:5.3f}  |  ",
-                 rad2deg(θ), dist_end, loss_val)
-    end
+    printfmt("θ {:5.1f}°  |  Final dist {:4.2f}  |  Loss {:5.3f}  |  ",
+             rad2deg(θ), dist_end, loss_val)
 
     return loss_val
 end
@@ -197,7 +191,7 @@
 Epoch 18  |  θ  90.6°  |  Final dist 0.99  |  Loss 0.011  |  Grad -0.530
 Epoch 19  |  θ  93.7°  |  Final dist 1.02  |  Loss 0.017  |  Grad  0.520
 Epoch 20  |  θ  90.7°  |  Final dist 0.99  |  Loss 0.010  |  Grad -0.530

The final value we get is 90.7°, close to the theoretical value of 91.2° which can be calculated with trigonometry. The final simulation looks like this: Angle simulation In the presence of other forces this value would not be so trivially obtainable. We can record the gradients for different values of θ:

θs = collect(0:3:180)[2:end]
-grads = [gradient(loss, deg2rad(θ))[1] for θ in θs]

The plot of these shows that the gradient has the expected sign either side of the correct value: Angle gradient

Neural network potentials

Since gradients can be computed with Zygote, Flux models can also be incorporated into simulations. Here we show a neural network in the force function, though they can also be used in other parts of the simulation. This example also shows how gradients for multiple parameters can be obtained, in this case the parameters of the neural network. The jump from single to multiple parameters is important because single parameters can be optimised using finite differencing, whereas differentiable simulation is well-placed to optimise many parameters simultaneously.

We set up three pseudo-atoms and train a network to imitate the Julia logo by moving the bottom two atoms:

using Molly
+grads = [gradient(loss, deg2rad(θ))[1] for θ in θs]

The plot of these shows that the gradient has the expected sign either side of the correct value: Angle gradient

Neural network potentials

Flux models can also be incorporated into simulations. Here we show a neural network in the force function, though they can also be used in other parts of the simulation. This example also shows how gradients for multiple parameters can be obtained, in this case the parameters of the neural network. The jump from single to multiple parameters is important because single parameters can be optimised using finite differencing, whereas differentiable simulation is well-placed to optimise many parameters simultaneously.

We set up three pseudo-atoms and train a network to imitate the Julia logo by moving the bottom two atoms:

using Molly
 using GLMakie
 using Zygote
 using Flux
@@ -240,15 +234,15 @@
 )
 
 function loss()
-    atoms = [Atom(0, 0.0f0, mass, 0.0f0, 0.0f0, false) for i in 1:n_atoms]
-    loggers = (coords=CoordinateLogger(Float32, 10),)
+    atoms = [Atom(0, 0, mass, 0.0f0, 0.0f0, 0.0f0) for i in 1:n_atoms]
+    loggers = (coords=CoordinatesLogger(Float32, 10),)
     general_inters = (NNBonds(),)
 
     sys = System(
         atoms=atoms,
-        coords=deepcopy(coords),
+        coords=copy(coords),
         boundary=boundary,
-        velocities=deepcopy(velocities),
+        velocities=copy(velocities),
         general_inters=general_inters,
         loggers=loggers,
         force_units=NoUnits,
@@ -262,10 +256,8 @@
                 norm(vector(sys.coords[3], sys.coords[1], boundary))) / 3
     loss_val = abs(dist_end - dist_true)
 
-    Zygote.ignore() do
-        printfmt("Dist end {:6.3f}  |  Loss {:6.3f}\n", dist_end, loss_val)
-        visualize(sys.loggers.coords, boundary, "sim.mp4"; show_boundary=false)
-    end
+    printfmt("Dist end {:6.3f}  |  Loss {:6.3f}\n", dist_end, loss_val)
+    visualize(sys.loggers.coords, boundary, "sim.mp4"; show_boundary=false)
 
     return loss_val
 end

Before training the result looks like this: Logo before

function train()
@@ -297,4 +289,4 @@
 Epoch 17  |  Dist end  1.041  |  Loss  0.041
 Epoch 18  |  Dist end  1.030  |  Loss  0.030
 Epoch 19  |  Dist end  1.017  |  Loss  0.017
-Epoch 20  |  Dist end  1.003  |  Loss  0.003

After training it looks much better: Logo after You could replace the simple network here with a much more complicated model and it would theoretically be able to train, even if it might prove practically difficult (see discussion below).

Biomolecular force fields

Molly was used to train the GB99dms force field for implicit solvent molecular dynamics of proteins. This involved doing differentiable simulations of one million steps with a loss function based on the residue-residue distance match to explicit solvent simulations. The code is available.

Molecular loss functions

Ultimately, you need some objective function in order to calculate the gradient for each parameter. Here are some ideas for loss functions suitable for differentiable molecular simulations:

  • The distance between atoms at the end of the simulation compared to some reference state. This loss is used in the examples given here, is physically reasonable, and has obvious bounds.
  • The distance between atoms throughout the simulation.
  • The radial distribution function of atoms.
  • RMSD between atoms and a reference state - this would be suitable for macromolecules.
  • dRMSD, the distance between a distance map and a reference distance map.
  • The radius of gyration of a molecule.
  • The flexibility of a set of atoms over the simulation.
  • Supramolecular geometry, for example assembly of molecules into straight fibres.
  • The correlation of different velocities over the simulation.
  • The energy of the system.
  • The temperature of the system.
  • Some measure of phase change or a critical point.
  • A combination of the above, for example to obtain a force field relevant to both ordered and disordered proteins.

Some of these are currently not possible in Molly as the loggers are ignored for gradient purposes, but this will hopefully change in future.

Tips and tricks

  • The magnitude of gradients may be less important than the sign. Consider sampling gradients across different sources of stochasticity, such as starting velocities and conformations.
  • Exploding gradients prove a problem when using the velocity Verlet integrator in the NVE ensemble. This is why the velocity rescaling and Berendsen thermostats were used in the above examples. Langevin dynamics also seems to work. It is likely that the development of suitable simulation strategies and thermostats will be necessary to unlock the potential of differentiable simulation.
  • Do you really need a neural network to describe your potential? Think about learning a smaller number of physically-meaningful parameters before you put in a large neural network and expect it to learn. Whilst it is true that neural networks are universal function approximators, it does not follow that you will be able to train one by differentiating through a long simulation. A 1,000-step simulation with a 10-layer network at each step is analogous to training a 10,000 layer network (with shared weights).
  • Forward mode AD holds much promise for differentiable simulation, provided that the number of parameters is small, because the memory requirement is constant in the number of simulation steps. However, if the code runs slower than non-differentiable alternatives then the best approach is likely to use finite differencing with the simulation as a black box. Adjoint sensitivity is another approach to getting gradients which is not yet available in Molly.jl.
+Epoch 20 | Dist end 1.003 | Loss 0.003

After training it looks much better: Logo after You could replace the simple network here with a much more complicated model and it would theoretically be able to train, even if it might prove practically difficult (see discussion below).

Biomolecular force fields

Molly was used to train the GB99dms force field for implicit solvent molecular dynamics of proteins. This involved doing differentiable simulations of one million steps with a loss function based on the residue-residue distance match to explicit solvent simulations. The code is available.

Molecular loss functions

Ultimately, you need some objective function in order to calculate the gradient for each parameter. Here are some ideas for loss functions suitable for differentiable molecular simulations:

  • The distance between atoms at the end of the simulation compared to some reference state. This loss is used in the examples given here, is physically reasonable, and has obvious bounds.
  • The distance between atoms throughout the simulation.
  • The radial distribution function of atoms.
  • RMSD between atoms and a reference state - this would be suitable for macromolecules.
  • dRMSD, the distance between a distance map and a reference distance map.
  • The radius of gyration of a molecule.
  • The flexibility of a set of atoms over the simulation.
  • Supramolecular geometry, for example assembly of molecules into straight fibres.
  • The correlation of different velocities over the simulation.
  • The energy of the system.
  • The temperature of the system.
  • Some measure of phase change or a critical point.
  • A combination of the above, for example to obtain a force field relevant to both ordered and disordered proteins.

Some of these are currently not possible in Molly as the loggers are ignored for gradient purposes, but this will hopefully change in future.

Tips and tricks

  • The magnitude of gradients may be less important than the sign. Consider sampling gradients across different sources of stochasticity, such as starting velocities and conformations.
  • Exploding gradients prove a problem when using the velocity Verlet integrator in the NVE ensemble. This is why the velocity rescaling and Berendsen thermostats were used in the above examples. Langevin dynamics also seems to work. It is likely that the development of suitable simulation strategies and thermostats will be necessary to unlock the potential of differentiable simulation.
  • Do you really need a neural network to describe your potential? Think about learning a smaller number of physically-meaningful parameters before you put in a large neural network and expect it to learn. Whilst it is true that neural networks are universal function approximators, it does not follow that you will be able to train one by differentiating through a long simulation. A 1,000-step simulation with a 10-layer network at each step is analogous to training a 10,000 layer network (with shared weights).
  • Forward mode AD holds much promise for differentiable simulation, provided that the number of parameters is small, because the memory requirement is constant in the number of simulation steps. However, if the code runs slower than non-differentiable alternatives then the best approach is likely to use finite differencing with the simulation as a black box. Adjoint sensitivity is another approach to getting gradients which is not yet available in Molly.jl.
diff --git a/dev/documentation/index.html b/dev/documentation/index.html index 4a9e23e2..ee703009 100644 --- a/dev/documentation/index.html +++ b/dev/documentation/index.html @@ -18,7 +18,7 @@ pairwise_inters=pairwise_inters, loggers=( temp=TemperatureLogger(10), - coords=CoordinateLogger(10), + coords=CoordinatesLogger(10), ), ) @@ -35,6 +35,7 @@ accelerations(sys) masses(sys) +density(sys) # 207.56738339673083 kg m^-3 temperature(sys) # 96.76667184796673 K random_velocities(sys, 300.0u"K") @@ -55,6 +56,7 @@ # For certain systems virial(sys) pressure(sys) +dipole_moment(sys) # AtomsBase.jl interface import AtomsBase @@ -83,7 +85,7 @@ pairwise_inters=(LennardJones(),), loggers=( temp=TemperatureLogger(typeof(1.0f0u"K"), 10), - coords=CoordinateLogger(typeof(1.0f0u"nm"), 10), + coords=CoordinatesLogger(typeof(1.0f0u"nm"), 10), ), ) @@ -125,7 +127,7 @@ neighbor_finder=neighbor_finder, loggers=( temp=TemperatureLogger(10), - coords=CoordinateLogger(10), + coords=CoordinatesLogger(10), ), ) @@ -151,7 +153,7 @@ boundary=boundary, velocities=velocities, pairwise_inters=pairwise_inters, - loggers=(coords=CoordinateLogger(Float32, 10; dims=2),), + loggers=(coords=CoordinatesLogger(Float32, 10; dims=2),), force_units=NoUnits, energy_units=NoUnits, ) @@ -278,7 +280,7 @@ boundary=boundary, pairwise_inters=pairwise_inters, loggers=( - coords=CoordinateLogger(n_atoms, dims=n_dimensions(boundary)), + coords=CoordinatesLogger(n_atoms, dims=2), montecarlo=MonteCarloLogger(), ), ) @@ -297,15 +299,20 @@ println(sys.loggers.montecarlo.n_accept) # 15234 -visualize(sys.loggers.coords, boundary, "sim_montecarlo.gif")

Monte Carlo simulation

trial_moves should be a function that takes a System as its argument and optional keyword arguments trial_args. It should modify the coordinates as appropriate, accounting for any boundary conditions. random_uniform_translation! and random_normal_translation! are provided as common trial move functions. MonteCarloLogger records various properties throughout the simulation.

Units

Molly is fairly opinionated about using Unitful.jl units as shown above: you don't have to use them, but it is better if you do. Any consistent unit scheme can be used, or no units at all. Molly is most strict about the mixture of molar and non-molar types. For example, if your energy and force units are molar then your atom masses should be g/mol or similar. If you are not using units then no quantities can have Unitful annotations and you are responsible for ensuring a consistent unit system. Whilst you occasionally may run into friction with dimension mismatches, using units has the major advantages of catching whole classes of errors and letting you physically interpret the numbers in your system. The performance overhead of using units is minimal. Units are not currently compatible with differentiable simulations.

All your interaction types need to return the same units of force and energy or the simulation will not run. By default these are kJ * mol^-1 * nm^-1 for force and kJ * mol^-1 for energy, but this can be changed using the force_units and energy_units arguments to System and some interactions. These arguments should be NoUnits if you are not using units. If you need to strip units for downstream analysis, use the ustrip or ustrip_vec functions. It should be noted that charges are stored as dimensionless, i.e. 1.0 represents an atomic charge of +1.

Atom types

Molly has a built-in Atom type with a few properties commonly used in molecular simulation defined. The mass and charge functions can be used on an Atom. Custom atom types can be used just as effectively provided that either the mass function is defined on the type or the type has a mass field (the fallback for the mass function). The type should also have all fields required by any interactions. The list of atoms passed to the System constructor should be concretely typed.

Custom atom types should generally be bits types, i.e. isbitstype(MyAtom) should be true, to work on the GPU. Additional non-bits type data for the atoms that is not directly used when calculating the interactions can be passed to the System constructor with the atoms_data keyword argument. For example the built-in AtomData type contains fields such as the atom name that are useful when writing trajectories.

Forces and energies

Interactions define how different parts of the system interact. The force on each particle in the system is derived from the potential corresponding to the interaction.

\[\vec{F}_i = -\sum_j \frac{dV_{ij}(r_{ij})}{dr_{ij}}\frac{\vec{r}_{ij}}{r_{ij}}\]

In Molly there are three types of interactions:

The available pairwise interactions are:

The available specific interactions are:

The available general interactions are:

Pairwise interactions

To define your own PairwiseInteraction, first define the struct:

struct MyPairwiseInter <: PairwiseInteraction
+visualize(sys.loggers.coords, boundary, "sim_montecarlo.gif")

Monte Carlo simulation

trial_moves should be a function that takes a System as its argument and optional keyword arguments trial_args. It should modify the coordinates as appropriate, accounting for any boundary conditions. random_uniform_translation! and random_normal_translation! are provided as common trial move functions. MonteCarloLogger records various properties throughout the simulation.

Units

Molly is fairly opinionated about using Unitful.jl units as shown above: you don't have to use them, but it is better if you do. Any consistent unit scheme can be used, or no units at all. Molly is most strict about the mixture of molar and non-molar types. For example, if your energy and force units are molar then your atom masses should be g/mol or similar. If you are not using units then no quantities can have Unitful annotations and you are responsible for ensuring a consistent unit system. Whilst you occasionally may run into friction with dimension mismatches, using units has the major advantages of catching whole classes of errors and letting you physically interpret the numbers in your system. The performance overhead of using units is minimal. Units are not currently compatible with differentiable simulations.

All your interaction types need to return the same units of force and energy or the simulation will not run. By default these are kJ * mol^-1 * nm^-1 for force and kJ * mol^-1 for energy, but this can be changed using the force_units and energy_units arguments to System and some interactions. These arguments should be NoUnits if you are not using units. If you need to strip units for downstream analysis, use the ustrip or ustrip_vec functions. It should be noted that charges are stored as dimensionless, i.e. 1.0 represents an atomic charge of +1.

Atom types

Molly has a built-in Atom type with a few properties commonly used in molecular simulation defined. The mass and charge functions can be used on an Atom. Custom atom types can be used just as effectively provided that either the mass function is defined on the type or the type has a mass field (the fallback for the mass function). The type should also have all fields required by any interactions. The list of atoms passed to the System constructor should be concretely typed.

Custom atom types should generally be bits types, i.e. isbitstype(MyAtom) should be true, to work on the GPU. Additional non-bits type data for the atoms that is not directly used when calculating the interactions can be passed to the System constructor with the atoms_data keyword argument. For example the built-in AtomData type contains fields such as the atom name that are useful when writing trajectories.

Forces and energies

Interactions define how different parts of the system interact. The force on each particle in the system is derived from the potential corresponding to the interaction.

\[\vec{F}_i = -\sum_j \frac{dV_{ij}(r_{ij})}{dr_{ij}}\frac{\vec{r}_{ij}}{r_{ij}}\]

In Molly there are three types of interactions:

The available pairwise interactions are:

The available specific interactions are:

The available general interactions are:

Pairwise interactions

To define your own pairwise interaction, first define the struct:

struct MyPairwiseInter
     # Any properties, e.g. constants for the interaction or cutoff parameters
-end

You can also define a use_neighbors method, which determines whether the neighbor list is used to omit distant atoms (true) or whether all atom pairs are always considered (false):

Molly.use_neighbors(inter::MyPairwiseInter) = true

This is false by default. If it is true, you must specify a neighbor finder when setting up the System. For built-in interactions this function accesses the use_neighbors field of the struct. To work on the GPU the struct should be a bits type, i.e. isbitstype(MyPairwiseInter) should be true.

Next, you need to define a method for the force function acting between a pair of atoms. This has a set series of arguments:

function Molly.force(inter::MyPairwiseInter,
-                        vec_ij,
-                        coord_i,
-                        coord_j,
-                        atom_i,
-                        atom_j,
-                        boundary)
+end

You can also define a use_neighbors method, which determines whether the neighbor list is used to omit distant atoms (true) or whether all atom pairs are always considered (false):

Molly.use_neighbors(inter::MyPairwiseInter) = true

This is false by default. If it is true, you must specify a neighbor finder when setting up the System. For built-in interactions this function accesses the use_neighbors field of the struct. To work on the GPU the struct should be a bits type, i.e. isbitstype(MyPairwiseInter) should be true.

Next, you need to define a method for the force function acting between a pair of atoms. This has a set series of arguments:

function Molly.force(inter::MyPairwiseInter,
+                     vec_ij,
+                     atom_i,
+                     atom_j,
+                     force_units,
+                     special,
+                     coord_i,
+                     coord_j,
+                     boundary,
+                     velocity_i,
+                     velocity_j,
+                     step_n)
     # Replace this with your force calculation
     # A positive force causes the atoms to move apart
     f = 0.0
@@ -313,9 +320,35 @@
     # Obtain a vector for the force
     fdr = f * normalize(vec_ij)
     return fdr
-end

vec_ij is the vector between the closest images of atoms i and j accounting for the periodic boundary conditions. Atom properties can be accessed, e.g. atom_i.σ. Typically the force function is where most computation time is spent during the simulation, so consider optimising this function if you want high performance. One nice feature of Molly is that this function will work on both the CPU and the GPU.

An optional final argument special is a Bool determining whether the atom pair interaction should be treated as special. This is specified during neighbor finder construction. When simulating molecules, for example, non-bonded interactions for atoms in a 1-4 bonding arrangement (i-x-x-j) are often weighted by a factor such as 0.5. For interactions where this is relevant, special can be used to apply this weighting in the interaction. It can have a variety of uses depending on the context, for example if you have multiple interactions and want to exclude certain atom pairs from one of the interactions only.

To use your custom interaction in a simulation, add it to the list of pairwise interactions:

pairwise_inters = (MyPairwiseInter(),)

Then create a System and simulate as above. Note that you can also use a named tuple instead of a tuple if you want to access interactions by name:

pairwise_inters = (MyPairwiseInter=MyPairwiseInter(),)

For performance reasons it is best to avoid containers with abstract type parameters, such as Vector{PairwiseInteraction}.

If you wish to calculate potential energies or log the energy throughout a simulation, you will need to define a method for the potential_energy function. This has the same arguments as force and should return a single value corresponding to the potential energy.

Specific interactions

To define your own SpecificInteraction, first define the struct:

struct MySpecificInter <: SpecificInteraction
+end

Most of the arguments will generally not be used but are passed to allow maximum flexibility. You can use args... to indicate unused further arguments, e.g. Molly.force(inter::MyPairwiseInter, vec_ij, args...). vec_ij is the vector between the closest images of atoms i and j accounting for the periodic boundary conditions. Atom properties can be accessed, e.g. atom_i.σ. force_units can be useful for returning a zero force under certain conditions. step_n is the step number in the simulator, allowing time-dependent interactions. Beware that this step counter starts from 1 every time simulate! is called. It also doesn't work with simulate_remd!. Typically the force function is where most computation time is spent during the simulation, so consider optimising this function if you want high performance. One nice feature of Molly is that this function will work on both the CPU and the GPU.

The argument special is a Bool determining whether the atom pair interaction should be treated as special. This is specified during neighbor finder construction. When simulating molecules, for example, non-bonded interactions for atoms in a 1-4 bonding arrangement (i-x-x-j) are often weighted by a factor such as 0.5. For interactions where this is relevant, special can be used to apply this weighting in the interaction. It can have a variety of uses depending on the context, for example if you have multiple interactions and want to exclude certain atom pairs from one of the interactions only.

To use your custom interaction in a simulation, add it to the list of pairwise interactions:

pairwise_inters = (MyPairwiseInter(),)

Then create a System and simulate as above. Note that you can also use a named tuple instead of a tuple if you want to access interactions by name:

pairwise_inters = (MyPairwiseInter=MyPairwiseInter(),)

For performance reasons it is best to avoid containers with abstract type parameters, such as Vector{Any}.

If you wish to calculate potential energies or log the energy throughout a simulation, you will need to define a method for the potential_energy function. This has the same arguments as force, except the fifth argument is the energy units not the force units, and should return a single value corresponding to the potential energy:

function Molly.potential_energy(inter::MyPairwiseInter,
+                                vec_ij,
+                                atom_i,
+                                atom_j,
+                                energy_units,
+                                special,
+                                coord_i,
+                                coord_j,
+                                boundary,
+                                velocity_i,
+                                velocity_j,
+                                step_n)
+    # Example Lennard-Jones interaction
+    σ = (atom_i.σ + atom_j.σ) / 2
+    ϵ = sqrt(atom_i.ϵ * atom_j.ϵ)
+    r = norm(vec_ij)
+    E = 4ϵ * ((σ/r)^6 - (σ/r)^12)
+    return E
+end

Specific interactions

To define your own specific interaction, first define the struct:

struct MySpecificInter
     # Properties, e.g. a bond distance corresponding to the energy minimum
-end

Next, you need to define a method for the force function. The form of this will depend on whether the interaction involves 1, 2, 3 or 4 atoms. For example in the 2 atom case:

function Molly.force(inter::MySpecificInter, coords_i, coords_j, boundary)
+end

Next, you need to define a method for the force function. The form of this will depend on whether the interaction involves 1, 2, 3 or 4 atoms. For example in the 2 atom case:

function Molly.force(inter::MySpecificInter,
+                     coord_i,
+                     coord_j,
+                     boundary,
+                     atom_i,
+                     atom_j,
+                     force_units,
+                     velocity_i,
+                     velocity_j)
     dr = vector(coords_i, coords_j, boundary)
 
     # Replace this with your force calculation
@@ -324,22 +357,36 @@
 
     fdr = f * normalize(dr)
     return SpecificForce2Atoms(-fdr, fdr)
-end

The 3 atom case would define Molly.force(inter::MySpecificInter, coords_i, coords_j, coords_k, boundary) and return SpecificForce3Atoms(f1, f2, f3). To use your custom interaction, add it to the specific interaction lists along with the atom indices:

specific_inter_lists = (
+end

Again, most of these arguments are rarely used and can be replaced with args.... The 3 atom case would define Molly.force(inter::MySpecificInter, coord_i, coord_j, coord_k, boundary, atom_i, atom_j, atom_k, force_units, velocity_i, velocity_j, velocity_k) and return SpecificForce3Atoms(f1, f2, f3). To use your custom interaction, add it to the specific interaction lists along with the atom indices:

specific_inter_lists = (
     InteractionList2Atoms(
         [1, 3],
         [2, 4],
         [MySpecificInter(), MySpecificInter()],
     ),
-)

For 3 atom interactions use InteractionList3Atoms and pass 3 sets of indices. If using the GPU, the inner list of indices and interactions should be moved to the GPU with CuArray. The number in the interaction list and the return type from force must match, e.g. InteractionList3Atoms must always return SpecificForce3Atoms from the corresponding force function. If some atoms are required in the interaction for force calculation but have no force applied to them by the interaction, give a zero force vector for those atoms. Again a method for the potential_energy function with the same arguments can be defined.

General interactions

To define your own general interaction, first define the struct:

struct MyGeneralInter
+)

For 3 atom interactions use InteractionList3Atoms and pass 3 sets of indices. If using the GPU, the inner list of indices and interactions should be moved to the GPU with CuArray. The number in the interaction list and the return type from force must match, e.g. InteractionList3Atoms must always return SpecificForce3Atoms from the corresponding force function. If some atoms are required in the interaction for force calculation but have no force applied to them by the interaction, give a zero force vector for those atoms. Again a method for potential_energy with the same arguments, except the seventh argument is the energy units not the force units, can be defined:

function Molly.potential_energy(inter::MySpecificInter,
+                                coord_i,
+                                coord_j,
+                                boundary,
+                                atom_i,
+                                atom_j,
+                                energy_units,
+                                velocity_i,
+                                velocity_j)
+    # Example harmonic bond interaction
+    dr = vector(coord_i, coord_j, boundary)
+    r = norm(dr)
+    return (inter.k / 2) * (r - inter.r0) ^ 2
+end

General interactions

To define your own general interaction, first define the struct:

struct MyGeneralInter
     # Properties, e.g. a neural network model
-end

Next, you need to define a method for the AtomsCalculators.forces function (note this is different to the force function above).

import AtomsCalculators
+end

Next, you need to define a method for the AtomsCalculators.forces function (note this is different to the force function above).

import AtomsCalculators
 
 function AtomsCalculators.forces(sys,
                                  inter::MyGeneralInter;
                                  neighbors=nothing,
+                                 step_n=0,
                                  n_threads=Threads.nthreads(),
                                  kwargs...)
-    # kwargs... is required, neighbors and n_threads can be omitted if not used
+    # kwargs... is required, neighbors/step_n/n_threads can be omitted if not used
 
     # Calculate the forces on all atoms using the interaction and the system
     # The output should have the same shape as the coordinates
@@ -391,7 +438,7 @@
 )
 
 # Volume of bounding box
-box_volume(b) # 3.8993746318188633 nm^3
+volume(b) # 3.8993746318188633 nm^3
 
 # Random coordinate uniformly distributed within boundary
 random_coord(b) # SVector(2.651062310435411, 2.1702306804433973, 0.9518105403051831)u"nm"
@@ -409,7 +456,7 @@
 
     for step_n in 1:n_steps
         # Calculate accelerations like this
-        accels_t = accelerations(sys, neighbors; n_threads=n_threads)
+        accels_t = accelerations(sys, neighbors, step_n; n_threads=n_threads)
 
         # Ensure coordinates stay within the simulation box like this
         sys.coords = wrap_coords.(sys.coords, (sys.boundary,))
@@ -426,8 +473,8 @@
         remove_CM_motion!(sys)
 
         # Apply the loggers like this
-        # Computed quantities can also be given as keyword arguments to run_loggers!
-        run_loggers!(sys, neighbors, step_n, run_loggers; n_threads=n_threads)
+        # Computed quantities can also be given as keyword arguments to apply_loggers!
+        apply_loggers!(sys, neighbors, step_n, run_loggers; n_threads=n_threads)
 
         # Find new neighbors like this
         neighbors = find_neighbors(sys, sys.neighbor_finder, neighbors, step_n, recompute_forces;
@@ -480,7 +527,7 @@
     #   for example by changing the coordinates
     recompute_forces = false
     return recompute_forces
-end

The functions random_velocity, maxwell_boltzmann and temperature may be useful here. To use your custom coupler, give it as the coupling argument to the simulator as above.

Loggers

Loggers record properties of the simulation to allow monitoring and analysis. The available loggers are:

Many of the loggers can be initialised with just the number of steps between recorded values, e.g. CoordinateLogger(10). An optional first argument is the type of the recorded value; the above is equivalent to CoordinateLogger(typeof(1.0u"nm"), 10) but if the simulation did not use units then CoordinateLogger(Float64, 10) would be required. If the simulation is in 2D, giving dims=2 as a keyword argument is required for some loggers. A logger's history can be accessed with values(my_logger).

To define your own logger, first define the struct and a method for values to access the stored values:

struct MyLogger
+end

The functions random_velocity, maxwell_boltzmann and temperature may be useful here. To use your custom coupler, give it as the coupling argument to the simulator as above.

Loggers

Loggers record properties of the simulation to allow monitoring and analysis. The available loggers are:

Many of the loggers can be initialised with just the number of steps between recorded values, e.g. CoordinatesLogger(10). An optional first argument is the type of the recorded value; the above is equivalent to CoordinatesLogger(typeof(1.0u"nm"), 10) but if the simulation did not use units then CoordinatesLogger(Float64, 10) would be required. If the simulation is in 2D, giving dims=2 as a keyword argument is required for some loggers. A logger's history can be accessed with values(my_logger).

To define your own logger, first define the struct and a method for values to access the stored values:

struct MyLogger
     n_steps::Int
     history::Vector{Float64}
     # Any other properties
@@ -497,7 +544,7 @@
     end
 end

The use of n_steps is optional and is an example of how to record a property periodically throughout the simulation. To use your custom logger, add it to the named tuple of loggers given when creating the System:

loggers = (mylogger=MyLogger(10, []),) # Don't forget the trailing comma!

In addition to being run at the end of each step, loggers are run before the first step, i.e. at step 0. This means that a logger that records a value every step for a simulation with 100 steps will end up with 101 values. Running loggers before the first step can be disabled by giving run_loggers=:skipzero as a keyword argument to simulate!, which can be useful when splitting up simulations into multiple simulate! calls. For example, this runs the loggers 301 times:

simulate!(sys, simulator, 100) # Default run_loggers=true
 simulate!(sys, simulator, 100; run_loggers=:skipzero)
-simulate!(sys, simulator, 100; run_loggers=:skipzero)

Running loggers can be disabled entirely with run_loggers=false, which is the default for SteepestDescentMinimizer. Loggers are currently ignored for the purposes of taking gradients, so if a logger is used in the gradient calculation the gradients will appear to be nothing.

Many times, a logger will just record an observation to an Array containing a record of past observations. For this purpose, you can use the GeneralObservableLogger without defining a custom logging function. Define your observation function as

function my_observable(sys::System, neighbors; n_threads::Integer, kwargs...)
+simulate!(sys, simulator, 100; run_loggers=:skipzero)

Running loggers can be disabled entirely with run_loggers=false, which is the default for SteepestDescentMinimizer. Loggers are currently ignored for the purposes of taking gradients, so if a logger is used in the gradient calculation the gradients will appear to be nothing.

Many times, a logger will just record an observation to an Array containing a record of past observations. For this purpose, you can use the GeneralObservableLogger without defining a custom logging function. Define your observation function as

function my_observable(sys::System, neighbors, step_n; n_threads::Integer, kwargs...)
     # Probe the system for some desired property
     return observation
 end

Keyword arguments current_forces and current_potential_energy can also be used here to avoid recomputing values that are passed from the simulator:

function my_pe_observable(sys::System, neighbors; n_threads::Integer,
@@ -596,4 +643,4 @@
     else
         return current_neighbors
     end
-end

To use your custom neighbor finder, give it as the neighbor_finder argument when creating the System.

Analysis

Molly contains some tools for analysing the results of simulations. Functions that may be useful for analysis include:

Julia is a language well-suited to implementing all kinds of analysis for molecular simulations.

+end

To use your custom neighbor finder, give it as the neighbor_finder argument when creating the System.

Analysis

Molly contains some tools for analysing the results of simulations. Functions that may be useful for analysis include:

Julia is a language well-suited to implementing all kinds of analysis for molecular simulations.

diff --git a/dev/examples/index.html b/dev/examples/index.html index 29e7afc7..61037b86 100644 --- a/dev/examples/index.html +++ b/dev/examples/index.html @@ -91,7 +91,7 @@ boundary=boundary, velocities=velocities, pairwise_inters=(inter,), - loggers=(coords=CoordinateLogger(typeof(1.0u"km"), 10),), + loggers=(coords=CoordinatesLogger(typeof(1.0u"km"), 10),), force_units=u"kg * km * d^-2", energy_units=u"kg * km^2 * d^-2", ) @@ -125,8 +125,8 @@ ϵ::Float64 end -# Custom PairwiseInteraction -struct SIRInteraction <: PairwiseInteraction +# Custom pairwise interaction +struct SIRInteraction dist_infection::Float64 prob_infection::Float64 prob_recovery::Float64 @@ -135,11 +135,9 @@ # Custom force function function Molly.force(inter::SIRInteraction, vec_ij, - coord_i, - coord_j, atom_i, atom_j, - boundary) + args...) if (atom_i.status == infected && atom_j.status == susceptible) || (atom_i.status == susceptible && atom_j.status == infected) # Infect close people randomly @@ -156,11 +154,11 @@ atom_i.status = recovered end end - return zero(coord_i) + return zero(vec_ij) end # Custom logger -function fracs_SIR(s::System, neighbors=nothing; n_threads::Integer=Threads.nthreads()) +function fracs_SIR(s::System, args...; kwargs...) counts_sir = [ count(p -> p.status == susceptible, s.atoms), count(p -> p.status == infected , s.atoms), @@ -180,12 +178,7 @@ coords = place_atoms(n_people, boundary; min_dist=0.1) velocities = [random_velocity(1.0, temp; dims=2) for i in 1:n_people] -lj = LennardJones( - cutoff=DistanceCutoff(1.6), - use_neighbors=true, - force_units=NoUnits, - energy_units=NoUnits, -) +lj = LennardJones(cutoff=DistanceCutoff(1.6), use_neighbors=true) sir = SIRInteraction(0.5, 0.06, 0.01) pairwise_inters = (LennardJones=lj, SIR=sir) neighbor_finder = DistanceNeighborFinder( @@ -206,7 +199,7 @@ pairwise_inters=pairwise_inters, neighbor_finder=neighbor_finder, loggers=( - coords=CoordinateLogger(Float64, 10; dims=2), + coords=CoordinatesLogger(Float64, 10; dims=2), SIR=SIRLogger(10), ), force_units=NoUnits, @@ -321,7 +314,7 @@ pairwise_inters=(lj,), specific_inter_lists=(bonds, angles), neighbor_finder=neighbor_finder, - loggers=(coords=CoordinateLogger(200),), + loggers=(coords=CoordinatesLogger(200),), ) sim = Langevin(dt=0.002u"ps", temperature=300.0u"K", friction=1.0u"ps^-1") @@ -504,7 +497,7 @@ coords = [SVector(1/8, 1/8, 1/8), SVector(-1/8, -1/8, -1/8)] velocities = [randn(SVector{3, Float64}) * 0.1 for _ in 1:2] boundary = CubicBoundary(Inf) -loggers = (coords=CoordinateLogger(Float64, 1),) +loggers = (coords=CoordinatesLogger(Float64, 1),) sys = System( atoms=atoms, @@ -524,7 +517,7 @@ values(sys.loggers.coords)[end] # 2-element Vector{SVector{3, Float64}}: # [0.12060853912863925, 0.12292128337998731, 0.13100409788691614] -# [-0.13352575661477334, -0.11473039463130282, -0.13189544838731393]

Making and breaking bonds

There is an example of mutable atom properties in the main documentation, but what if you want to make and break bonds during the simulation? In this case you can use a PairwiseInteraction to make, break and apply the bonds. The partners of the atom can be stored in the atom type. We make a logger to record when the bonds are present, allowing us to visualize them with the connection_frames keyword argument to visualize (this can take a while to plot).

using Molly
+#  [-0.13352575661477334, -0.11473039463130282, -0.13189544838731393]

Making and breaking bonds

There is an example of mutable atom properties in the main documentation, but what if you want to make and break bonds during the simulation? In this case you can use a pairwise interaction to make, break and apply the bonds. The partners of the atom can be stored in the atom type. We make a logger to record when the bonds are present, allowing us to visualize them with the connection_frames keyword argument to visualize (this can take a while to plot).

using Molly
 using GLMakie
 using LinearAlgebra
 
@@ -536,7 +529,7 @@
     partners::Set{Int}
 end
 
-struct BondableInteraction <: PairwiseInteraction
+struct BondableInteraction
     prob_formation::Float64
     prob_break::Float64
     dist_formation::Float64
@@ -548,11 +541,9 @@
 
 function Molly.force(inter::BondableInteraction,
                         dr,
-                        coord_i,
-                        coord_j,
                         atom_i,
                         atom_j,
-                        boundary)
+                        args...)
     # Break bonds randomly
     if atom_j.i in atom_i.partners && rand() < inter.prob_break
         delete!(atom_i.partners, atom_j.i)
@@ -570,11 +561,11 @@
         fdr = -c * normalize(dr)
         return fdr
     else
-        return zero(coord_i)
+        return zero(dr)
     end
 end
 
-function bonds(sys::System, neighbors=nothing; n_threads::Integer=Threads.nthreads())
+function bonds(sys::System, args...; kwargs...)
     bonds = BitVector()
     for i in 1:length(sys)
         for j in 1:(i - 1)
@@ -595,12 +586,7 @@
 coords = place_atoms(n_atoms, boundary; min_dist=0.1)
 velocities = [random_velocity(1.0, temp; dims=2) for i in 1:n_atoms]
 pairwise_inters = (
-    SoftSphere(
-        cutoff=DistanceCutoff(2.0),
-        use_neighbors=true,
-        force_units=NoUnits,
-        energy_units=NoUnits,
-    ),
+    SoftSphere(cutoff=DistanceCutoff(2.0), use_neighbors=true),
     BondableInteraction(0.1, 0.1, 1.1, 2.0, 0.1),
 )
 neighbor_finder = DistanceNeighborFinder(
@@ -621,7 +607,7 @@
     pairwise_inters=pairwise_inters,
     neighbor_finder=neighbor_finder,
     loggers=(
-        coords=CoordinateLogger(Float64, 20; dims=2),
+        coords=CoordinatesLogger(Float64, 20; dims=2),
         bonds=BondLogger(20),
     ),
     force_units=NoUnits,
@@ -648,7 +634,7 @@
 using Zygote
 using GLMakie
 
-inter = LennardJones(force_units=NoUnits, energy_units=NoUnits)
+inter = LennardJones()
 boundary = CubicBoundary(5.0)
 a1, a2 = Atom(σ=0.3, ϵ=0.5), Atom(σ=0.3, ϵ=0.5)
 
@@ -656,7 +642,7 @@
     c1 = SVector(1.0, 1.0, 1.0)
     c2 = SVector(dist + 1.0, 1.0, 1.0)
     vec = vector(c1, c2, boundary)
-    F = force(inter, vec, c1, c2, a1, a2, boundary)
+    F = force(inter, vec, a1, a2, NoUnits)
     return F[1]
 end
 
@@ -665,7 +651,7 @@
         c1 = SVector(1.0, 1.0, 1.0)
         c2 = SVector(dist + 1.0, 1.0, 1.0)
         vec = vector(c1, c2, boundary)
-        potential_energy(inter, vec, c1, c2, a1, a2, boundary)
+        potential_energy(inter, vec, a1, a2, NoUnits)
     end
     return -grad[1]
 end
@@ -698,7 +684,7 @@
                     [0.0    , 0.0      , 1.7928950]]u"Å",
 )
 
-coul = Coulomb(coulomb_const=2.307e-21u"kJ*Å", force_units=u"kJ/Å", energy_units=u"kJ")
+coul = Coulomb(coulomb_const=2.307e-21u"kJ*Å")
 calc = MollyCalculator(pairwise_inters=(coul,), force_units=u"kJ/Å", energy_units=u"kJ")
 
 AtomsCalculators.potential_energy(ab_sys, calc)
9.112207692184968e-21 kJ
AtomsCalculators.forces(ab_sys, calc)
5-element Vector{SVector{3, Quantity{Float64, 𝐋 𝐌 𝐓^-2, Unitful.FreeUnits{(Å^-1, kJ), 𝐋 𝐌 𝐓^-2, nothing}}}}:
@@ -756,7 +742,7 @@
         c1 = SVector(1.0, 1.0, 1.0)
         c2 = SVector(dist + 1.0, 1.0, 1.0)
         vec = vector(c1, c2, boundary)
-        potential_energy(inter, vec, c1, c2, a1, a2, boundary)
+        potential_energy(inter, vec, a1, a2, NoUnits)
     end
 end
 
@@ -793,7 +779,7 @@
         c1 = SVector(1.0, 1.0, 1.0)
         c2 = SVector(dist + 1.0, 1.0, 1.0)
         vec = vector(c1, c2, boundary)
-        potential_energy(inter, vec, c1, c2, a1, a2, boundary)
+        potential_energy(inter, vec, a1, a2, NoUnits)
     end
 end
 
@@ -836,11 +822,7 @@
 sys = System(
     fcc_crystal;
     velocities=velocities,
-    pairwise_inters=(LennardJones(
-        cutoff=ShiftedForceCutoff(r_cut),
-        energy_units=u"kJ * mol^-1",
-        force_units=u"kJ * mol^-1 * nm^-1",
-    ),),
+    pairwise_inters=(LennardJones(cutoff=ShiftedForceCutoff(r_cut)),),
     loggers=(
         kinetic_eng=KineticEnergyLogger(100),
         pot_eng=PotentialEnergyLogger(100),
@@ -852,8 +834,9 @@
 updated_atoms = []
 
 for i in eachindex(sys)
-    push!(updated_atoms, Atom(index=sys.atoms[i].index, charge=sys.atoms[i].charge,
-                              mass=sys.atoms[i].mass, σ=σ, ϵ=ϵ, solute=sys.atoms[i].solute))
+    push!(updated_atoms, Atom(index=sys.atoms[i].index, atom_type=sys.atoms[i].atom_type,
+                              mass=sys.atoms[i].mass, charge=sys.atoms[i].charge,
+                              σ=σ, ϵ=ϵ))
 end
 
 sys = System(sys; atoms=[updated_atoms...])

Now the system can be simulated using any of the available simulators:

simulator = Langevin(
@@ -873,12 +856,7 @@
 max_coord = 200.0u"Å"
 coords = [max_coord .* rand(SVector{3}) for i in 1:n_atoms_half]
 boundary = CubicBoundary(200.0u"Å")
-lj = LennardJones(
-    cutoff=ShiftedPotentialCutoff(r_cut),
-    use_neighbors=true,
-    energy_units=u"kcal * mol^-1",
-    force_units=u"kcal * mol^-1 * Å^-1",
-)
+lj = LennardJones(cutoff=ShiftedPotentialCutoff(r_cut), use_neighbors=true)
 
 # Add bonded atoms
 bond_length = 0.74u"Å" # Hydrogen bond length
@@ -916,4 +894,4 @@
 
 # Check that the constraints are satisfied at the end of the simulation
 @test check_position_constraints(sys, shake)
-@test check_velocity_constraints(sys, shake)
+@test check_velocity_constraints(sys, shake) diff --git a/dev/exercises/index.html b/dev/exercises/index.html index f73ed08a..58701e49 100644 --- a/dev/exercises/index.html +++ b/dev/exercises/index.html @@ -1,2 +1,2 @@ -Exercises · Molly.jl

Molly exercises

A notebook with exercises covering various parts of Molly is available here, with answers available here.

Another notebook with exercises on computing transport coefficients with Molly is available here.

+Exercises · Molly.jl

Molly exercises

A notebook with exercises covering various parts of Molly is available here, with answers available here.

Another notebook with exercises on computing transport coefficients with Molly is available here.

diff --git a/dev/index.html b/dev/index.html index b2333730..02e9a47d 100644 --- a/dev/index.html +++ b/dev/index.html @@ -42,4 +42,4 @@ coupling=AndersenThermostat(temp, 1.0u"ps"), ) -simulate!(sys, simulator, 5_000)

The above 1 ps simulation looks something like this when you view it in VMD: MD simulation

Contributing

Contributions are very welcome including reporting bugs, fixing bugs, adding new features, improving performance, adding tests and improving documentation. Feel free to open an issue or use the channels below to discuss your contribution. New features will generally require tests to be added as well. See the roadmap issue for some discussion of recent progress and future plans.

Join the #juliamolsim channel on the Julia Slack, the #molly channel on the JuliaMolSim Zulip or post on the Julia Discourse to discuss the usage and development of Molly.jl.

Molly.jl follows the Contributor Covenant code of conduct.

Citation

If you use Molly, please cite the following paper (bib entry here):

A paper involving more contributors with further details on the software will be written at some point.

Interested?

There is the possibility of a postdoc position involving the development and use of Molly. Contact Joe Greener with informal enquiries.

+simulate!(sys, simulator, 5_000)

The above 1 ps simulation looks something like this when you view it in VMD: MD simulation

Contributing

Contributions are very welcome including reporting bugs, fixing bugs, adding new features, improving performance, adding tests and improving documentation. Feel free to open an issue or use the channels below to discuss your contribution. New features will generally require tests to be added as well. See the roadmap issue for some discussion of recent progress and future plans.

Join the #juliamolsim channel on the Julia Slack, the #molly channel on the JuliaMolSim Zulip or post on the Julia Discourse to discuss the usage and development of Molly.jl.

Molly.jl follows the Contributor Covenant code of conduct.

Citation

If you use Molly, please cite the following paper (bib entry here):

A paper involving more contributors with further details on the software will be written at some point.

Interested?

There is the possibility of a postdoc position involving the development and use of Molly. Contact Joe Greener with informal enquiries.

diff --git a/dev/objects.inv b/dev/objects.inv index 6b44394fa52b7d0ac9216d03e6eb9902d3bab5a9..e5f39008eb2f70d5612a2c244a4bcd8077b80fcd 100644 GIT binary patch delta 3702 zcmV-+4vF!e9n&3ea6Y;u|k;s&t({u#Q~o+cFmW=N((t5?s^O*_W6%{{I_ROF82 zY|o`&thj#-%k3W~cu#dWczlMY*WXPpS)Lyg_$5Yjydilz{+r(-F)HAeQdX?0O+a&J z043ohNeaMKl7F+hPPT_K?Zwa|ntLNBlX!^Zp$@N|o0hvIqrgK!)1;8VB*adSi&|XN z;ZAN2Wy#4lq;7_kG)V#-1&IXYWD10m=C}_|&jFDDoXlYI5Co{^5PPkiEr6k!>l4n8D>WD}nB8fP;#OW0)Afp``ah$vtgu6FF;OL!F)s zaT=2I34gdr&>@(=wi-7VMcI&Kj;ov{HRqf2>X)BDK>`J}D3f<39huKq@E2^iBN6^% zLCTso$Ifx!JZWwTtB!6-D-_hDAc-i~$FkHJ*1%B*-l?V*i|huY6jMc|C4b}opgz_ODcnc`3Q4HR^|84Hg_|zV zk`(8ubnR|j36209_27!#anSZ@4`?BYNv@DBZV@#X8ExoRZ@_=@o2t;LL5qj{d(RRK>hTmJW08e zZ|H{Rqpdd#evFC?)XBRV?}$Z&%RWAUzke7d4bH{Fen|MqS$T`g6dY5ek8k072d`)X z30#&0`+Zvnj5^@SoZ3uPX;1@O4_+zz!(g-@IeGO4AXO5av`MfD-sZ|YSsP}{SA_0v zHw=;{3YAAN2DRWwflsc7*?fm<h;*y0tbLa$Bve1Gc0n#V45pjQB9l4K40O61}hsqb(6fEjt zZO)NC)-_VyF-{>(IMy^WV|<#av}6V0ihP=u$Is}e{B=PfGG<80$pU%u)eK-yzFPEz z=pJ(Mb-_d=iy$|K9}~SjW))L~!hgEL89meqq-Cu9BqvyXK1K4^qWd-7@I6eMX;tCl zr{#N+f@k`)fZ@*o!&&-_?r*^ak$mZ5&^b4-*|NI=W=~$uPhLK6-z-LN41VBQaU@_R zAV+DjqyWU@bHOG2H9S z175TgIziWvRIDN6$sBt59gj6BZ6Hs*14aLFHW!1iI#U-{F6Nw5dDU<{tpUb`>r}T@ zx6BG=WW}1Nnm`Nk!ge=#*ngivn*X#_Zm9*IUMUN6Eyh8RZ?}Ua480l{N5|eznCnb#dl~dW)+9I83MI z4k}u=?sgI}^jD}!P<^eq%7k1RyoVrELnMl@PZM-(nyO5FgyC5*Lw}Zs_xt>~PE~dF zM3<9hfT!pR0(vHXYu4shPLuS>%NJuonuaG{^%L2x%ci(;rd)Ooo^XEJ65qy=f&+$n z0$H}+Gmssita&milNMbX-j({$_4N+;R_2>BR%L3g`C&s-)u#&Dy@m+07^wO*1Lt)< zyL#!iocjvzCE`6+uG!^;#DEy$czqN@rk2>IXw*D)Q8 z9?Vi5L4Q@r`_6rD(U&Cg#224^OmBH+72>WU1$g}R{Bm6|zM3ZytPXL8ep~M^K{UZT z5+AYJbkNt70>3DZS!cKkq=73^?0HuRDo;o56LQfG3ZwkTRvz8~3jKKugll)RL3N z?THgVRQQ8QRJ)jVg$_7YYFhpTrIl!-QBf-BY3{kDo!ln6r|8_$K0bG{VV_9Lde0+z z$66jN-uF2EKJmG}ztpZMH&=aX))LfQkJ;NzGo%z zI4vVmc#K+l)$2B03VBv>d2z-{&kc;x$#`^6c7z$zij|FATk|xgiQvYzwi85a{|@Je zPKO=MaLa@vTpot)*p@n$*CKP~t3=+MU7h)N2C`I|9S9Ex9!u#jp7Co0p?^%ZYYGJv z=Um;rjcJnjuRshK44o$&_!av-Y;h{3KHeBDne2Ip$t8!-mQ$~ara3M9jK<@2U>W|M z5&!mARHG#n%5VB)`;rtJFhUY8fPTCqRfb{xS-4fUYzWyyfwUksuL>_g9tFfX&jQ|) zDkq-VuSrE&M$_O$szQB9q<_j$vqv(2$d$YKXWLyTIpS=lT4&dil=&}l>25x}bQjT8 znH$GBMeVxt6@3@cGa7kGvFGB~y_FaX9eH)}P?L43(yN#Ty9IviHsm!1d1E6wdg@3q zf%Ec^=cJld?A~KQda@A?K}K2iB2-rCm0rxPm~oRW;>L@!$2Pcr%76RlZYI?CWZ~`A z0mu8Q?zbI7{%r{wS&%9OCM3vjtdzm93{+kus#RU7GypLjwiVjnvX^Gu1>}I_kZ9;UwyB_y)2(cA2Rchepc^_9c`_luQo!$IBYz$-!*phZOb>R z_$k4DalQs+FKb!$Juw?61Cjqi5_itd>|=g)nwSoayC@3tK;p?Wt% zvbFvhO>`s;5sHbf@-vI|?kK+oh%bRHXr5{Kc|9>N9eL08$$#idksb$^XEEgkgpc*U zVo>`uN6(dC9ZbLRtCaLmQV5pC|H?)z)Bisj39Z++1m8WlCpSd$sL%&_j@=%L{1>*s zHsk-1%;%7=lqB$(L)%j2X@e&o(=r>C2LUs+;aJVJy(wY%HCM46yl-K3YA$NVd64+# zDCUB&1~|@*`EP$HVcuf?!ms5c1fcna-4~N0TN8B^>4~G%wcVI-WlV!!m8BUR?~Zid zY#u0D!;LqwiPc~w-VXFbY-lRJ%Z)pw;9q@)PMypq#8UjP8K?dKNYj5A82hoGNlLE3 ULe5x|LOGu;r#6QF4ISCMn=?Qo+W-In delta 3649 zcmV-H4!-fz9iAPKfPdV$^?kmAL2IA|tXy}o*C3+v|EJ>01XJi5_z!i33Sv zXEw+mv_PK=^c(tX{e&LgBvK+N+M8|nB_2uV93GzgIif<#jC@3OlfBAVijrpty0ARlqD%Smlw>w8bo ziTVX#q)KVshf_B>p!zx4?Ke411#t`5y#EAU8dsBw05c>rq1CM?=t@6h*UcTSKvd+G zKVC}fO4cqN5CU{3pIJkU*rk8Ig=Pb_;3H%aIbG#wBe*U}PBk@$gEv2kjRoj5J zW&kDOBuNUuRezGRx=yb5W!j0ML$q}x7n691{uf(^8j-S327wM zApyY;oD43rWK$7&v0-JON1GgnLV$ua3KA%j87b470#_fx%1I_Owl6bW(NTpY%HnNL zHrUd>+**Ho3p7~lU=7%i5MiA(ESWAsI#lpd%2`r#wm2(c@dy+oP>@EMyesLzJBD&SW7{o>2oDQV*0edaaRKK^ zb3<5l@C#C*pbiB|l*#Gky9B2x$w}2E4gcx@j!f{$eBZF^>v5*1Tewa(xCZAfSVr=p zEKP%hNq;~g2{pMqG&f*R)8$E$;yjg++m9>3 z5rCr(T(LXM$sz0Ej9a(CY4$OO$L*r^>ENQa>l;|TgJ9eNOtFUY*#qb58+nWu8nutPUF`9!k zI8xw~%YL!m;#%dp*w4_JIU8Un6-my%f0$3V6wulChHIblCk4Od zUnfw;u=3Li(h>Qz`h1M!uSNT7y5Ukvn`u?y!>8qYl7c6HTEOGa05dHQpFg9!8!%ZU zZ=#>_R|SF0nIY9MjjVn2%}lJs|9>8Rvp9M_KYIRD-&qXqSp2}X;z+54;rhMj{0$FT8RwVfl8%rOB6BnbnFW z7rT$31D=ab2%;r=HCsVE=CR4Ui3;)|E7rV_NNEFEU$4!B`V5M=$66JV=YLNxls)(u z^AP0Bc}T*e7d=(B?7aa)${SWvTwa}=FZ}zsV6vR`L(HKPJ)?DVj(0i;z7{bspy4;u zA5PX+)79$i#1HibR|RmGPRlK{BVMo_#E2RKMTBx~(4k)OZkHd{sj4m?brg@0;poTc z0v99`zopW;fBbf3kv@9<%zxrRsL1wbajRR%QsnT5xrr=W?-=wpKyA%)p^VbzW`c}{ z&`oxG%PRX!8LKk2)_lLAsp_If4ckD3RV-9FnaU-!$$jypx3M zp)Rc@j-dcZ8(eOAkZM`>@;o5y(xbfx@yM}=46t2j8AAB?S3_q-)_)rX=aPC@*DicF z<2_}c0gL38mP=4ZBZ_1%k5@#jvV!zcFi7o@E|N&97( zvIz?$UpfgkG7+pX4^o=&*I~jg99ZCM=K?DR<fqkPgu(PAX;_e-EaDqNU_ja497<&7cuijdfZ zMm~zg);nB5#D+3C+I)SO0UK8J_>Y^KRJXkOm6{Et+Z{>iHBCkF^+L!fl<;fvhOyfa zL7B|jERlA-z@z9i<0awDjXe>hHzfU_YoUXQZIzA3$gDKHynl>Q>k3Q3i%HXDYR>Zk zEuoFH994*d_oZ;l%Yqx%lz^gOE_jJ)bw_LRwrk8{3sRjdq-&n>f!C)q5Z5vK^BzNY z)Eqvoj?sVq^^dz{@}2Sn1?dnZ7Uh4Hk;p%KdC{JUJ4dgd9(}tw`u6G3%ekr-Ts_uG zKNEjiqB?**Y&G+8arE^`^6cRpjI3o*TRoV)9vQv37P_C}^l19p*xZAJ)}zrEkSP+c z@a*btMGG=#m6$uh4#om^f$Nx_wF4Wb;01JMd@|R~Ykxwe8n3PK$;b4D*LP#wRiuD4 zv7Vo=3&wZzD1y~KPCwY269q^?cuV5boGwa3V+e?p;ziU9S0lyfiWECO2Y`0bQ+JKU zbPt6=?d6EJ=Y>KCj5M`ji#u3>uZbEJCJEEn>aC z)v@n6+kfY**!Z>b11(8IQ%5}Yz2zf3(Xg1c z@u_2gsH@{+bm|xwo_g7EFJzR;rxDXaqj(ngJDl)j@YJeO)X?Pt+H#?eDQd~Co8?YO z$ZJKnV(7Vkw&+!>Vejf;^~+P2gI%!L4&t0N6@S~YoYq~C@mkBV-6%tk?bI-sL>fyUGmdmp`wTzVo z4Syej#M{LX+!4j=HT108-QEh{4khTgZC-VAj#J0PVhO>p!xoWLz9e?h9=vwzl))emlW(_=Ji?T}x5_v#ONj;7vRe)p<} z&PB(~_m5vu?IuEiv{bKuXT6s=KhEZU*eQ7Opk*G!mVY+u zjJafhoHN>3$;jcbXQV7~m3_}j5{MwxzFg<>EaM}WXAhvDhw8WGV?tHo?^4$_VdKk0YYrPTTJU2#hYaE^!7a}Btpj-G>W&2mxIACFu^rtl z?@;C})StXQxi|@H2FgjA?Fr8%<9|smYCdcL@wfS7^`}Uj_pj{-Xd{JvJH8q*=2LZ! zGG?3;KJd_!!`SN*h~uR=kV7HzW}@HLh43KAMbyMza=b1c5Db^dyra8i`Mw}uv`I8c z{?aCK4=ze~;IyfvH(b}K<2wz;l<|G&VHICx*U{ljg9f74s-kI5%T5S^oPP#36?R?m zZ+G@H+FZv<*{*FmC&dP$G#PJYf4n7ChC!U!_=I)Y5VC_-cR^}COgIO56j03uHt?QQ zISH(ONh-=RnvR~hD%2M^YKmcx*5>y)n>YV#9P6<muywQQ z94{yKHCdOc>CnZFDd$-HTY|+Fq#6Sg66D9R%F$ZsTpz|=@qshj27d%DPe!;-X_(T# z#~}+pDN`r@6BeR?z9Q1xM$Qoz_~RYP^Yw=1&Cdlc%kWI4mr2J=w(YYX{4Od$VQfq^ z+o$}C#v2^y2d{I6n>BwHHKjZj$0~X^x}nZ$d}$Ko9q$y5|!77k}q#F!r*RLx)H9Jgz75FC=mA>O2Vz25(`R{^|nl6?x&>_=2_H z|Amf{@*cbkGbCG^->*cc?=eC#DP4ZAvECl!X9e*y6bD*h8Ga0e*azT(M@Gotc$=9A zmnSjp2c(bnu42$?HHW}t%LFq=TgpiHC52>J{J(s}vHWk*Nq=a)z9IPb-V@Wk*$e3n)b-k!@5q;_)4ALM9}dLAwG_nytzV0 zv}sy*Vk@HFPC9}wF79LeQ}HEh+{-QgW#!mgNPD`lZ1{g;aoqhEE#0?)v7h_dq~s19 TSFl6+wpDIu+=Ho diff --git a/dev/publications/index.html b/dev/publications/index.html index a2cb7f5c..47aef71d 100644 --- a/dev/publications/index.html +++ b/dev/publications/index.html @@ -1,2 +1,2 @@ -Publications · Molly.jl

Publications

If you use Molly, please cite the following paper (bib entry here):

  • Greener JG. Differentiable simulation to develop molecular dynamics force fields for disordered proteins, Chemical Science 15, 4897-4909 (2024)

A paper involving more contributors with further details on the software will be written at some point.

Other papers that use, contribute to or are compatible with Molly are listed below:

  • Martínez L. CellListMap.jl: Efficient and customizable cell list implementation for calculation of pairwise particle properties within a cutoff, Comput Phys Commun 279, 108452 (2022)
  • Blassel N and Stoltz G. Fixing the flux: A dual approach to computing transport coefficients, arXiv (2023)
  • Witt WC et al. ACEpotentials.jl: A Julia implementation of the atomic cluster expansion, J Chem Phys 159, 164101 (2023)
+Publications · Molly.jl

Publications

If you use Molly, please cite the following paper (bib entry here):

  • Greener JG. Differentiable simulation to develop molecular dynamics force fields for disordered proteins, Chemical Science 15, 4897-4909 (2024)

A paper involving more contributors with further details on the software will be written at some point.

Other papers that use, contribute to or are compatible with Molly are listed below:

  • Martínez L. CellListMap.jl: Efficient and customizable cell list implementation for calculation of pairwise particle properties within a cutoff, Comput Phys Commun 279, 108452 (2022)
  • Blassel N and Stoltz G. Fixing the flux: A dual approach to computing transport coefficients, arXiv (2023)
  • Witt WC et al. ACEpotentials.jl: A Julia implementation of the atomic cluster expansion, J Chem Phys 159, 164101 (2023)
  • Monmarché P, Spacek R and Stoltz G. Transient subtraction: A control variate method for computing transport coefficients, arXiv (2024)
diff --git a/dev/related/index.html b/dev/related/index.html index 3063c212..eaff2daa 100644 --- a/dev/related/index.html +++ b/dev/related/index.html @@ -1,2 +1,2 @@ -Related software · Molly.jl

Related software

There are many mature packages for molecular simulation. Of particular note here are OpenMM and GROMACS, both of which influenced the implementation of Molly. Molly can be thought of as similar to OpenMM in that it exposes simulation internals in a high-level language, though it is written in one language all the way down rather than using multiple device-specific kernels. It also aims to be differentiable and work just as well with non-molecular physical simulations, though how much this impacts the ability to reach high simulation speeds remains to be seen.

For differentiable simulations there are a number of related packages:

In Julia there are a number of packages related to atomic simulation, some of which are involved with the JuliaMolSim organisation:

+Related software · Molly.jl

Related software

There are many mature packages for molecular simulation. Of particular note here are OpenMM and GROMACS, both of which influenced the implementation of Molly. Molly can be thought of as similar to OpenMM in that it exposes simulation internals in a high-level language, though it is written in one language all the way down rather than using multiple device-specific kernels. It also aims to be differentiable and work just as well with non-molecular physical simulations, though how much this impacts the ability to reach high simulation speeds remains to be seen.

For differentiable simulations there are a number of related packages:

In Julia there are a number of packages related to atomic simulation, some of which are involved with the JuliaMolSim organisation:

diff --git a/dev/search_index.js b/dev/search_index.js index e10c5147..9269b1c2 100644 --- a/dev/search_index.js +++ b/dev/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"api/#Molly-API","page":"API","title":"Molly API","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"The API reference can be found here.","category":"page"},{"location":"api/","page":"API","title":"API","text":"Molly also re-exports StaticArrays.jl and Unitful.jl, making the likes of SVector and 1.0u\"nm\" available when you call using Molly.","category":"page"},{"location":"api/","page":"API","title":"API","text":"The visualize function is in a package extension and is only available once you have called using GLMakie. The ASECalculator code is in a package extension and is only available once you have called using PythonCall.","category":"page"},{"location":"api/","page":"API","title":"API","text":"Order = [:module, :type, :constant, :function, :macro]","category":"page"},{"location":"api/","page":"API","title":"API","text":"Modules = [Molly]\nPrivate = false\nOrder = [:module, :type, :constant, :function, :macro]","category":"page"},{"location":"api/#Molly.ASECalculator","page":"API","title":"Molly.ASECalculator","text":"ASECalculator(; )\n\nA Python ASE calculator.\n\nThis calculator is only available when PythonCall is imported. It is the user's responsibility to have the required Python packages installed. This includes ASE and any packages providing the calculator.\n\nContrary to the rest of Molly, unitless quantities are assumed to have ASE units: Å for length, eV for energy, u for mass, and Å sqrt(u/eV) for time. Unitful quantities will be converted as appropriate.\n\nNot currently compatible with TriclinicBoundary.\n\nArguments\n\nase_calc: the ASE calculator created with PythonCall.\natoms: the atoms, or atom equivalents, in the system.\ncoords: the coordinates of the atoms in the system. Typically a vector of SVectors of 2 or 3 dimensions.\nboundary: the bounding box in which the simulation takes place.\nelements=nothing: vector of atom elements as a string, either elements or atoms_data (which contains element data) must be provided.\natoms_data=nothing: other data associated with the atoms.\nvelocities=nothing: the velocities of the atoms in the system, only required if the velocities contribute to the potential energy or forces.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.AbstractGBSA","page":"API","title":"Molly.AbstractGBSA","text":"Generalized Born (GB) implicit solvent models augmented with the hydrophobic solvent accessible surface area (SA) term.\n\nCustom GBSA methods should sub-type this abstract type.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.AndersenThermostat","page":"API","title":"Molly.AndersenThermostat","text":"AndersenThermostat(temperature, coupling_const)\n\nThe Andersen thermostat for controlling temperature.\n\nThe velocity of each atom is randomly changed each time step with probability dt / coupling_const to a velocity drawn from the Maxwell-Boltzmann distribution. See Andersen 1980.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.Atom","page":"API","title":"Molly.Atom","text":"Atom(; )\n\nAn atom and its associated information.\n\nProperties unused in the simulation or in analysis can be left with their default values. The types used should be bits types if the GPU is going to be used.\n\nArguments\n\nindex::Int: the index of the atom in the system.\ncharge::C=0.0: the charge of the atom, used for electrostatic interactions.\nmass::M=1.0u\"g/mol\": the mass of the atom.\nσ::S=0.0u\"nm\": the Lennard-Jones finite distance at which the inter-particle potential is zero.\nϵ::E=0.0u\"kJ * mol^-1\": the Lennard-Jones depth of the potential well.\nsolute::Bool=false: whether the atom is part of the solute.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.AtomData","page":"API","title":"Molly.AtomData","text":"AtomData(atom_type, atom_name, res_number, res_name)\n\nData associated with an atom.\n\nStoring this separately allows the Atom types to be bits types and hence work on the GPU.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.AtomType","page":"API","title":"Molly.AtomType","text":"AtomType(type, class, element, charge, mass, σ, ϵ)\n\nAn atom type.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.AverageObservableLogger","page":"API","title":"Molly.AverageObservableLogger","text":"AverageObservableLogger(observable::Function, T::DataType, n_steps::Integer;\n n_blocks::Integer=1024)\n\nA logger that periodically records observations of a system and keeps a running empirical average.\n\nWhile GeneralObservableLogger holds a full record of observations, AverageObservableLogger does not. In addition, calling values(logger::AverageObservableLogger; std::Bool=true) returns two values: the current running average, and an estimate of the standard deviation for this average based on the block averaging method described in Flyvbjerg and Petersen 1989.\n\nArguments\n\nobservable::Function: the observable whose mean is recorded, must support the method observable(s::System, neighbors; n_threads::Integer).\nT::DataType: the type returned by observable.\nn_steps::Integer: number of simulation steps between observations.\nn_blocks::Integer=1024: the number of blocks used in the block averaging method, should be an even number.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.BerendsenThermostat","page":"API","title":"Molly.BerendsenThermostat","text":"BerendsenThermostat(temperature, coupling_const)\n\nThe Berendsen thermostat for controlling temperature.\n\nThe scaling factor for the velocities each step is\n\nlambda^2 = 1 + fracdelta ttau left( fracT_0T - 1 right)\n\nThis thermostat should be used with caution as it can lead to simulation artifacts.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.Buckingham","page":"API","title":"Molly.Buckingham","text":"Buckingham(; cutoff, use_neighbors, weight_special, force_units, energy_units)\n\nThe Buckingham interaction between two atoms.\n\nThe potential energy is defined as\n\nV(r_ij) = A_ij exp(-B_ij r_ij) - fracC_ijr_ij^6\n\nand the force on each atom by\n\nvecF_i = left( A_ij B_ij exp(-B_ij r_ij) - 6 fracC_ijr_ij^7 right) fracvecr_ijr_ij\n\nThe parameters are derived from the atom parameters according to\n\nbeginaligned\nA_ij = (A_ii A_jj)^12 \nB_ij = frac2frac1B_ii + frac1B_jj \nC_ij = (C_ii C_jj)^12\nendaligned\n\nso atoms that use this interaction should have fields A, B and C available.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.CellListMapNeighborFinder","page":"API","title":"Molly.CellListMapNeighborFinder","text":"CellListMapNeighborFinder(; eligible, dist_cutoff, special, n_steps, x0, unit_cell)\n\nFind close atoms by distance using a cell list algorithm from CellListMap.jl.\n\nx0 and unit_cell are optional initial coordinates and system unit cell that improve the first approximation of the cell list structure. Can not be used if one or more dimensions has infinite boundaries.\n\nExample\n\njulia> coords\n15954-element Vector{SVector{3, Quantity{Float64, 𝐋, Unitful.FreeUnits{(nm,), 𝐋, nothing}}}}:\n [2.5193063341012127 nm, 3.907448346081021 nm, 4.694954671434135 nm]\n [2.4173958848835233 nm, 3.916034913604175 nm, 4.699661024574953 nm]\n ⋮\n [1.818842280373283 nm, 5.592152965227421 nm, 4.992100424805031 nm]\n [1.7261366568663976 nm, 5.610326185704369 nm, 5.084523386833478 nm]\n\njulia> boundary\nCubicBoundary{Quantity{Float64, 𝐋, Unitful.FreeUnits{(nm,), 𝐋, nothing}}}(Quantity{Float64, 𝐋, Unitful.FreeUnits{(nm,), 𝐋, nothing}}[5.676 nm, 5.6627 nm, 6.2963 nm])\n\njulia> neighbor_finder = CellListMapNeighborFinder(\n eligible=s.neighbor_finder.eligible, dist_cutoff=1.2u\"nm\",\n special=s.neighbor_finder.special, n_steps=10,\n x0=coords, unit_cell=boundary,\n )\nCellListMapNeighborFinder{Quantity{Float64, 𝐋, Unitful.FreeUnits{(nm,), 𝐋, nothing}}, 3, Float64}\n Size of eligible matrix = (15954, 15954)\n n_steps = 10\n dist_cutoff = 1.2 nm\n\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.CosineAngle","page":"API","title":"Molly.CosineAngle","text":"CosineAngle(; k, θ0)\n\nA cosine bond angle between three atoms.\n\nθ0 is in radians. The potential energy is defined as\n\nV(theta) = k(1 + cos(theta - theta_0))\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.Coulomb","page":"API","title":"Molly.Coulomb","text":"Coulomb(; cutoff, use_neighbors, weight_special, coulomb_const, force_units, energy_units)\n\nThe Coulomb electrostatic interaction between two atoms.\n\nThe potential energy is defined as\n\nV(r_ij) = fracq_i q_j4 pi varepsilon_0 r_ij\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.CoulombReactionField","page":"API","title":"Molly.CoulombReactionField","text":"CoulombReactionField(; dist_cutoff, solvent_dielectric, use_neighbors, weight_special,\n coulomb_const, force_units, energy_units)\n\nThe Coulomb electrostatic interaction modified using the reaction field approximation between two atoms.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.CoulombSoftCore","page":"API","title":"Molly.CoulombSoftCore","text":"CoulombSoftCore(; cutoff, α, λ, p, use_neighbors, lorentz_mixing, weight_special,\n coulomb_const, force_units, energy_units)\n\nThe Coulomb electrostatic interaction between two atoms with a soft core.\n\nThe potential energy is defined as\n\nV(r_ij) = fracq_i q_j4 pi varepsilon_0 (r_ij^6 + alpha sigma_ij^6 lambda^p)^frac16\n\nIf alpha or lambda are zero this gives the standard Coulomb potential.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.CubicBoundary","page":"API","title":"Molly.CubicBoundary","text":"CubicBoundary(x, y, z)\nCubicBoundary(x)\n\nCubic 3D bounding box defined by three side lengths.\n\nIf one length is given then all three sides will have that length. Setting one or more values to Inf gives no boundary in that dimension.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.CubicSplineCutoff","page":"API","title":"Molly.CubicSplineCutoff","text":"CubicSplineCutoff(dist_activation, dist_cutoff)\n\nCutoff that interpolates the true potential and zero between an activation point and a cutoff point, using a cubic Hermite spline.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.DistanceConstraint","page":"API","title":"Molly.DistanceConstraint","text":"DistanceConstraint(i, j, dist)\n\nConstraint between two atoms that maintains a fixed distance between the two atoms.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.DistanceCutoff","page":"API","title":"Molly.DistanceCutoff","text":"DistanceCutoff(dist_cutoff)\n\nCutoff that sets the potential and force to be zero past a specified cutoff point.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.DistanceNeighborFinder","page":"API","title":"Molly.DistanceNeighborFinder","text":"DistanceNeighborFinder(; eligible, dist_cutoff, special, n_steps)\n\nFind close atoms by distance.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.FENEBond","page":"API","title":"Molly.FENEBond","text":"FENEBond(; k, r0, σ, ϵ)\n\nA finitely extensible non-linear elastic (FENE) bond between two atoms, see Kremer and Grest 1990.\n\nThe potential energy is defined as\n\nV(r) = -frac12 k r^2_0 ln left( 1 - left( fracrr_0 right) ^2 right) + V_textWCA(r)\n\nwhere the WCA contribution is given by\n\nV_textWCA(r) =\n begincases\n 4varepsilon left left( fracsigmar right) ^12 - left( fracsigmar right) ^6 right + varepsilon r 2^16sigma\n 0 r geq 2^16sigma\n endcases\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.GeneralObservableLogger","page":"API","title":"Molly.GeneralObservableLogger","text":"GeneralObservableLogger(observable::Function, T, n_steps)\n\nA logger which holds a record of regularly sampled observations of a system.\n\nobservable should return an object of type T and support the method observable(s::System, neighbors; n_threads::Integer)::T.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.Gravity","page":"API","title":"Molly.Gravity","text":"Gravity(; G, use_neighbors)\n\nThe gravitational interaction between two atoms.\n\nThe potential energy is defined as\n\nV(r_ij) = -fracG m_i m_jr_ij\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.HamiltonianREMD","page":"API","title":"Molly.HamiltonianREMD","text":"HamiltonianREMD(; )\n\nA simulator for a parallel Hamiltonian replica exchange MD (H-REMD) simulation on a ReplicaSystem.\n\nThe replicas are expected to have different Hamiltonians, i.e. different interactions. When calling simulate!, the assign_velocities keyword argument determines whether to assign random velocities at the appropriate temperature for each replica.\n\nNot currently compatible with automatic differentiation using Zygote.\n\nArguments\n\ndt::DT: the time step of the simulation.\ntemperature::T: the temperatures of the simulation.\nsimulators::ST: individual simulators for simulating each replica.\nexchange_time::ET: the time interval between replica exchange attempts.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.HarmonicAngle","page":"API","title":"Molly.HarmonicAngle","text":"HarmonicAngle(; k, θ0)\n\nA harmonic bond angle between three atoms.\n\nθ0 is in radians. The second atom is the middle atom. The potential energy is defined as\n\nV(theta) = frac12 k (theta - theta_0)^2\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.HarmonicBond","page":"API","title":"Molly.HarmonicBond","text":"HarmonicBond(; k, r0)\n\nA harmonic bond between two atoms.\n\nThe potential energy is defined as\n\nV(r) = frac12 k (r - r_0)^2\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.HarmonicPositionRestraint","page":"API","title":"Molly.HarmonicPositionRestraint","text":"HarmonicPositionRestraint(; k, x0)\n\nA harmonic position restraint on an atom to coordinate x0.\n\nThe potential energy is defined as\n\nV(boldsymbolx) = frac12 k boldsymbolx - boldsymbolx_0^2\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.ImplicitSolventGBN2","page":"API","title":"Molly.ImplicitSolventGBN2","text":"ImplicitSolventGBN2(atoms, atoms_data, bonds)\n\nGBn2 solvation model implemented as an AtomsCalculators.jl calculator.\n\nShould be used along with a Coulomb or CoulombReactionField interaction.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.ImplicitSolventOBC","page":"API","title":"Molly.ImplicitSolventOBC","text":"ImplicitSolventOBC(atoms, atoms_data, bonds)\n\nOnufriev-Bashford-Case GBSA model implemented as an AtomsCalculators.jl calculator.\n\nShould be used along with a Coulomb or CoulombReactionField interaction. The keyword argument use_OBC2 determines whether to use parameter set I (false, the default) or II (true).\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.InteractionList1Atoms","page":"API","title":"Molly.InteractionList1Atoms","text":"InteractionList1Atoms(is, inters)\nInteractionList1Atoms(is, inters, types)\nInteractionList1Atoms(inter_type)\n\nA list of specific interactions that involve one atom such as position restraints.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.InteractionList2Atoms","page":"API","title":"Molly.InteractionList2Atoms","text":"InteractionList2Atoms(is, js, inters)\nInteractionList2Atoms(is, js, inters, types)\nInteractionList2Atoms(inter_type)\n\nA list of specific interactions that involve two atoms such as bond potentials.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.InteractionList3Atoms","page":"API","title":"Molly.InteractionList3Atoms","text":"InteractionList3Atoms(is, js, ks, inters)\nInteractionList3Atoms(is, js, ks, inters, types)\nInteractionList3Atoms(inter_type)\n\nA list of specific interactions that involve three atoms such as bond angle potentials.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.InteractionList4Atoms","page":"API","title":"Molly.InteractionList4Atoms","text":"InteractionList4Atoms(is, js, ks, ls, inters)\nInteractionList4Atoms(is, js, ks, ls, inters, types)\nInteractionList4Atoms(inter_type)\n\nA list of specific interactions that involve four atoms such as torsion potentials.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.Langevin","page":"API","title":"Molly.Langevin","text":"Langevin(; )\n\nThe Langevin integrator, based on the Langevin Middle Integrator in OpenMM.\n\nThis is a leapfrog integrator, so the velocities are offset by half a time step behind the positions.\n\nArguments\n\ndt::S: the time step of the simulation.\ntemperature::K: the equilibrium temperature of the simulation.\nfriction::F: the friction coefficient of the simulation.\ncoupling::C=NoCoupling(): the coupling which applies during the simulation.\nremove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.LangevinSplitting","page":"API","title":"Molly.LangevinSplitting","text":"LangevinSplitting(; )\n\nThe Langevin simulator using a general splitting scheme.\n\nThis consists of a succession of A, B and O steps, corresponding respectively to updates in position, velocity for the potential part, and velocity for the thermal fluctuation-dissipation part. The Langevin and VelocityVerlet simulators without coupling correspond to the BAOA and BAB schemes respectively. For more information on the sampling properties of splitting schemes, see Fass et al. 2018.\n\nNot currently compatible with constraints, will print a warning and continue without applying constraints. Not currently compatible with automatic differentiation using Zygote.\n\nArguments\n\ndt::S: the time step of the simulation.\ntemperature::K: the equilibrium temperature of the simulation.\nfriction::F: the friction coefficient. If units are used, it should have a dimensionality of mass per time.\nsplitting::W: the splitting specifier. Should be a string consisting of the characters A, B and O. Strings with no Os reduce to deterministic symplectic schemes.\nremove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.LennardJones","page":"API","title":"Molly.LennardJones","text":"LennardJones(; cutoff, use_neighbors, lorentz_mixing, weight_special, weight_solute_solvent,\n force_units, energy_units, skip_shortcut)\n\nThe Lennard-Jones 6-12 interaction between two atoms.\n\nThe potential energy is defined as\n\nV(r_ij) = 4varepsilon_ij leftleft(fracsigma_ijr_ijright)^12 - left(fracsigma_ijr_ijright)^6right\n\nand the force on each atom by\n\nbeginaligned\nvecF_i = 24varepsilon_ij left(2fracsigma_ij^12r_ij^13 - fracsigma_ij^6r_ij^7right) fracvecr_ijr_ij \n= frac24varepsilon_ijr_ij^2 left2left(fracsigma_ij^6r_ij^6right)^2 -left(fracsigma_ijr_ijright)^6right vecr_ij\nendaligned\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.LennardJonesSoftCore","page":"API","title":"Molly.LennardJonesSoftCore","text":"LennardJonesSoftCore(; cutoff, α, λ, p, use_neighbors, lorentz_mixing, weight_special,\n weight_solute_solvent, force_units, energy_units, skip_shortcut)\n\nThe Lennard-Jones 6-12 interaction between two atoms with a soft core.\n\nThe potential energy is defined as\n\nV(r_ij) = 4varepsilon_ij leftleft(fracsigma_ijr_ij^textscright)^12 - left(fracsigma_ijr_ij^textscright)^6right\n\nand the force on each atom by\n\nvecF_i = 24varepsilon_ij left(2fracsigma_ij^12(r_ij^textsc)^13 - fracsigma_ij^6(r_ij^textsc)^7right) left(fracr_ijr_ij^textscright)^5 fracvecr_ijr_ij\n\nwhere\n\nr_ij^textsc = left(r_ij^6 + alpha sigma_ij^6 lambda^p right)^16\n\nIf alpha or lambda are zero this gives the standard LennardJones potential.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MetropolisMonteCarlo","page":"API","title":"Molly.MetropolisMonteCarlo","text":"MetropolisMonteCarlo(; )\n\nA Monte Carlo simulator that uses the Metropolis algorithm to sample the configuration space.\n\nArguments\n\ntemperature::T: the temperature of the system.\ntrial_moves::M: a function that performs the trial moves.\ntrial_args::Dict: a dictionary of arguments to be passed to the trial move function.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.Mie","page":"API","title":"Molly.Mie","text":"Mie(; m, n, cutoff, use_neighbors, lorentz_mixing, force_units, energy_units, skip_shortcut)\n\nThe Mie generalized interaction between two atoms.\n\nWhen m equals 6 and n equals 12 this is equivalent to the Lennard-Jones interaction. The potential energy is defined as\n\nV(r_ij) = C varepsilon_ij leftleft(fracsigma_ijr_ijright)^n - left(fracsigma_ijr_ijright)^mright\n\nwhere\n\nC = fracnn - m left( fracnm right) ^fracmn - m\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MolecularForceField","page":"API","title":"Molly.MolecularForceField","text":"MolecularForceField(ff_files...; units=true)\nMolecularForceField(T, ff_files...; units=true)\nMolecularForceField(atom_types, residue_types, bond_types, angle_types,\n torsion_types, torsion_order, weight_14_coulomb,\n weight_14_lj, attributes_from_residue)\n\nA molecular force field.\n\nRead one or more OpenMM force field XML files by passing them to the constructor.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MolecularTopology","page":"API","title":"Molly.MolecularTopology","text":"MolecularTopology(bond_is, bond_js, n_atoms)\nMolecularTopology(atom_molecule_inds, molecule_atom_counts)\n\nTopology information for a system.\n\nStores the index of the molecule each atom belongs to and the number of atoms in each molecule.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MollyCalculator","page":"API","title":"Molly.MollyCalculator","text":"MollyCalculator(; )\n\nA calculator for use with the AtomsCalculators.jl interface.\n\nneighbors can optionally be given as a keyword argument when calling the calculation functions to save on computation when the neighbors are the same for multiple calls. In a similar way, n_threads can be given to determine the number of threads to use when running the calculation function. Note that this calculator is designed for using Molly in other contexts; if you want to use another calculator in Molly it can be given as general_inters when creating a System.\n\nNot currently compatible with virial calculation. Not currently compatible with using atom properties such as σ and ϵ.\n\nArguments\n\npairwise_inters::PI=(): the pairwise interactions in the system, i.e. interactions between all or most atom pairs such as electrostatics. Typically a Tuple.\nspecific_inter_lists::SI=(): the specific interactions in the system, i.e. interactions between specific atoms such as bonds or angles. Typically a Tuple.\ngeneral_inters::GI=(): the general interactions in the system, i.e. interactions involving all atoms such as implicit solvent. Each should implement the AtomsCalculators.jl interface. Typically a Tuple.\nneighbor_finder::NF=NoNeighborFinder(): the neighbor finder used to find close atoms and save on computation.\nforce_units::F=u\"kJ * mol^-1 * nm^-1\": the units of force of the system. Should be set to NoUnits if units are not being used.\nenergy_units::E=u\"kJ * mol^-1\": the units of energy of the system. Should be set to NoUnits if units are not being used.\nk::K=Unitful.k or Unitful.k * Unitful.Na: the Boltzmann constant, which may be modified in some simulations. k is chosen based on the energy_units given.\ndims::Integer=3: the number of dimensions in the system.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MonteCarloAnisotropicBarostat","page":"API","title":"Molly.MonteCarloAnisotropicBarostat","text":"MonteCarloAnisotropicBarostat(pressure, temperature, boundary; n_steps=30,\n n_iterations=1, scale_factor=0.01, scale_increment=1.1,\n max_volume_frac=0.3, trial_find_neighbors=false)\n\nThe Monte Carlo anisotropic barostat for controlling pressure.\n\nFor 3D systems, pressure is a SVector of length 3 with components pressX, pressY, and pressZ representing the target pressure in each axis. For 2D systems, pressure is a SVector of length 2 with components pressX and pressY. To keep an axis fixed, set the corresponding pressure to nothing.\n\nSee Chow and Ferguson 1995, Åqvist et al. 2004 and the OpenMM source code. At regular intervals a Monte Carlo step is attempted by scaling the coordinates and the bounding box by a randomly chosen amount in a randomly selected axis. The step is accepted or rejected based on\n\nDelta W = Delta E + P Delta V - N k_B T ln left( fracV + Delta VV right)\n\nwhere ΔE is the change in potential energy, P is the equilibrium pressure along the selected axis, ΔV is the change in volume, N is the number of molecules in the system, T is the equilibrium temperature and V is the system volume. If ΔW ≤ 0 the step is always accepted, if ΔW > 0 the step is accepted with probability exp(-ΔW/kT).\n\nThe scale factor is modified over time to maintain an acceptance rate of around half. If the topology of the system is set then molecules are moved as a unit so properties such as bond lengths do not change.\n\nThe barostat assumes that the simulation is being run at a constant temperature but does not actively control the temperature. It should be used alongside a temperature coupling method such as the Langevin simulator or AndersenThermostat coupling. The neighbor list is not updated when making trial moves or after accepted moves. Note that the barostat can change the bounding box of the system.\n\nNot currently compatible with automatic differentiation using Zygote.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MonteCarloBarostat","page":"API","title":"Molly.MonteCarloBarostat","text":"MonteCarloBarostat(pressure, temperature, boundary; n_steps=30, n_iterations=1,\n scale_factor=0.01, scale_increment=1.1, max_volume_frac=0.3,\n trial_find_neighbors=false)\n\nThe Monte Carlo barostat for controlling pressure.\n\nSee Chow and Ferguson 1995, Åqvist et al. 2004 and the OpenMM source code. At regular intervals a Monte Carlo step is attempted by scaling the coordinates and the bounding box by a randomly chosen amount. The step is accepted or rejected based on\n\nDelta W = Delta E + P Delta V - N k_B T ln left( fracV + Delta VV right)\n\nwhere ΔE is the change in potential energy, P is the equilibrium pressure, ΔV is the change in volume, N is the number of molecules in the system, T is the equilibrium temperature and V is the system volume. If ΔW ≤ 0 the step is always accepted, if ΔW > 0 the step is accepted with probability exp(-ΔW/kT).\n\nThe scale factor is modified over time to maintain an acceptance rate of around half. If the topology of the system is set then molecules are moved as a unit so properties such as bond lengths do not change.\n\nThe barostat assumes that the simulation is being run at a constant temperature but does not actively control the temperature. It should be used alongside a temperature coupling method such as the Langevin simulator or AndersenThermostat coupling. The neighbor list is not updated when making trial moves or after accepted moves. Note that the barostat can change the bounding box of the system.\n\nNot currently compatible with automatic differentiation using Zygote.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MonteCarloLogger","page":"API","title":"Molly.MonteCarloLogger","text":"MonteCarloLogger()\nMonteCarloLogger(T)\n\nA logger that records acceptances in a Monte Carlo simulation.\n\nThe logged quantities include the number of new selections (n_select), the number of successful acceptances (n_accept), an array named energy_rates which stores the value of fracEk_B T i.e. the argument of the Boltzmann factor for the states, and a BitVector named state_changed that stores whether a new state was accepted for the logged step.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MonteCarloMembraneBarostat","page":"API","title":"Molly.MonteCarloMembraneBarostat","text":"MonteCarloMembraneBarostat(pressure, tension, temperature, boundary; n_steps=30,\n n_iterations=1, scale_factor=0.01, scale_increment=1.1,\n max_volume_frac=0.3, trial_find_neighbors=false,\n xy_isotropy=false, z_axis_fixed=false, constant_volume=false)\n\nThe Monte Carlo membrane barostat for controlling pressure.\n\nSet the xy_isotropy flag to true to scale the x and y axes isotropically. Set the z_axis_fixed flag to true to uncouple the z-axis and keep it fixed. Set the constant_volume flag to true to keep the system volume constant by scaling the z-axis accordingly. The z_axis_fixed and constant_volume flags cannot be true simultaneously.\n\nSee Chow and Ferguson 1995, Åqvist et al. 2004 and the OpenMM source code. At regular intervals a Monte Carlo step is attempted by scaling the coordinates and the bounding box by a randomly chosen amount in a randomly selected axis. The step is accepted or rejected based on\n\nDelta W = Delta E + P Delta V - gamma Delta A - N k_B T ln left( fracV + Delta VV right)\n\nwhere ΔE is the change in potential energy, P is the equilibrium pressure along the selected axis, ΔV is the change in volume, γ is the surface tension, ΔA is the change in surface area, N is the number of molecules in the system, T is the equilibrium temperature and V is the system volume. If ΔW ≤ 0 the step is always accepted, if ΔW > 0 the step is accepted with probability exp(-ΔW/kT).\n\nThe scale factor is modified over time to maintain an acceptance rate of around half. If the topology of the system is set then molecules are moved as a unit so properties such as bond lengths do not change.\n\nThe barostat assumes that the simulation is being run at a constant temperature but does not actively control the temperature. It should be used alongside a temperature coupling method such as the Langevin simulator or AndersenThermostat coupling. The neighbor list is not updated when making trial moves or after accepted moves. Note that the barostat can change the bounding box of the system.\n\nThis barostat is only available for 3D systems. Not currently compatible with automatic differentiation using Zygote.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MorseBond","page":"API","title":"Molly.MorseBond","text":"MorseBond(; D, a, r0)\n\nA Morse potential bond between two atoms.\n\nThe potential energy is defined as\n\nV(r) = D(1 - e^-a(r - r_0))^2\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MullerBrown","page":"API","title":"Molly.MullerBrown","text":"MullerBrown(; A, a, b, c, x0, y0, force_units, energy_units)\n\nThe Müller-Brown potential energy surface implemented as an AtomsCalculators.jl calculator.\n\nThe potential energy is defined as\n\nV(xy) = sum_n=1^4 A_k expa_k(x-x_k^0)^2 + b_k(x-x_k^0)(y-y_k^0) + c_k(y-y_k^0)^2\n\nwhere A, a, b, c, x0, y0 are 4-element SVectors with standard defaults.\n\nThis potential is only compatible with 2D systems. It is often used for testing algorithms that find transition states or explore minimum energy pathways. There are 3 minima and 2 saddle points with the default parameters.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.NeighborList","page":"API","title":"Molly.NeighborList","text":"NeighborList(n, list)\nNeighborList()\n\nStructure to contain neighbor lists.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.NoCoupling","page":"API","title":"Molly.NoCoupling","text":"NoCoupling()\n\nPlaceholder coupler that does nothing.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.NoCutoff","page":"API","title":"Molly.NoCutoff","text":"NoCutoff()\n\nPlaceholder cutoff that does not alter forces or potentials.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.NoNeighborFinder","page":"API","title":"Molly.NoNeighborFinder","text":"NoNeighborFinder()\n\nPlaceholder neighbor finder that returns no neighbors.\n\nWhen using this neighbor finder, ensure that use_neighbors for the interactions returns false.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.NoseHoover","page":"API","title":"Molly.NoseHoover","text":"NoseHoover(; )\n\nThe Nosé-Hoover integrator, a NVT simulator that extends velocity Verlet to control the temperature of the system.\n\nSee Evans and Holian 1985. The current implementation is limited to ergodic systems.\n\nNot currently compatible with constraints, will print a warning and continue without applying constraints.\n\nArguments\n\ndt::T: the time step of the simulation.\ntemperature::K: the equilibrium temperature of the simulation.\ndamping::D=100*dt: the temperature damping time scale.\ncoupling::C=NoCoupling(): the coupling which applies during the simulation.\nremove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.OverdampedLangevin","page":"API","title":"Molly.OverdampedLangevin","text":"OverdampedLangevin(; )\n\nSimulates the overdamped Langevin equation using the Euler-Maruyama method.\n\nNot currently compatible with constraints, will print a warning and continue without applying constraints.\n\nArguments\n\ndt::S: the time step of the simulation.\ntemperature::K: the equilibrium temperature of the simulation.\nfriction::F: the friction coefficient of the simulation.\nremove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.PairwiseInteraction","page":"API","title":"Molly.PairwiseInteraction","text":"A pairwise interaction that will apply to all or most atom pairs.\n\nCustom pairwise interactions should sub-type this abstract type.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.PeriodicTorsion","page":"API","title":"Molly.PeriodicTorsion","text":"PeriodicTorsion(; periodicities, phases, ks, proper)\n\nA periodic torsion angle between four atoms.\n\nphases are in radians. The potential energy is defined as\n\nV(phi) = sum_n=1^N k_n (1 + cos(n phi - phi_sn))\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.PeriodicTorsionType","page":"API","title":"Molly.PeriodicTorsionType","text":"PeriodicTorsionType(periodicities, phases, ks, proper)\n\nA periodic torsion type.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.RBTorsion","page":"API","title":"Molly.RBTorsion","text":"RBTorsion(; f1, f2, f3, f4)\n\nA Ryckaert-Bellemans torsion angle between four atoms.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.RectangularBoundary","page":"API","title":"Molly.RectangularBoundary","text":"RectangularBoundary(x, y)\nRectangularBoundary(x)\n\nRectangular 2D bounding box defined by two side lengths.\n\nIf one length is given then both sides will have that length. Setting one or more values to Inf gives no boundary in that dimension.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.ReplicaExchangeLogger","page":"API","title":"Molly.ReplicaExchangeLogger","text":"ReplicaExchangeLogger(n_replicas)\nReplicaExchangeLogger(T, n_replicas)\n\nA logger that records exchanges in a replica exchange simulation.\n\nThe logged quantities include the number of exchange attempts (n_attempts), number of successful exchanges (n_exchanges), exchanged replica indices (indices), exchange steps (steps) and the value of Δ i.e. the argument of Metropolis rate for the exchanges (deltas).\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.ReplicaSystem","page":"API","title":"Molly.ReplicaSystem","text":"ReplicaSystem(; )\n\nA wrapper for replicas in a replica exchange simulation.\n\nEach individual replica is a System. Properties unused in the simulation or in analysis can be left with their default values. The minimal required arguments are atoms, replica_coords, boundary and n_replicas. atoms and the elements in replica_coords should have the same length, along with atoms_data and the elements in replica_velocities if these are provided. The number of elements in replica_coords, replica_velocities, replica_loggers and the interaction arguments replica_pairwise_inters, replica_specific_inter_lists, replica_general_inters and replica_constraints should be equal to n_replicas. This is a sub-type of AbstractSystem from AtomsBase.jl and implements the interface described there.\n\nWhen using ReplicaSystem with CellListMapNeighborFinder, the number of threads used for both the simulation of replicas and the neighbor finder should be set to be the same. This can be done by passing nbatches=(min(n, 8), n) to CellListMapNeighborFinder during construction where n is the number of threads to be used per replica.\n\nArguments\n\natoms::A: the atoms, or atom equivalents, in the system. Can be of any type but should be a bits type if the GPU is used.\nreplica_coords: the coordinates of the atoms in each replica.\nboundary::B: the bounding box in which the simulation takes place.\nn_replicas::Integer: the number of replicas of the system.\nreplica_velocities=[zero(replica_coords[1]) * u\"ps^-1\" for _ in 1:n_replicas]: the velocities of the atoms in each replica.\natoms_data::AD: other data associated with the atoms, allowing the atoms to be bits types and hence work on the GPU.\ntopology::TO=nothing: topological information about the system such as which atoms are in the same molecule (to be used if the same for all replicas). This is only used if no value is passed to the argument replica_topology.\nreplica_topology=[nothing for _ in 1:n_replicas]: the topological information for each replica.\npairwise_inters=(): the pairwise interactions in the system, i.e. interactions between all or most atom pairs such as electrostatics (to be used if the same for all replicas). Typically a Tuple. This is only used if no value is passed to the argument replica_pairwise_inters.\nreplica_pairwise_inters=[() for _ in 1:n_replicas]: the pairwise interactions for each replica.\nspecific_inter_lists=(): the specific interactions in the system, i.e. interactions between specific atoms such as bonds or angles (to be used if the same for all replicas). Typically a Tuple. This is only used if no value is passed to the argument replica_specific_inter_lists.\nreplica_specific_inter_lists=[() for _ in 1:n_replicas]: the specific interactions in each replica.\ngeneral_inters=(): the general interactions in the system, i.e. interactions involving all atoms such as implicit solvent (to be used if the same for all replicas). Each should implement the AtomsCalculators.jl interface. Typically a Tuple. This is only used if no value is passed to the argument replica_general_inters.\nreplica_general_inters=[() for _ in 1:n_replicas]: the general interactions for each replica.\nconstraints::CN=(): the constraints for bonds and angles in the system (to be used if the same for all replicas). Typically a Tuple. This is only used if no value is passed to the argument replica_constraints.\nreplica_constraints=[() for _ in 1:n_replicas]: the constraints for bonds and angles in each replica.\nneighbor_finder::NF=NoNeighborFinder(): the neighbor finder used to find close atoms and save on computation. It is duplicated for each replica.\nreplica_loggers=[() for _ in 1:n_replicas]: the loggers for each replica that record properties of interest during a simulation.\nexchange_logger::EL=ReplicaExchangeLogger(n_replicas): the logger used to record the exchange of replicas.\nforce_units::F=u\"kJ * mol^-1 * nm^-1\": the units of force of the system. Should be set to NoUnits if units are not being used.\nenergy_units::E=u\"kJ * mol^-1\": the units of energy of the system. Should be set to NoUnits if units are not being used.\nk::K=Unitful.k or Unitful.k * Unitful.Na: the Boltzmann constant, which may be modified in some simulations. k is chosen based on the energy_units given.\ndata::DA=nothing: arbitrary data associated with the replica system.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.RescaleThermostat","page":"API","title":"Molly.RescaleThermostat","text":"RescaleThermostat(temperature)\n\nThe velocity rescaling thermostat for controlling temperature.\n\nVelocities are immediately rescaled to match a target temperature. The scaling factor for the velocities each step is\n\nlambda = sqrtfracT_0T\n\nThis thermostat should be used with caution as it can lead to simulation artifacts.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.ResidueType","page":"API","title":"Molly.ResidueType","text":"ResidueType(name, types, charges, indices)\n\nA residue type.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.SHAKE_RATTLE","page":"API","title":"Molly.SHAKE_RATTLE","text":"SHAKE_RATTLE(constraints, n_atoms, dist_tolerance, vel_tolerance)\n\nConstrain distances during a simulation using the SHAKE and RATTLE algorithms.\n\nVelocity constraints will be imposed for simulators that integrate velocities such as VelocityVerlet. See Ryckaert et al. 1977 for SHAKE, Andersen 1983 for RATTLE and Elber et al. 2011 for a derivation of the linear system solved to satisfy the RATTLE algorithm.\n\nNot currently compatible with GPU simulation.\n\nArguments\n\nconstraints: a vector of constraints to be imposed on the system.\nn_atoms::Integer: the number of atoms in the system.\ndist_tolerance: the tolerance used to end the iterative procedure when calculating position constraints, should have the same units as the coordinates.\nvel_tolerance: the tolerance used to end the iterative procedure when calculating velocity constraints, should have the same units as the velocities * the coordinates.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.ShiftedForceCutoff","page":"API","title":"Molly.ShiftedForceCutoff","text":"ShiftedForceCutoff(dist_cutoff)\n\nCutoff that shifts the force to be continuous at a specified cutoff point.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.ShiftedPotentialCutoff","page":"API","title":"Molly.ShiftedPotentialCutoff","text":"ShiftedPotentialCutoff(dist_cutoff)\n\nCutoff that shifts the potential to be continuous at a specified cutoff point.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.SoftSphere","page":"API","title":"Molly.SoftSphere","text":"SoftSphere(; cutoff, use_neighbors, lorentz_mixing, force_units, energy_units, skip_shortcut)\n\nThe soft-sphere potential.\n\nThe potential energy is defined as\n\nV(r_ij) = 4varepsilon_ij left(fracsigma_ijr_ijright)^12\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.SpecificForce1Atoms","page":"API","title":"Molly.SpecificForce1Atoms","text":"SpecificForce1Atoms(f1)\n\nForce on one atom arising from an interaction such as a position restraint.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.SpecificForce2Atoms","page":"API","title":"Molly.SpecificForce2Atoms","text":"SpecificForce2Atoms(f1, f2)\n\nForces on two atoms arising from an interaction such as a bond potential.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.SpecificForce3Atoms","page":"API","title":"Molly.SpecificForce3Atoms","text":"SpecificForce3Atoms(f1, f2, f3)\n\nForces on three atoms arising from an interaction such as a bond angle potential.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.SpecificForce4Atoms","page":"API","title":"Molly.SpecificForce4Atoms","text":"SpecificForce4Atoms(f1, f2, f3, f4)\n\nForces on four atoms arising from an interaction such as a torsion potential.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.SpecificInteraction","page":"API","title":"Molly.SpecificInteraction","text":"A specific interaction between sets of specific atoms, e.g. a bond angle.\n\nCustom specific interactions should sub-type this abstract type.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.SteepestDescentMinimizer","page":"API","title":"Molly.SteepestDescentMinimizer","text":"SteepestDescentMinimizer(; )\n\nSteepest descent energy minimization.\n\nNot currently compatible with automatic differentiation using Zygote.\n\nArguments\n\nstep_size::D=0.01u\"nm\": the initial maximum displacement.\nmax_steps::Int=1000: the maximum number of steps.\ntol::F=1000.0u\"kJ * mol^-1 * nm^-1\": the maximum force below which to finish minimization.\nlog_stream::L=devnull: stream to print minimization progress to.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.StormerVerlet","page":"API","title":"Molly.StormerVerlet","text":"StormerVerlet(; )\n\nThe Störmer-Verlet integrator.\n\nThe velocity calculation is accurate to O(dt).\n\nDoes not currently work with coupling methods that alter the velocity. Does not currently remove the center of mass motion.\n\nArguments\n\ndt::T: the time step of the simulation.\ncoupling::C=NoCoupling(): the coupling which applies during the simulation.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.StructureWriter","page":"API","title":"Molly.StructureWriter","text":"StructureWriter(n_steps, filepath, excluded_res=String[])\n\nWrite 3D output structures to a file in the PDB format throughout a simulation.\n\nThe System should have atoms_data defined.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.System","page":"API","title":"Molly.System","text":"System(; )\n\nA physical system to be simulated.\n\nProperties unused in the simulation or in analysis can be left with their default values. The minimal required arguments are atoms, coords and boundary. atoms and coords should have the same length, along with velocities and atoms_data if these are provided. This is a sub-type of AbstractSystem from AtomsBase.jl and implements the interface described there.\n\nArguments\n\natoms::A: the atoms, or atom equivalents, in the system. Can be of any type but should be a bits type if the GPU is used.\ncoords::C: the coordinates of the atoms in the system. Typically a vector of SVectors of 2 or 3 dimensions.\nboundary::B: the bounding box in which the simulation takes place.\nvelocities::V=zero(coords) * u\"ps^-1\": the velocities of the atoms in the system.\natoms_data::AD=[]: other data associated with the atoms, allowing the atoms to be bits types and hence work on the GPU.\ntopology::TO=nothing: topological information about the system such as which atoms are in the same molecule.\npairwise_inters::PI=(): the pairwise interactions in the system, i.e. interactions between all or most atom pairs such as electrostatics. Typically a Tuple.\nspecific_inter_lists::SI=(): the specific interactions in the system, i.e. interactions between specific atoms such as bonds or angles. Typically a Tuple.\ngeneral_inters::GI=(): the general interactions in the system, i.e. interactions involving all atoms such as implicit solvent. Each should implement the AtomsCalculators.jl interface. Typically a Tuple.\nconstraints::CN=(): the constraints for bonds and angles in the system. Typically a Tuple.\nneighbor_finder::NF=NoNeighborFinder(): the neighbor finder used to find close atoms and save on computation.\nloggers::L=(): the loggers that record properties of interest during a simulation.\nforce_units::F=u\"kJ * mol^-1 * nm^-1\": the units of force of the system. Should be set to NoUnits if units are not being used.\nenergy_units::E=u\"kJ * mol^-1\": the units of energy of the system. Should be set to NoUnits if units are not being used.\nk::K=Unitful.k or Unitful.k * Unitful.Na: the Boltzmann constant, which may be modified in some simulations. k is chosen based on the energy_units given.\ndata::DA=nothing: arbitrary data associated with the system.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.System-Tuple{AbstractString, MolecularForceField}","page":"API","title":"Molly.System","text":"System(coordinate_file, force_field; )\n\nRead a coordinate file in a file format readable by Chemfiles and apply a force field to it.\n\nAtom names should exactly match residue templates - no searching of residue templates is carried out.\n\nSystem(coordinate_file, topology_file; )\nSystem(T, coordinate_file, topology_file; )\n\nRead a Gromacs coordinate file and a Gromacs topology file with all includes collapsed into one file.\n\nGromacs file reading should be considered experimental. The implicit_solvent, kappa and rename_terminal_res keyword arguments are not available when reading Gromacs files.\n\nArguments\n\nboundary=nothing: the bounding box used for simulation, read from the file by default.\nvelocities=nothing: the velocities of the atoms in the system, set to zero by default.\nloggers=(): the loggers that record properties of interest during a simulation.\nunits::Bool=true: whether to use Unitful quantities.\ngpu::Bool=false: whether to move the relevant parts of the system onto the GPU.\ndist_cutoff=1.0u\"nm\": cutoff distance for long-range interactions.\ndist_neighbors=1.2u\"nm\": cutoff distance for the neighbor list, should be greater than dist_cutoff.\ncenter_coords::Bool=true: whether to center the coordinates in the simulation box.\nuse_cell_list::Bool=true: whether to use CellListMapNeighborFinder on CPU. If false, DistanceNeighborFinder is used.\ndata=nothing: arbitrary data associated with the system.\nimplicit_solvent=nothing: specify a string to add an implicit solvent model, options are \"obc1\", \"obc2\" and \"gbn2\".\nkappa=0.0u\"nm^-1\": the kappa value for the implicit solvent model if one is used.\nrename_terminal_res=true: whether to rename the first and last residues to match the appropriate atom templates, for example the first (N-terminal) residue could be changed from \"MET\" to \"NMET\".\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.System-Tuple{System}","page":"API","title":"Molly.System","text":"System(sys; )\n\nConvenience constructor for changing properties in a System.\n\nA copy of the System is returned with the provided keyword arguments modified.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.System-Union{Tuple{AtomsBase.AbstractSystem{D}}, Tuple{D}} where D","page":"API","title":"Molly.System","text":"System(abstract_system; force_units=u\"kJ * mol^-1 * nm^-1\", energy_units=u\"kJ * mol^-1\")\n\nConvert an AtomsBase AbstractSystem to a Molly System.\n\nforce_units and energy_units should be set as appropriate. To add properties not present in the AtomsBase interface (e.g. pair potentials) use the convenience constructor System(sys::System).\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.System-Union{Tuple{SimpleCrystals.Crystal{D, A, B} where {A, B<:(AbstractVector{<:SimpleCrystals.Atom{D}})}}, Tuple{D}} where D","page":"API","title":"Molly.System","text":"System(crystal; )\n\nConstruct a System from a SimpleCrystals.jl Crystal struct.\n\nProperties unused in the simulation or in analysis can be left with their default values. atoms, atoms_data, coords and boundary are automatically calcualted from the Crystal struct. Extra atom paramaters like σ have to be added manually after construction using the convenience constructor System(sys; ).\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.TemperatureREMD","page":"API","title":"Molly.TemperatureREMD","text":"TemperatureREMD(; )\n\nA simulator for a parallel temperature replica exchange MD (T-REMD) simulation on a ReplicaSystem.\n\nSee Sugita and Okamoto 1999. The corresponding ReplicaSystem should have the same number of replicas as the number of temperatures in the simulator. When calling simulate!, the assign_velocities keyword argument determines whether to assign random velocities at the appropriate temperature for each replica.\n\nNot currently compatible with automatic differentiation using Zygote.\n\nArguments\n\ndt::DT: the time step of the simulation.\ntemperatures::TP: the temperatures corresponding to the replicas.\nsimulators::ST: individual simulators for simulating each replica.\nexchange_time::ET: the time interval between replica exchange attempts.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.TimeCorrelationLogger","page":"API","title":"Molly.TimeCorrelationLogger","text":"TimeCorrelationLogger(observableA::Function, observableB::Function,\n TA::DataType, TB::DataType,\n observable_length::Integer, n_correlation::Integer)\n\nA time correlation logger.\n\nEstimates statistical correlations of normalized form\n\nC(t)=fraclangle A_tcdot B_0rangle -langle Aranglecdot langle Branglesqrtlangle A^2ranglelangle B^2rangle\n\nor unnormalized form\n\nC(t)=langle A_tcdot B_0rangle -langle A ranglecdot langle Brangle\n\nThese can be used to estimate statistical error, or to compute transport coefficients from Green-Kubo type formulas. A and B are observables, functions of the form observable(sys::System, neighbors; n_threads::Integer). The return values of A and B can be of scalar or vector type (including Vector{SVector{...}}, like positions or velocities) and must implement dot.\n\nn_correlation should typically be chosen so that dt * n_correlation > t_corr, where dt is the simulation timestep and t_corr is the decorrelation time for the considered system and observables. For the purpose of numerical stability, the logger internally records sums instead of running averages. The normalized and unnormalized form of the correlation function can be retrieved through values(logger::TimeCorrelationLogger; normalize::Bool).\n\nArguments\n\nobservableA::Function: the function corresponding to observable A.\nobservableB::Function: the function corresponding to observable B.\nTA::DataType: the type returned by observableA, supporting zero(TA).\nTB::DataType: the type returned by observableB, supporting zero(TB).\nobservable_length::Integer: the length of the observables if they are vectors, or 1 if they are scalar-valued.\nn_correlation::Integer: the length of the computed correlation vector.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.TreeNeighborFinder","page":"API","title":"Molly.TreeNeighborFinder","text":"TreeNeighborFinder(; eligible, dist_cutoff, special, n_steps)\n\nFind close atoms by distance using a tree search.\n\nCan not be used if one or more dimensions has infinite boundaries. Can not be used with TriclinicBoundary.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.TriclinicBoundary","page":"API","title":"Molly.TriclinicBoundary","text":"TriclinicBoundary(v1, v2, v3; approx_images=true)\nTriclinicBoundary(SVector(v1, v2, v3); approx_images=true)\nTriclinicBoundary(SVector(l1, l2, l3), SVector(α, β, γ); approx_images=true)\nTriclinicBoundary(arr; approx_images=true)\n\nTriclinic 3D bounding box defined by 3 SVector{3} basis vectors or basis vector lengths and angles α/β/γ in radians.\n\nThe first basis vector must point along the x-axis and the second must lie in the xy plane. An approximation is used to find the closest periodic image when using the minimum image convention. The approximation is correct for distances shorter than half the shortest box height/width. Setting the keyword argument approx_images to false means the exact closest image is found, which is slower.\n\nNot currently able to simulate a cubic box, use CubicBoundary or small offsets instead. Not currently compatible with infinite boundaries. Not currently compatible with automatic differentiation using Zygote.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.VelocityVerlet","page":"API","title":"Molly.VelocityVerlet","text":"VelocityVerlet(; )\n\nThe velocity Verlet integrator.\n\nArguments\n\ndt::T: the time step of the simulation.\ncoupling::C=NoCoupling(): the coupling which applies during the simulation.\nremove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.Verlet","page":"API","title":"Molly.Verlet","text":"Verlet(; )\n\nThe leapfrog Verlet integrator.\n\nThis is a leapfrog integrator, so the velocities are offset by half a time step behind the positions.\n\nArguments\n\ndt::T: the time step of the simulation.\ncoupling::C=NoCoupling(): the coupling which applies during the simulation.\nremove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.\n\n\n\n\n\n","category":"type"},{"location":"api/#Base.values-Tuple{GeneralObservableLogger}","page":"API","title":"Base.values","text":"values(logger)\nvalues(logger::TimeCorrelationLogger; normalize::Bool=true)\nvalues(logger::AverageObservableLogger; std::Bool=true)\n\nAccess the stored observations in a logger.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.AutoCorrelationLogger-Tuple{Any, Any, Integer, Integer}","page":"API","title":"Molly.AutoCorrelationLogger","text":"AutoCorrelationLogger(observable::Function, TA::DataType,\n observable_length::Integer, n_correlation::Integer)\n\nAn autocorrelation logger, equivalent to a TimeCorrelationLogger in the case that observableA == observableB.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.CoordinateLogger-Tuple{Any, Integer}","page":"API","title":"Molly.CoordinateLogger","text":"CoordinateLogger(n_steps; dims=3)\nCoordinateLogger(T, n_steps; dims=3)\n\nLog the coordinates throughout a simulation.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.ForceLogger-Tuple{Any, Integer}","page":"API","title":"Molly.ForceLogger","text":"ForceLogger(n_steps; dims=3)\nForceLogger(T, n_steps; dims=3)\n\nLog the forces throughout a simulation.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.KineticEnergyLogger-Tuple{Type, Integer}","page":"API","title":"Molly.KineticEnergyLogger","text":"KineticEnergyLogger(n_steps)\nKineticEnergyLogger(T, n_steps)\n\nLog the kinetic_energy of a system throughout a simulation.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.PotentialEnergyLogger-Tuple{Type, Integer}","page":"API","title":"Molly.PotentialEnergyLogger","text":"PotentialEnergyLogger(n_steps)\nPotentialEnergyLogger(T, n_steps)\n\nLog the potential_energy of a system throughout a simulation.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.PressureLogger-Tuple{Type, Integer}","page":"API","title":"Molly.PressureLogger","text":"PressureLogger(n_steps)\nPressureLogger(T, n_steps)\n\nLog the pressure of a system throughout a simulation.\n\nThis should only be used on systems containing just pairwise interactions, or where the specific interactions, general interactions and constraints do not contribute to the pressure.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.TemperatureLogger-Tuple{DataType, Integer}","page":"API","title":"Molly.TemperatureLogger","text":"TemperatureLogger(n_steps)\nTemperatureLogger(T, n_steps)\n\nLog the temperature throughout a simulation.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.TotalEnergyLogger-Tuple{DataType, Any}","page":"API","title":"Molly.TotalEnergyLogger","text":"TotalEnergyLogger(n_steps)\nTotalEnergyLogger(T, n_steps)\n\nLog the total_energy of a system throughout a simulation.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.VelocityLogger-Tuple{Any, Integer}","page":"API","title":"Molly.VelocityLogger","text":"VelocityLogger(n_steps; dims=3)\nVelocityLogger(T, n_steps; dims=3)\n\nLog the velocities throughout a simulation.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.VirialLogger-Tuple{Type, Integer}","page":"API","title":"Molly.VirialLogger","text":"VirialLogger(n_steps)\nVirialLogger(T, n_steps)\n\nLog the virial of a system throughout a simulation.\n\nThis should only be used on systems containing just pairwise interactions, or where the specific interactions, general interactions and constraints do not contribute to the virial.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.accelerations-Tuple{Any}","page":"API","title":"Molly.accelerations","text":"accelerations(system, neighbors=find_neighbors(sys); n_threads=Threads.nthreads())\n\nCalculate the accelerations of all atoms in a system using the pairwise, specific and general interactions and Newton's second law of motion.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.add_position_restraints-Tuple{Any, Any}","page":"API","title":"Molly.add_position_restraints","text":"add_position_restraints(sys, k; atom_selector=is_any_atom, restrain_coords=sys.coords)\n\nReturn a copy of a System with HarmonicPositionRestraints added to restrain the atoms.\n\nThe force constant k can be a single value or an array of equal length to the number of atoms in the system. The atom_selector function takes in each atom and atom data and determines whether to restrain that atom. For example, is_heavy_atom means non-hydrogen atoms are restrained.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.apply_coupling!-Tuple{Any, Union{Tuple, NamedTuple}, Any, Any, Any}","page":"API","title":"Molly.apply_coupling!","text":"apply_coupling!(system, coupling, simulator, neighbors=nothing,\n step_n=0; n_threads=Threads.nthreads())\n\nApply a coupler to modify a simulation.\n\nReturns whether the coupling has invalidated the currently stored forces, for example by changing the coordinates. This information is useful for some simulators. If coupling is a tuple or named tuple then each coupler will be applied in turn. Custom couplers should implement this function.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.apply_position_constraints!-Tuple{Any, Any}","page":"API","title":"Molly.apply_position_constraints!","text":"apply_position_constraints!(sys, coord_storage; n_threads::Integer=Threads.nthreads())\napply_position_constraints!(sys, coord_storage, vel_storage, dt;\n n_threads::Integer=Threads.nthreads())\n\nApplies the system constraints to the coordinates.\n\nIf vel_storage and dt are provided then velocity corrections are applied as well.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.apply_velocity_constraints!-Tuple{Any}","page":"API","title":"Molly.apply_velocity_constraints!","text":"apply_velocity_constraints!(sys; n_threads::Integer=Threads.nthreads())\n\nApplies the system constraints to the velocities.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.bond_angle-NTuple{4, Any}","page":"API","title":"Molly.bond_angle","text":"bond_angle(coord_i, coord_j, coord_k, boundary)\nbond_angle(vec_ji, vec_jk)\n\nCalculate the bond or pseudo-bond angle in radians between three coordinates or two vectors.\n\nThe angle between j→i and j→k is returned in the range 0 to π.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.born_radii_and_grad-Tuple{ImplicitSolventOBC, Any, Any}","page":"API","title":"Molly.born_radii_and_grad","text":"born_radii_and_grad(inter, coords, boundary)\n\nCalculate Born radii, gradients of Born radii and surface area overlap with respect to atomic distance.\n\nCustom GBSA methods should implement this function.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.box_center-Tuple{Union{CubicBoundary, RectangularBoundary}}","page":"API","title":"Molly.box_center","text":"box_center(boundary)\n\nCalculate the center of a bounding box.\n\nDimensions with infinite length return zero.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.box_volume-Tuple{Union{CubicBoundary, RectangularBoundary}}","page":"API","title":"Molly.box_volume","text":"box_volume(boundary)\n\nCalculate the volume of a 3D bounding box or the area of a 2D bounding box.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.charge-Tuple{Any}","page":"API","title":"Molly.charge","text":"charge(atom)\n\nThe partial charge of an Atom.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.charges-Tuple{Union{ReplicaSystem, System}}","page":"API","title":"Molly.charges","text":"charges(sys)\n\nThe partial charges of the atoms in a System or ReplicaSystem.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.check_position_constraints-Tuple{Any, SHAKE_RATTLE}","page":"API","title":"Molly.check_position_constraints","text":"check_position_constraints(sys, constraints)\n\nChecks if the position constraints are satisfied by the current coordinates of sys.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.check_velocity_constraints-Tuple{System, SHAKE_RATTLE}","page":"API","title":"Molly.check_velocity_constraints","text":"check_velocity_constraints(sys, constraints)\n\nChecks if the velocity constraints are satisfied by the current velocities of sys.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.disable_constrained_interactions!-Tuple{Any, Any}","page":"API","title":"Molly.disable_constrained_interactions!","text":"disable_constrained_interactions!(neighbor_finder, constraint_clusters)\n\nDisables neighbor list interactions between atoms in a constraint.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.displacements-Tuple{Any, Any}","page":"API","title":"Molly.displacements","text":"displacements(coords, boundary)\n\nCalculate the pairwise vector displacements of a set of coordinates, accounting for the periodic boundary conditions.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.distances-Tuple{Any, Any}","page":"API","title":"Molly.distances","text":"distances(coords, boundary)\n\nCalculate the pairwise distances of a set of coordinates, accounting for the periodic boundary conditions.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.extract_parameters-Tuple{Any, Any}","page":"API","title":"Molly.extract_parameters","text":"extract_parameters(system, force_field)\n\nForm a Dict of all parameters in a System, allowing gradients to be tracked.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.find_neighbors-Tuple{System}","page":"API","title":"Molly.find_neighbors","text":"find_neighbors(system; n_threads=Threads.nthreads())\nfind_neighbors(system, neighbor_finder, current_neighbors=nothing, step_n=0,\n force_recompute=false; n_threads=Threads.nthreads())\n\nObtain a list of close atoms in a System.\n\nCustom neighbor finders should implement this function.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.float_type-Union{Tuple{Union{ReplicaSystem{D, G, T}, System{D, G, T}}}, Tuple{T}, Tuple{G}, Tuple{D}} where {D, G, T}","page":"API","title":"Molly.float_type","text":"float_type(sys)\nfloat_type(boundary)\n\nThe float type a System, ReplicaSystem or bounding box uses.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.force-NTuple{8, Any}","page":"API","title":"Molly.force","text":"force(inter::PairwiseInteraction, vec_ij, coord_i, coord_j,\n atom_i, atom_j, boundary)\nforce(inter::PairwiseInteraction, vec_ij, coord_i, coord_j,\n atom_i, atom_j, boundary, special)\nforce(inter::SpecificInteraction, coord_i, coord_j,\n boundary)\nforce(inter::SpecificInteraction, coord_i, coord_j,\n coord_k, boundary)\nforce(inter::SpecificInteraction, coord_i, coord_j,\n coord_k, coord_l, boundary)\n\nCalculate the force between atoms due to a given interaction type.\n\nFor PairwiseInteractions returns a single force vector and for SpecificInteractions returns a type such as SpecificForce2Atoms. Custom pairwise and specific interaction types should implement this function.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.forces-Tuple{Any}","page":"API","title":"Molly.forces","text":"forces(system, neighbors=find_neighbors(sys); n_threads=Threads.nthreads())\n\nCalculate the forces on all atoms in a system using the pairwise, specific and general interactions.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.hydrodynamic_radius-Union{Tuple{T}, Tuple{D}, Tuple{AbstractArray{SVector{D, T}}, Any}} where {D, T}","page":"API","title":"Molly.hydrodynamic_radius","text":"hydrodynamic_radius(coords, boundary)\n\nCalculate the hydrodynamic radius of a set of coordinates.\n\nR_hyd is defined by\n\nfrac1R_hyd = frac12N^2sum_i neq j frac1r_ij\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.inject_gradients-Union{Tuple{G}, Tuple{D}, Tuple{System{D, G}, Any}} where {D, G}","page":"API","title":"Molly.inject_gradients","text":"inject_gradients(sys, params_dic)\n\nAdd parameters from a dictionary to a System.\n\nAllows gradients for individual parameters to be tracked. Returns atoms, pairwise interactions, specific interaction lists and general interactions.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.is_any_atom-Tuple{Any, Any}","page":"API","title":"Molly.is_any_atom","text":"is_any_atom(at, at_data)\n\nPlaceholder function that returns true, used to select any Atom.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.is_heavy_atom-Tuple{Any, Any}","page":"API","title":"Molly.is_heavy_atom","text":"is_heavy_atom(at, at_data)\n\nDetermines whether an Atom is a heavy atom, i.e. any element other than hydrogen.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.is_on_gpu-Union{Tuple{Union{ReplicaSystem{D, G}, System{D, G}}}, Tuple{G}, Tuple{D}} where {D, G}","page":"API","title":"Molly.is_on_gpu","text":"is_on_gpu(sys)\n\nWhether a System or ReplicaSystem is on the GPU.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.kinetic_energy-Union{Tuple{System{D, G, T}}, Tuple{T}, Tuple{G}, Tuple{D}} where {D, G, T}","page":"API","title":"Molly.kinetic_energy","text":"kinetic_energy(system)\n\nCalculate the kinetic energy of a system.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.log_property!","page":"API","title":"Molly.log_property!","text":"log_property!(logger, system, neighbors=nothing, step_n=0; n_threads=Threads.nthreads(), kwargs...)\n\nLog a property of a system throughout a simulation.\n\nCustom loggers should implement this function. Additional keyword arguments can be passed to the logger if required.\n\n\n\n\n\n","category":"function"},{"location":"api/#Molly.mass-Tuple{Any}","page":"API","title":"Molly.mass","text":"mass(atom)\n\nThe mass of an Atom.\n\nCustom atom types should implement this function unless they have a mass field defined, which the function accesses by default.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.masses-Tuple{System}","page":"API","title":"Molly.masses","text":"masses(sys)\n\nThe masses of the atoms in a System or ReplicaSystem.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.maxwell_boltzmann","page":"API","title":"Molly.maxwell_boltzmann","text":"maxwell_boltzmann(atom_mass::Unitful.Mass, temp::Unitful.Temperature,\n k::BoltzmannConstUnits=Unitful.k; rng=Random.GLOBAL_RNG)\nmaxwell_boltzmann(atom_mass::MolarMass, temp::Unitful.Temperature,\n k_molar::MolarBoltzmannConstUnits=(Unitful.k * Unitful.Na);\n rng=Random.GLOBAL_RNG)\nmaxwell_boltzmann(atom_mass::Real, temperature::Real,\n k::Real=ustrip(u\"u * nm^2 * ps^-2 * K^-1\", Unitful.k); rng=Random.GLOBAL_RNG)\n\nGenerate a random velocity along one dimension from the Maxwell-Boltzmann distribution, with optional custom Boltzmann constant.\n\n\n\n\n\n","category":"function"},{"location":"api/#Molly.molecule_centers-Union{Tuple{C}, Tuple{D}, Tuple{AbstractArray{SVector{D, C}}, Any, Any}} where {D, C}","page":"API","title":"Molly.molecule_centers","text":"molecule_centers(coords, boundary, topology)\n\nCalculate the coordinates of the center of each molecule in a system.\n\nAccounts for periodic boundary conditions by using the circular mean. If topology=nothing then the coordinates are returned.\n\nNot currently compatible with TriclinicBoundary if the topology is set. Not currently compatible with automatic differentiation using Zygote.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.place_atoms-Tuple{Integer, Any}","page":"API","title":"Molly.place_atoms","text":"place_atoms(n_atoms, boundary; min_dist=nothing, max_attempts=100)\n\nGenerate random coordinates.\n\nObtain n_atoms coordinates in bounding box boundary where no two points are closer than min_dist, accounting for periodic boundary conditions. The keyword argument max_attempts determines the number of failed tries after which to stop placing atoms. Can not be used if one or more dimensions has infinite boundaries.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.place_diatomics-Tuple{Integer, Any, Any}","page":"API","title":"Molly.place_diatomics","text":"place_diatomics(n_molecules, boundary, bond_length; min_dist=nothing,\n max_attempts=100, aligned=false)\n\nGenerate random diatomic molecule coordinates.\n\nObtain coordinates for n_molecules diatomics in bounding box boundary where no two points are closer than min_dist and the bond length is bond_length, accounting for periodic boundary conditions. The keyword argument max_attempts determines the number of failed tries after which to stop placing atoms. The keyword argument aligned determines whether the bonds all point the same direction (true) or random directions (false). Can not be used if one or more dimensions has infinite boundaries.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.potential_energy-Tuple{Any}","page":"API","title":"Molly.potential_energy","text":"potential_energy(system, neighbors=find_neighbors(sys); n_threads=Threads.nthreads())\n\nCalculate the potential energy of a system using the pairwise, specific and general interactions.\n\npotential_energy(inter::PairwiseInteraction, vec_ij, coord_i, coord_j,\n atom_i, atom_j, boundary)\npotential_energy(inter::SpecificInteraction, coords_i, coords_j,\n boundary)\npotential_energy(inter::SpecificInteraction, coords_i, coords_j,\n coords_k, boundary)\npotential_energy(inter::SpecificInteraction, coords_i, coords_j,\n coords_k, coords_l, boundary)\n\nCalculate the potential energy due to a given interaction type.\n\nCustom interaction types should implement this function.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.pressure-Tuple{Any}","page":"API","title":"Molly.pressure","text":"pressure(sys, neighbors=find_neighbors(sys); n_threads=Threads.nthreads())\n\nCalculate the pressure of a system.\n\nThe pressure is defined as\n\nP = frac1V left( NkT - frac2D Xi right)\n\nwhere V is the system volume, N is the number of atoms, k is the Boltzmann constant, T is the system temperature, D is the number of dimensions and Ξ is the virial calculated using virial.\n\nThis should only be used on systems containing just pairwise interactions, or where the specific interactions, constraints and general interactions without virial defined do not contribute to the virial. Not compatible with infinite boundaries. Not currently compatible with automatic differentiation using Zygote when using pairwise interactions.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.radius_gyration-Tuple{Any, Any}","page":"API","title":"Molly.radius_gyration","text":"radius_gyration(coords, atoms)\n\nCalculate the radius of gyration of a set of coordinates.\n\nAssumes the coordinates do not cross the bounding box, i.e. all coordinates correspond to the same periodic image.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.random_coord-Tuple{CubicBoundary}","page":"API","title":"Molly.random_coord","text":"random_coord(boundary)\n\nGenerate a random coordinate uniformly distributed within a bounding box.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.random_normal_translation!-Union{Tuple{System{D, G, T}}, Tuple{T}, Tuple{G}, Tuple{D}} where {D, G, T}","page":"API","title":"Molly.random_normal_translation!","text":"random_normal_translation!(sys::System; shift_size=oneunit(eltype(eltype(sys.coords))))\n\nPerforms a random translation of the coordinates of a randomly selected atom in a System.\n\nThe translation is generated using a uniformly chosen direction and length selected from the standard normal distribution i.e. with mean 0 and standard deviation 1, scaled by shift_size which should have appropriate length units.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.random_uniform_translation!-Union{Tuple{System{D, G, T}}, Tuple{T}, Tuple{G}, Tuple{D}} where {D, G, T}","page":"API","title":"Molly.random_uniform_translation!","text":"random_uniform_translation!(sys::System; shift_size=oneunit(eltype(eltype(sys.coords))))\n\nPerforms a random translation of the coordinates of a randomly selected atom in a System.\n\nThe translation is generated using a uniformly selected direction and uniformly selected length in range [0, 1) scaled by shift_size which should have appropriate length units.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.random_velocities!-Tuple{Any, Any}","page":"API","title":"Molly.random_velocities!","text":"random_velocities!(sys, temp)\n\nSet the velocities of a System to random velocities generated from the Maxwell-Boltzmann distribution.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.random_velocities-Tuple{AtomsBase.AbstractSystem{3}, Any}","page":"API","title":"Molly.random_velocities","text":"random_velocities(sys, temp)\n\nGenerate random velocities from the Maxwell-Boltzmann distribution for a System.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.random_velocity-Tuple{Union{Level{L, S, Quantity{T, 𝐌, U}} where {T, U, L, S}, Level{L, S, Quantity{T, 𝐌 𝐍^-1, U}} where {T, U, L, S}, Quantity{T, 𝐌} where T, Quantity{T, 𝐌 𝐍^-1} where T}, Union{Quantity{T, 𝚯, U}, Level{L, S, Quantity{T, 𝚯, U}} where {L, S}} where {T, U}}","page":"API","title":"Molly.random_velocity","text":"random_velocity(atom_mass::Union{Unitful.Mass, MolarMass}, temp::Unitful.Temperature;\n dims=3, rng=Random.GLOBAL_RNG)\nrandom_velocity(atom_mass::Union{Unitful.Mass, MolarMass}, temp::Unitful.Temperature,\n k::Union{BoltzmannConstUnits, MolarBoltzmannConstUnits};\n dims=3, rng=Random.GLOBAL_RNG)\nrandom_velocity(atom_mass::Real, temp::Real, k::Real=ustrip(u\"u * nm^2 * ps^-2 * K^-1\", Unitful.k);\n dims=3, rng=Random.GLOBAL_RNG)\n\nGenerate a random velocity from the Maxwell-Boltzmann distribution, with optional custom Boltzmann constant.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.rdf-Tuple{Any, Any}","page":"API","title":"Molly.rdf","text":"rdf(coords, boundary; npoints=200)\n\nCalculate the radial distribution function of a set of coordinates.\n\nThis describes how density varies as a function of distance from each atom. Returns a list of distance bin centers and a list of the corresponding densities.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.remd_exchange!-Union{Tuple{T}, Tuple{G}, Tuple{D}, Tuple{ReplicaSystem{D, G, T}, TemperatureREMD, Integer, Integer}} where {D, G, T}","page":"API","title":"Molly.remd_exchange!","text":"remd_exchange!(sys, sim, n, m; rng=Random.GLOBAL_RNG, n_threads=Threads.nthreads())\n\nAttempt an exchange of replicas n and m in a ReplicaSystem during a REMD simulation.\n\nSuccessful exchanges should exchange coordinates and velocities as appropriate. Returns acceptance quantity Δ and a Bool indicating whether the exchange was successful.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.remove_CM_motion!-Tuple{Any}","page":"API","title":"Molly.remove_CM_motion!","text":"remove_CM_motion!(system)\n\nRemove the center of mass motion from a System.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.rmsd-Union{Tuple{T}, Tuple{D}, Tuple{AbstractArray{SVector{D, T}}, AbstractArray{SVector{D, T}}}} where {D, T}","page":"API","title":"Molly.rmsd","text":"rmsd(coords_1, coords_2)\n\nCalculate the root-mean-square deviation (RMSD) of two sets of 3D coordinates after superimposition by the Kabsch algorithm.\n\nAssumes the coordinates do not cross the bounding box, i.e. all coordinates in each set correspond to the same periodic image.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.run_loggers!","page":"API","title":"Molly.run_loggers!","text":"run_loggers!(system, neighbors=nothing, step_n=0, run_loggers=true;\n n_threads=Threads.nthreads(), kwargs...)\n\nRun the loggers associated with a system.\n\nrun_loggers can be true, false or :skipzero, in which case the loggers are not run before the first step. Additional keyword arguments can be passed to the loggers if required. Ignored for gradient calculation during automatic differentiation.\n\n\n\n\n\n","category":"function"},{"location":"api/#Molly.scale_boundary-Tuple{CubicBoundary, Any}","page":"API","title":"Molly.scale_boundary","text":"scale_boundary(boundary, scale_factor)\n\nScale the sides of a bounding box by a scaling factor.\n\nThe scaling factor can be a single number or a SVector of the appropriate number of dimensions corresponding to the scaling factor for each axis. For a 3D bounding box the volume scales as the cube of the scaling factor.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.scale_coords!-Tuple{Any, Any}","page":"API","title":"Molly.scale_coords!","text":"scale_coords!(sys, scale_factor; ignore_molecules=false)\n\nScale the coordinates and bounding box of a system by a scaling factor.\n\nThe scaling factor can be a single number or a SVector of the appropriate number of dimensions corresponding to the scaling factor for each axis. Velocities are not scaled. If the topology of the system is set then atoms in the same molecule will be moved by the same amount according to the center of coordinates of the molecule. This can be disabled with ignore_molecules=true.\n\nNot currently compatible with TriclinicBoundary if the topology is set. Not currently compatible with automatic differentiation using Zygote.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.simulate!-Tuple{Any, SteepestDescentMinimizer}","page":"API","title":"Molly.simulate!","text":"simulate!(system, simulator, n_steps; n_threads=Threads.nthreads(), run_loggers=true)\nsimulate!(system, simulator; n_threads=Threads.nthreads(), run_loggers=true)\n\nRun a simulation on a system according to the rules of the given simulator.\n\nrun_loggers can be true, false or :skipzero, in which case the loggers are not run before the first step. run_loggers is true by default except for SteepestDescentMinimizer, where it is false. Custom simulators should implement this function.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.simulate_remd!-Tuple{ReplicaSystem, Any, Integer}","page":"API","title":"Molly.simulate_remd!","text":"simulate_remd!(sys, remd_sim, n_steps; rng=Random.GLOBAL_RNG,\n n_threads=Threads.nthreads(), run_loggers=true)\n\nRun a REMD simulation on a ReplicaSystem using a REMD simulator.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.temperature-Tuple{Any}","page":"API","title":"Molly.temperature","text":"temperature(system)\n\nCalculate the temperature of a system from the kinetic energy of the atoms.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.torsion_angle-NTuple{5, Any}","page":"API","title":"Molly.torsion_angle","text":"torsion_angle(coord_i, coord_j, coord_k, coord_l, boundary)\ntorsion_angle(vec_ij, vec_jk, vec_kl)\n\nCalculate the torsion angle in radians defined by four coordinates or three vectors.\n\nThe angle between the planes defined by atoms (i, j, k) and (j, k, l) is returned in the range -π to π.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.total_energy-Tuple{Any}","page":"API","title":"Molly.total_energy","text":"total_energy(system, neighbors=find_neighbors(sys); n_threads=Threads.nthreads())\n\nCalculate the total energy of a system as the sum of the kinetic_energy and the potential_energy.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.use_neighbors-Tuple{PairwiseInteraction}","page":"API","title":"Molly.use_neighbors","text":"use_neighbors(inter)\n\nWhether a pairwise interaction uses the neighbor list, default false.\n\nCustom pairwise interactions can define a method for this function. For built-in interactions such as LennardJones this function accesses the use_neighbors field of the struct.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.ustrip_vec-Tuple","page":"API","title":"Molly.ustrip_vec","text":"ustrip_vec(x)\nustrip_vec(u, x)\n\nBroadcasted form of ustrip from Unitful.jl, allowing e.g. ustrip_vec.(coords).\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.vector-Tuple{Any, Any, CubicBoundary}","page":"API","title":"Molly.vector","text":"vector(c1, c2, boundary)\n\nDisplacement between two coordinate values from c1 to c2, accounting for periodic boundary conditions.\n\nThe minimum image convention is used, so the displacement is to the closest version of the coordinates accounting for the periodic boundaries. For the TriclinicBoundary an approximation is used to find the closest version by default.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.vector_1D-Tuple{Any, Any, Any}","page":"API","title":"Molly.vector_1D","text":"vector_1D(c1, c2, side_length)\n\nDisplacement between two 1D coordinate values from c1 to c2, accounting for periodic boundary conditions in a CubicBoundary or RectangularBoundary.\n\nThe minimum image convention is used, so the displacement is to the closest version of the coordinate accounting for the periodic boundaries.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.virial-Tuple{Any}","page":"API","title":"Molly.virial","text":"virial(sys, neighbors=find_neighbors(sys); n_threads=Threads.nthreads())\nvirial(inter, sys, neighbors; n_threads=Threads.nthreads())\n\nCalculate the virial of a system or the virial resulting from a general interaction.\n\nThe virial is defined as\n\nXi = -frac12 sum_iji r_ij cdot F_ij\n\nCustom general interaction types can implement this function.\n\nThis should only be used on systems containing just pairwise interactions, or where the specific interactions, constraints and general interactions without virial defined do not contribute to the virial. Not currently compatible with automatic differentiation using Zygote when using pairwise interactions.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.visualize","page":"API","title":"Molly.visualize","text":"visualize(coord_logger, boundary, out_filepath; )\n\nVisualize a simulation as an animation.\n\nThis function is only available when GLMakie is imported. It can take a while to run, depending on the length of the simulation and the number of atoms.\n\nArguments\n\nconnections=Tuple{Int, Int}[]: pairs of atoms indices to link with bonds.\nconnection_frames: the frames in which bonds are shown. Should be a list of the same length as the number of frames, where each item is a list of Bools of the same length as connections. Defaults to always true.\ntrails::Integer=0: the number of preceding frames to show as transparent trails.\nframerate::Integer=30: the frame rate of the animation.\ncolor=:purple: the color of the atoms. Can be a single color or a list of colors of the same length as the number of atoms.\nconnection_color=:orange: the color of the bonds. Can be a single color or a list of colors of the same length as connections.\nmarkersize=0.05: the size of the atom markers, in the units of the data.\nlinewidth=2.0: the width of the bond lines.\ntransparency=true: whether transparency is active on the plot.\nshow_boundary::Bool=true: whether to show the bounding box as lines.\nboundary_linewidth=2.0: the width of the boundary lines.\nboundary_color=:black: the color of the boundary lines.\nkwargs...: other keyword arguments are passed to the point plotting function.\n\n\n\n\n\n","category":"function"},{"location":"api/#Molly.wrap_coord_1D-Tuple{Any, Any}","page":"API","title":"Molly.wrap_coord_1D","text":"wrap_coord_1D(c, side_length)\n\nEnsure a 1D coordinate is within the bounding box and return the coordinate.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.wrap_coords-Tuple{Any, Union{CubicBoundary, RectangularBoundary}}","page":"API","title":"Molly.wrap_coords","text":"wrap_coords(c, boundary)\n\nEnsure a coordinate is within the bounding box and return the coordinate.\n\n\n\n\n\n","category":"method"},{"location":"publications/#Publications","page":"Publications","title":"Publications","text":"","category":"section"},{"location":"publications/","page":"Publications","title":"Publications","text":"If you use Molly, please cite the following paper (bib entry here):","category":"page"},{"location":"publications/","page":"Publications","title":"Publications","text":"Greener JG. Differentiable simulation to develop molecular dynamics force fields for disordered proteins, Chemical Science 15, 4897-4909 (2024)","category":"page"},{"location":"publications/","page":"Publications","title":"Publications","text":"A paper involving more contributors with further details on the software will be written at some point.","category":"page"},{"location":"publications/","page":"Publications","title":"Publications","text":"Other papers that use, contribute to or are compatible with Molly are listed below:","category":"page"},{"location":"publications/","page":"Publications","title":"Publications","text":"Martínez L. CellListMap.jl: Efficient and customizable cell list implementation for calculation of pairwise particle properties within a cutoff, Comput Phys Commun 279, 108452 (2022)\nBlassel N and Stoltz G. Fixing the flux: A dual approach to computing transport coefficients, arXiv (2023)\nWitt WC et al. ACEpotentials.jl: A Julia implementation of the atomic cluster expansion, J Chem Phys 159, 164101 (2023)","category":"page"},{"location":"related/#Related-software","page":"Related software","title":"Related software","text":"","category":"section"},{"location":"related/","page":"Related software","title":"Related software","text":"There are many mature packages for molecular simulation. Of particular note here are OpenMM and GROMACS, both of which influenced the implementation of Molly. Molly can be thought of as similar to OpenMM in that it exposes simulation internals in a high-level language, though it is written in one language all the way down rather than using multiple device-specific kernels. It also aims to be differentiable and work just as well with non-molecular physical simulations, though how much this impacts the ability to reach high simulation speeds remains to be seen.","category":"page"},{"location":"related/","page":"Related software","title":"Related software","text":"For differentiable simulations there are a number of related packages:","category":"page"},{"location":"related/","page":"Related software","title":"Related software","text":"Jax, M.D.\nTorchMD\nmdgrad\nDMFF\nTime Machine\nDiffTaichi","category":"page"},{"location":"related/","page":"Related software","title":"Related software","text":"In Julia there are a number of packages related to atomic simulation, some of which are involved with the JuliaMolSim organisation:","category":"page"},{"location":"related/","page":"Related software","title":"Related software","text":"AtomsBase.jl\nJuLIP.jl\nCellListMap.jl\nDFTK.jl\nACE.jl\nAtomicGraphNets.jl\nInteratomicPotentials.jl, Atomistic.jl and PotentialLearning.jl from the CESMIX project at MIT\nNBodySimulator.jl, DiffEqPhysics.jl and the SciML ecosystem more broadly","category":"page"},{"location":"development/#Development-documentation","page":"Development","title":"Development documentation","text":"","category":"section"},{"location":"development/#Running-tests","page":"Development","title":"Running tests","text":"","category":"section"},{"location":"development/","page":"Development","title":"Development","text":"The tests will automatically include multithreading and/or GPU tests if multiple threads and/or a CUDA-enabled GPU are available. test/runtests.jl does not include all the tests, see the test directory for more, though these extra tests do not need to be run for every change. Various environmental variables can be set to modify the tests:","category":"page"},{"location":"development/","page":"Development","title":"Development","text":"VISTESTS determines whether to run the GLMakie.jl plotting tests which will error on remote systems where a display is not available, default VISTESTS=1.\nGPUTESTS determines whether to run the GPU tests, default GPUTESTS=1.\nDEVICE determines which GPU to run the GPU tests on, default DEVICE=0.\nGROUP can be used to run a subset of the tests, options All/Protein/Zygote/NotZygote, default GROUP=All.","category":"page"},{"location":"development/","page":"Development","title":"Development","text":"The CI run does not carry out all tests - for example the GPU tests are not run - and this is reflected in the code coverage.","category":"page"},{"location":"development/#Benchmarks","page":"Development","title":"Benchmarks","text":"","category":"section"},{"location":"development/","page":"Development","title":"Development","text":"The benchmark directory contains some benchmarks for the package.","category":"page"},{"location":"examples/#Molly-examples","page":"Examples","title":"Molly examples","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"The best examples for learning how the package works are in the Molly documentation section. Here we give further examples showing what you can do with the package. Each is a self-contained block of code. Made something cool yourself? Make a PR to add it to this page.","category":"page"},{"location":"examples/#Simulated-annealing","page":"Examples","title":"Simulated annealing","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"You can change the thermostat temperature of a simulation by changing the simulator. Here we reduce the temperature of a simulation in stages from 300 K to 0 K.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing GLMakie\n\ndata_dir = joinpath(dirname(pathof(Molly)), \"..\", \"data\")\nff = MolecularForceField(\n joinpath(data_dir, \"force_fields\", \"ff99SBildn.xml\"),\n joinpath(data_dir, \"force_fields\", \"tip3p_standard.xml\"),\n joinpath(data_dir, \"force_fields\", \"his.xml\"),\n)\n\nsys = System(\n joinpath(data_dir, \"6mrr_equil.pdb\"),\n ff;\n loggers=(temp=TemperatureLogger(100),),\n)\n\nminimizer = SteepestDescentMinimizer()\nsimulate!(sys, minimizer)\n\ntemps = [300.0, 200.0, 100.0, 0.0]u\"K\"\nrandom_velocities!(sys, temps[1])\n\nfor temp in temps\n simulator = Langevin(\n dt=0.001u\"ps\",\n temperature=temp,\n friction=1.0u\"ps^-1\",\n )\n simulate!(sys, simulator, 5_000; run_loggers=:skipzero)\nend\n\nf = Figure(resolution=(600, 400))\nax = Axis(\n f[1, 1],\n xlabel=\"Step\",\n ylabel=\"Temperature\",\n title=\"Temperature change during simulated annealing\",\n)\nfor (i, temp) in enumerate(temps)\n lines!(\n ax,\n [5000 * i - 5000, 5000 * i],\n [ustrip(temp), ustrip(temp)],\n linestyle=\"--\",\n color=:orange,\n )\nend\nscatter!(\n ax,\n 100 .* (1:length(values(sys.loggers.temp))),\n ustrip.(values(sys.loggers.temp)),\n markersize=5,\n)\nsave(\"annealing.png\", f)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Annealing)","category":"page"},{"location":"examples/#Solar-system","page":"Examples","title":"Solar system","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"Orbits of the four closest planets to the sun can be simulated.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing GLMakie\n\n# Using get_body_barycentric_posvel from Astropy\ncoords = [\n SVector(-1336052.8665050615, 294465.0896030796 , 158690.88781384667)u\"km\",\n SVector(-58249418.70233503 , -26940630.286818042, -8491250.752464907 )u\"km\",\n SVector( 58624128.321813114, -81162437.2641475 , -40287143.05760552 )u\"km\",\n SVector(-99397467.7302648 , -105119583.06486066, -45537506.29775053 )u\"km\",\n SVector( 131714235.34070954, -144249196.60814604, -69730238.5084304 )u\"km\",\n]\n\nvelocities = [\n SVector(-303.86327859262457, -1229.6540090943934, -513.791218405548 )u\"km * d^-1\",\n SVector( 1012486.9596885007, -3134222.279236384 , -1779128.5093088674)u\"km * d^-1\",\n SVector( 2504563.6403826815, 1567163.5923297722, 546718.234192132 )u\"km * d^-1\",\n SVector( 1915792.9709661514, -1542400.0057833872, -668579.962254351 )u\"km * d^-1\",\n SVector( 1690083.43357355 , 1393597.7855017239, 593655.0037930267 )u\"km * d^-1\",\n]\n\nbody_masses = [\n 1.989e30u\"kg\",\n 0.330e24u\"kg\",\n 4.87e24u\"kg\" ,\n 5.97e24u\"kg\" ,\n 0.642e24u\"kg\",\n]\n\nboundary = CubicBoundary(1e9u\"km\")\n\n# Convert the gravitational constant to the appropriate units\ninter = Gravity(G=convert(typeof(1.0u\"km^3 * kg^-1 * d^-2\"), Unitful.G))\n\nsys = System(\n atoms=[Atom(mass=m) for m in body_masses],\n coords=coords .+ (SVector(5e8, 5e8, 5e8)u\"km\",),\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=(inter,),\n loggers=(coords=CoordinateLogger(typeof(1.0u\"km\"), 10),),\n force_units=u\"kg * km * d^-2\",\n energy_units=u\"kg * km^2 * d^-2\",\n)\n\nsimulator = Verlet(\n dt=0.1u\"d\",\n remove_CM_motion=false,\n)\n\nsimulate!(sys, simulator, 3650) # 1 year\n\nvisualize(\n sys.loggers.coords,\n boundary,\n \"sim_planets.mp4\";\n trails=5,\n color=[:yellow, :grey, :orange, :blue, :red],\n markersize=[0.25, 0.08, 0.08, 0.08, 0.08],\n transparency=false,\n)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Planet simulation)","category":"page"},{"location":"examples/#Agent-based-modelling","page":"Examples","title":"Agent-based modelling","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"Agent-based modelling (ABM) is conceptually similar to molecular dynamics. Julia has Agents.jl for ABM, but Molly can also be used to simulate arbitrary agent-based systems in continuous space. Here we simulate a toy SIR model for disease spread. This example shows how atom properties can be mutable, i.e. change during the simulation, and includes custom forces and loggers (see below for more info).","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing GLMakie\n\n@enum Status susceptible infected recovered\n\n# Custom atom type\nmutable struct Person\n i::Int\n status::Status\n mass::Float64\n σ::Float64\n ϵ::Float64\nend\n\n# Custom PairwiseInteraction\nstruct SIRInteraction <: PairwiseInteraction\n dist_infection::Float64\n prob_infection::Float64\n prob_recovery::Float64\nend\n\n# Custom force function\nfunction Molly.force(inter::SIRInteraction,\n vec_ij,\n coord_i,\n coord_j,\n atom_i,\n atom_j,\n boundary)\n if (atom_i.status == infected && atom_j.status == susceptible) ||\n (atom_i.status == susceptible && atom_j.status == infected)\n # Infect close people randomly\n r2 = sum(abs2, vec_ij)\n if r2 < inter.dist_infection^2 && rand() < inter.prob_infection\n atom_i.status = infected\n atom_j.status = infected\n end\n end\n # Workaround to obtain a self-interaction\n if atom_i.i == (atom_j.i - 1)\n # Recover randomly\n if atom_i.status == infected && rand() < inter.prob_recovery\n atom_i.status = recovered\n end\n end\n return zero(coord_i)\nend\n\n# Custom logger\nfunction fracs_SIR(s::System, neighbors=nothing; n_threads::Integer=Threads.nthreads())\n counts_sir = [\n count(p -> p.status == susceptible, s.atoms),\n count(p -> p.status == infected , s.atoms),\n count(p -> p.status == recovered , s.atoms)\n ]\n return counts_sir ./ length(s)\nend\n\nSIRLogger(n_steps) = GeneralObservableLogger(fracs_SIR, Vector{Float64}, n_steps)\n\ntemp = 1.0\nboundary = RectangularBoundary(10.0)\nn_steps = 1_000\nn_people = 500\nn_starting = 2\natoms = [Person(i, i <= n_starting ? infected : susceptible, 1.0, 0.1, 0.02) for i in 1:n_people]\ncoords = place_atoms(n_people, boundary; min_dist=0.1)\nvelocities = [random_velocity(1.0, temp; dims=2) for i in 1:n_people]\n\nlj = LennardJones(\n cutoff=DistanceCutoff(1.6),\n use_neighbors=true,\n force_units=NoUnits,\n energy_units=NoUnits,\n)\nsir = SIRInteraction(0.5, 0.06, 0.01)\npairwise_inters = (LennardJones=lj, SIR=sir)\nneighbor_finder = DistanceNeighborFinder(\n eligible=trues(n_people, n_people),\n n_steps=10,\n dist_cutoff=2.0,\n)\nsimulator = VelocityVerlet(\n dt=0.02,\n coupling=AndersenThermostat(temp, 5.0),\n)\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=pairwise_inters,\n neighbor_finder=neighbor_finder,\n loggers=(\n coords=CoordinateLogger(Float64, 10; dims=2),\n SIR=SIRLogger(10),\n ),\n force_units=NoUnits,\n energy_units=NoUnits,\n)\n\nsimulate!(sys, simulator, n_steps)\n\nvisualize(sys.loggers.coords, boundary, \"sim_agent.mp4\"; markersize=0.1)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Agent simulation)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"We can use the logger to plot the fraction of people susceptible, infected and recovered over the course of the simulation:","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using GLMakie\n\nf = Figure()\nax = Axis(f[1, 1], xlabel=\"Snapshot\", ylabel=\"Fraction\")\n\nlines!([l[1] for l in values(sys.loggers.SIR)], label=\"Susceptible\")\nlines!([l[2] for l in values(sys.loggers.SIR)], label=\"Infected\")\nlines!([l[3] for l in values(sys.loggers.SIR)], label=\"Recovered\")\naxislegend()","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Fraction SIR)","category":"page"},{"location":"examples/#Polymer-melt","page":"Examples","title":"Polymer melt","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"Here we use FENEBond, CosineAngle and LennardJones to simulate interacting polymers. We also analyse the end-to-end polymer distances and chain angles across the trajectory.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing GLMakie\nusing Colors\nusing LinearAlgebra\n\n# Simulate 10 polymers each consisting of 6 monomers\nn_polymers = 10\nn_monomers = 6\nn_atoms = n_monomers * n_polymers\nn_bonds_mon = n_monomers - 1\nn_bonds_tot = n_bonds_mon * n_polymers\nn_angles_mon = n_monomers - 2\nn_angles_tot = n_angles_mon * n_polymers\n\nstarting_length = 1.1u\"nm\"\nboundary = CubicBoundary(20.0u\"nm\")\n\n# Random placement of polymer centers at the start\nstart_coords = place_atoms(n_polymers, boundary; min_dist=6.0u\"nm\")\n\n# Polymers start almost completely extended\ncoords = []\nfor pol_i in 1:n_polymers\n for mon_i in 1:n_monomers\n push!(coords, start_coords[pol_i] .+ SVector(\n starting_length * (mon_i - 1 - n_monomers / 2),\n rand() * 0.1u\"nm\",\n rand() * 0.1u\"nm\",\n ))\n end\nend\ncoords = [coords...] # Ensure the array is concretely typed\n\n# Create FENEBonds between adjacent monomers\nbond_is, bond_js = Int[], Int[]\nfor pol_i in 1:n_polymers\n for bi in 1:n_bonds_mon\n push!(bond_is, (pol_i - 1) * n_monomers + bi )\n push!(bond_js, (pol_i - 1) * n_monomers + bi + 1)\n end\nend\n\nfene_k = 250.0u\"kJ * mol^-1 * nm^-2\"\nfene_r0 = 1.6u\"nm\"\nbonds = InteractionList2Atoms(\n bond_is,\n bond_js,\n [FENEBond(k=fene_k, r0=fene_r0, σ=1.0u\"nm\", ϵ=2.5u\"kJ * mol^-1\") for _ in 1:n_bonds_tot],\n)\n\n# Create CosineAngles between adjacent monomers\nangle_is, angle_js, angle_ks = Int[], Int[], Int[]\nfor pol_i in 1:n_polymers\n for bi in 1:n_angles_mon\n push!(angle_is, (pol_i - 1) * n_monomers + bi )\n push!(angle_js, (pol_i - 1) * n_monomers + bi + 1)\n push!(angle_ks, (pol_i - 1) * n_monomers + bi + 2)\n end\nend\n\nangles = InteractionList3Atoms(\n angle_is,\n angle_js,\n angle_ks,\n [CosineAngle(k=2.0u\"kJ * mol^-1\", θ0=0.0) for _ in 1:n_angles_tot],\n)\n\natoms = [Atom(mass=10.0u\"g/mol\", σ=1.0u\"nm\", ϵ=0.5u\"kJ * mol^-1\") for _ in 1:n_atoms]\n\n# Since we are using a generic pairwise Lennard-Jones potential too we need to\n# exclude adjacent monomers from the neighbor list\neligible = trues(n_atoms, n_atoms)\nfor pol_i in 1:n_polymers\n for mon_i in 1:n_bonds_mon\n i = (pol_i - 1) * n_monomers + mon_i\n j = (pol_i - 1) * n_monomers + mon_i + 1\n eligible[i, j] = false\n eligible[j, i] = false\n end\nend\n\nlj = LennardJones(\n cutoff=DistanceCutoff(5.0u\"nm\"),\n use_neighbors=true,\n)\nneighbor_finder = DistanceNeighborFinder(\n eligible=eligible,\n n_steps=10,\n dist_cutoff=5.5u\"nm\",\n)\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n pairwise_inters=(lj,),\n specific_inter_lists=(bonds, angles),\n neighbor_finder=neighbor_finder,\n loggers=(coords=CoordinateLogger(200),),\n)\n\nsim = Langevin(dt=0.002u\"ps\", temperature=300.0u\"K\", friction=1.0u\"ps^-1\")\n\nsimulate!(sys, sim, 100_000)\n\ncolors = distinguishable_colors(n_polymers, [RGB(1, 1, 1), RGB(0, 0, 0)]; dropseed=true)\n\nvisualize(\n sys.loggers.coords,\n boundary,\n \"sim_polymer.gif\";\n connections=zip(bond_is, bond_js),\n color=repeat(colors; inner=n_monomers),\n connection_color=repeat(colors; inner=n_bonds_mon),\n)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Polymer simulation)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"logged_coords = values(sys.loggers.coords)\nn_frames = length(logged_coords)\n\n# Calculate end-to-end polymer distances for second half of trajectory\nend_to_end_dists = Float64[]\nfor traj_coords in logged_coords[(n_frames ÷ 2):end]\n for pol_i in 1:n_polymers\n start_i = (pol_i - 1) * n_monomers + 1\n end_i = pol_i * n_monomers\n dist = norm(vector(traj_coords[start_i], traj_coords[end_i], boundary))\n push!(end_to_end_dists, ustrip(dist))\n end\nend\n\nf = Figure(resolution=(600, 400))\nax = Axis(\n f[1, 1],\n xlabel=\"End-to-end distance / nm\",\n ylabel=\"Density\",\n title=\"End-to-end polymer distance over the trajectory\",\n)\nhist!(ax, end_to_end_dists, normalization=:pdf)\nxlims!(ax, low=0)\nylims!(ax, low=0)\nsave(\"polymer_dist.png\", f)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Polymer distances)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"# Calculate angles to adjacent monomers for second half of trajectory\nchain_angles = Float64[]\nfor traj_coords in logged_coords[(n_frames ÷ 2):end]\n for pol_i in 1:n_polymers\n for mon_i in 2:(n_monomers - 1)\n ang = bond_angle(\n traj_coords[(pol_i - 1) * n_monomers + mon_i - 1],\n traj_coords[(pol_i - 1) * n_monomers + mon_i ],\n traj_coords[(pol_i - 1) * n_monomers + mon_i + 1],\n boundary,\n )\n push!(chain_angles, rad2deg(ang))\n end\n end\nend\n\nf = Figure(resolution=(600, 400))\nax = Axis(\n f[1, 1],\n xlabel=\"Angle with adjacent monomers / degrees\",\n ylabel=\"Density\",\n title=\"Chain angles over the trajectory\",\n)\nhist!(ax, chain_angles, normalization=:pdf)\nxlims!(ax, 0, 180)\nylims!(ax, low=0)\nsave(\"polymer_angle.png\", f)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Polymer angles)","category":"page"},{"location":"examples/#Machine-learning-potentials","page":"Examples","title":"Machine learning potentials","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"There is an example of using ACE potentials in Molly.","category":"page"},{"location":"examples/#Python-ASE-calculator","page":"Examples","title":"Python ASE calculator","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"ASECalculator can be used along with PythonCall.jl to use a Python ASE calculator with Molly. Here we simulate a dipeptide molecule in a vacuum with MACE-OFF23:","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing PythonCall # Python packages ase and mace need to be installed beforehand\nusing Downloads\n\nDownloads.download(\n \"https://raw.githubusercontent.com/noeblassel/SINEQSummerSchool2023/main/notebooks/dipeptide_nowater.pdb\",\n \"dipeptide_nowater.pdb\",\n)\n\ndata_dir = joinpath(dirname(pathof(Molly)), \"..\", \"data\")\nff = MolecularForceField(joinpath(data_dir, \"force_fields\", \"ff99SBildn.xml\"))\nsys = System(\"dipeptide_nowater.pdb\", ff; rename_terminal_res=false)\n\nmc = pyimport(\"mace.calculators\")\nase_calc = mc.mace_off(model=\"medium\", device=\"cuda\")\n\ncalc = ASECalculator(\n ase_calc=ase_calc,\n atoms=sys.atoms,\n coords=sys.coords,\n boundary=sys.boundary,\n atoms_data=sys.atoms_data,\n)\n\nsys = System(\n sys;\n general_inters=(calc,),\n loggers=(StructureWriter(20, \"mace_dipeptide.pdb\"),) # Every 10 fs\n)\npotential_energy(sys)\n\nminimizer = SteepestDescentMinimizer(log_stream=stdout)\nsimulate!(sys, minimizer)\n\ntemp = 298.0u\"K\"\nrandom_velocities!(sys, temp)\nsimulator = Langevin(\n dt=0.0005u\"ps\", # 0.5 fs\n temperature=temp,\n friction=1.0u\"ps^-1\",\n)\n\nsimulate!(deepcopy(sys), simulator, 5; run_loggers=false)\n@time simulate!(sys, simulator, 2000)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Another example using psi4 to get the potential energy of a water molecule:","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing PythonCall # Python packages ase and psi4 need to be installed beforehand\n\nbuild = pyimport(\"ase.build\")\npsi4 = pyimport(\"ase.calculators.psi4\")\n\npy_atoms = build.molecule(\"H2O\")\nase_calc = psi4.Psi4(\n atoms=py_atoms,\n method=\"b3lyp\",\n basis=\"6-311g_d_p_\",\n)\n\natoms = [Atom(mass=16.0u\"u\"), Atom(mass=1.0u\"u\"), Atom(mass=1.0u\"u\")]\ncoords = SVector{3, Float64}.(eachrow(pyconvert(Matrix, py_atoms.get_positions()))) * u\"Å\"\nboundary = CubicBoundary(100.0u\"Å\")\n\ncalc = ASECalculator(\n ase_calc=ase_calc,\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n elements=[\"O\", \"H\", \"H\"],\n)\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n general_inters=(calc,),\n energy_units=u\"eV\",\n force_units=u\"eV/Å\",\n)\n\npotential_energy(sys) # -2080.2391023908813 eV","category":"page"},{"location":"examples/#Density-functional-theory","page":"Examples","title":"Density functional theory","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"DFTK.jl can be used to calculate forces using density functional theory (DFT), allowing the simulation of quantum systems in Molly. This example uses the DFTK.jl tutorial to simulate two silicon atoms with atomic units. A general interaction is used since the whole force calculation is offloaded to DFTK.jl.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing DFTK\nimport AtomsCalculators\n\nstruct DFTKInteraction{L, A}\n lattice::L\n atoms::A\nend\n\n# Define lattice and atomic positions\na = 5.431u\"Å\" # Silicon lattice constant\nlattice = a / 2 * [[0 1 1.]; # Silicon lattice vectors\n [1 0 1.]; # specified column by column\n [1 1 0.]];\n\n# Load HGH pseudopotential for Silicon\nSi = ElementPsp(:Si, psp=load_psp(\"hgh/lda/Si-q4\"))\n\n# Specify type of atoms\natoms_dftk = [Si, Si]\n\ndftk_interaction = DFTKInteraction(lattice, atoms_dftk)\n\nfunction AtomsCalculators.forces(sys, inter::DFTKInteraction; kwargs...)\n # Select model and basis\n model = model_LDA(inter.lattice, inter.atoms, sys.coords)\n kgrid = [4, 4, 4] # k-point grid (Regular Monkhorst-Pack grid)\n Ecut = 7 # kinetic energy cutoff\n basis = PlaneWaveBasis(model; Ecut=Ecut, kgrid=kgrid)\n\n # Run the SCF procedure to obtain the ground state\n scfres = self_consistent_field(basis; tol=1e-5)\n\n return compute_forces_cart(scfres)\nend\n\natoms = fill(Atom(mass=28.0), 2)\ncoords = [SVector(1/8, 1/8, 1/8), SVector(-1/8, -1/8, -1/8)]\nvelocities = [randn(SVector{3, Float64}) * 0.1 for _ in 1:2]\nboundary = CubicBoundary(Inf)\nloggers = (coords=CoordinateLogger(Float64, 1),)\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n general_inters=(dftk_interaction,),\n loggers=loggers,\n force_units=NoUnits,\n energy_units=NoUnits,\n)\n\nsimulator = Verlet(dt=0.0005, remove_CM_motion=false)\n\nsimulate!(sys, simulator, 100)\n\nvalues(sys.loggers.coords)[end]\n# 2-element Vector{SVector{3, Float64}}:\n# [0.12060853912863925, 0.12292128337998731, 0.13100409788691614]\n# [-0.13352575661477334, -0.11473039463130282, -0.13189544838731393]","category":"page"},{"location":"examples/#Making-and-breaking-bonds","page":"Examples","title":"Making and breaking bonds","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"There is an example of mutable atom properties in the main documentation, but what if you want to make and break bonds during the simulation? In this case you can use a PairwiseInteraction to make, break and apply the bonds. The partners of the atom can be stored in the atom type. We make a logger to record when the bonds are present, allowing us to visualize them with the connection_frames keyword argument to visualize (this can take a while to plot).","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing GLMakie\nusing LinearAlgebra\n\nstruct BondableAtom\n i::Int\n mass::Float64\n σ::Float64\n ϵ::Float64\n partners::Set{Int}\nend\n\nstruct BondableInteraction <: PairwiseInteraction\n prob_formation::Float64\n prob_break::Float64\n dist_formation::Float64\n k::Float64\n r0::Float64\nend\n\nMolly.use_neighbors(::BondableInteraction) = true\n\nfunction Molly.force(inter::BondableInteraction,\n dr,\n coord_i,\n coord_j,\n atom_i,\n atom_j,\n boundary)\n # Break bonds randomly\n if atom_j.i in atom_i.partners && rand() < inter.prob_break\n delete!(atom_i.partners, atom_j.i)\n delete!(atom_j.partners, atom_i.i)\n end\n # Make bonds between close atoms randomly\n r2 = sum(abs2, dr)\n if r2 < inter.r0 * inter.dist_formation && rand() < inter.prob_formation\n push!(atom_i.partners, atom_j.i)\n push!(atom_j.partners, atom_i.i)\n end\n # Apply the force of a harmonic bond\n if atom_j.i in atom_i.partners\n c = inter.k * (norm(dr) - inter.r0)\n fdr = -c * normalize(dr)\n return fdr\n else\n return zero(coord_i)\n end\nend\n\nfunction bonds(sys::System, neighbors=nothing; n_threads::Integer=Threads.nthreads())\n bonds = BitVector()\n for i in 1:length(sys)\n for j in 1:(i - 1)\n push!(bonds, j in sys.atoms[i].partners)\n end\n end\n return bonds\nend\n\nBondLogger(n_steps) = GeneralObservableLogger(bonds, BitVector, n_steps)\n\nn_atoms = 200\nboundary = RectangularBoundary(10.0)\nn_steps = 2_000\ntemp = 1.0\n\natoms = [BondableAtom(i, 1.0, 0.1, 0.02, Set([])) for i in 1:n_atoms]\ncoords = place_atoms(n_atoms, boundary; min_dist=0.1)\nvelocities = [random_velocity(1.0, temp; dims=2) for i in 1:n_atoms]\npairwise_inters = (\n SoftSphere(\n cutoff=DistanceCutoff(2.0),\n use_neighbors=true,\n force_units=NoUnits,\n energy_units=NoUnits,\n ),\n BondableInteraction(0.1, 0.1, 1.1, 2.0, 0.1),\n)\nneighbor_finder = DistanceNeighborFinder(\n eligible=trues(n_atoms, n_atoms),\n n_steps=10,\n dist_cutoff=2.2,\n)\nsimulator = VelocityVerlet(\n dt=0.02,\n coupling=AndersenThermostat(temp, 5.0),\n)\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=pairwise_inters,\n neighbor_finder=neighbor_finder,\n loggers=(\n coords=CoordinateLogger(Float64, 20; dims=2),\n bonds=BondLogger(20),\n ),\n force_units=NoUnits,\n energy_units=NoUnits,\n)\n\nsimulate!(sys, simulator, n_steps)\n\nconnections = Tuple{Int, Int}[]\nfor i in 1:length(sys)\n for j in 1:(i - 1)\n push!(connections, (i, j))\n end\nend\n\nvisualize(\n sys.loggers.coords,\n boundary,\n \"sim_mutbond.mp4\";\n connections=connections,\n connection_frames=values(sys.loggers.bonds),\n markersize=0.1,\n)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Mutable bond simulation)","category":"page"},{"location":"examples/#Comparing-forces-to-AD","page":"Examples","title":"Comparing forces to AD","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"The force is the negative derivative of the potential energy with respect to position. MD packages, including Molly, implement the force functions directly for performance. However it is also possible to compute the forces using AD. Here we compare the two approaches for the Lennard-Jones potential and see that they give the same result.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing Zygote\nusing GLMakie\n\ninter = LennardJones(force_units=NoUnits, energy_units=NoUnits)\nboundary = CubicBoundary(5.0)\na1, a2 = Atom(σ=0.3, ϵ=0.5), Atom(σ=0.3, ϵ=0.5)\n\nfunction force_direct(dist)\n c1 = SVector(1.0, 1.0, 1.0)\n c2 = SVector(dist + 1.0, 1.0, 1.0)\n vec = vector(c1, c2, boundary)\n F = force(inter, vec, c1, c2, a1, a2, boundary)\n return F[1]\nend\n\nfunction force_grad(dist)\n grad = gradient(dist) do dist\n c1 = SVector(1.0, 1.0, 1.0)\n c2 = SVector(dist + 1.0, 1.0, 1.0)\n vec = vector(c1, c2, boundary)\n potential_energy(inter, vec, c1, c2, a1, a2, boundary)\n end\n return -grad[1]\nend\n\ndists = collect(0.2:0.01:1.2)\nforces_direct = force_direct.(dists)\nforces_grad = force_grad.(dists)\n\nf = Figure(resolution=(600, 400))\nax = Axis(\n f[1, 1],\n xlabel=\"Distance / nm\",\n ylabel=\"Force / kJ * mol^-1 * nm^-1\",\n title=\"Comparing gradients from direct calculation and AD\",\n)\nscatter!(ax, dists, forces_direct, label=\"Direct\", markersize=8)\nscatter!(ax, dists, forces_grad , label=\"AD\" , markersize=8, marker='x')\nxlims!(ax, low=0)\nylims!(ax, -6.0, 10.0)\naxislegend()\nsave(\"force_comparison.png\", f)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Force comparison)","category":"page"},{"location":"examples/#AtomsCalculators.jl-compatibility","page":"Examples","title":"AtomsCalculators.jl compatibility","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"The AtomsCalculators.jl package provides a consistent interface that allows forces, energies etc. to be calculated with different packages. Calculators can be used with a Molly System by giving them as general_inters during system setup. It is also possible to use a MollyCalculator to calculate properties on AtomsBase.jl systems:","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nimport AtomsBase\nusing AtomsBaseTesting\nusing AtomsCalculators\n\nab_sys = AtomsBase.AbstractSystem(\n make_test_system().system; \n bounding_box = [[1.54732, 0.0 , 0.0 ],\n [0.0 , 1.4654985, 0.0 ],\n [0.0 , 0.0 , 1.7928950]]u\"Å\",\n)\n\ncoul = Coulomb(coulomb_const=2.307e-21u\"kJ*Å\", force_units=u\"kJ/Å\", energy_units=u\"kJ\")\ncalc = MollyCalculator(pairwise_inters=(coul,), force_units=u\"kJ/Å\", energy_units=u\"kJ\")\n\nAtomsCalculators.potential_energy(ab_sys, calc)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"9.112207692184968e-21 kJ","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"AtomsCalculators.forces(ab_sys, calc)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"5-element Vector{SVector{3, Quantity{Float64, 𝐋 𝐌 𝐓^-2, Unitful.FreeUnits{(Å^-1, kJ), 𝐋 𝐌 𝐓^-2, nothing}}}}:\n [5.052086904272771e-21 kJ Å^-1, 1.0837307191961731e-20 kJ Å^-1, -5.366866699852613e-21 kJ Å^-1]\n [5.252901001053284e-22 kJ Å^-1, -2.3267009382813732e-21 kJ Å^-1, 9.276115314848821e-21 kJ Å^-1]\n [-8.613462805775053e-21 kJ Å^-1, 5.726650141840073e-21 kJ Å^-1, -2.072868074170469e-20 kJ Å^-1]\n [3.0360858013969523e-21 kJ Å^-1, -1.423725639552043e-20 kJ Å^-1, 1.681943212670848e-20 kJ Å^-1]\n [0.0 kJ Å^-1, 0.0 kJ Å^-1, 0.0 kJ Å^-1]","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"We can also convert the AtomsBase.jl system to a Molly System:","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"System(ab_sys; force_units=u\"kJ/Å\", energy_units=u\"kJ\")","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"System with 5 atoms, boundary CubicBoundary{Quantity{Float64, 𝐋, Unitful.FreeUnits{(Å,), 𝐋, nothing}}}(Quantity{Float64, 𝐋, Unitful.FreeUnits{(Å,), 𝐋, nothing}}[1.54732 Å, 1.4654985 Å, 1.792895 Å])","category":"page"},{"location":"examples/#Variations-of-the-Morse-potential","page":"Examples","title":"Variations of the Morse potential","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"The Morse potential for bonds has a parameter a that determines the width of the potential. It can also be compared to the harmonic bond potential.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing GLMakie\n\nboundary = CubicBoundary(5.0)\ndists = collect(0.12:0.005:2.0)\n\nfunction energies(inter)\n return map(dists) do dist\n c1 = SVector(1.0, 1.0, 1.0)\n c2 = SVector(dist + 1.0, 1.0, 1.0)\n potential_energy(inter, c1, c2, boundary)\n end\nend\n\nf = Figure(resolution=(600, 400))\nax = Axis(\n f[1, 1],\n xlabel=\"Distance / nm\",\n ylabel=\"Potential energy / kJ * mol^-1\",\n title=\"Variations of the Morse potential\",\n)\nlines!(\n ax,\n dists,\n energies(HarmonicBond(k=20_000.0, r0=0.2)),\n label=\"Harmonic\",\n)\nfor a in [2.5, 5.0, 10.0]\n lines!(\n ax,\n dists,\n energies(MorseBond(D=100.0, a=a, r0=0.2)),\n label=\"Morse a=$a nm^-1\",\n )\nend\nylims!(ax, 0.0, 120.0)\naxislegend(position=:rb)\nsave(\"morse.png\", f)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Morse)","category":"page"},{"location":"examples/#Variations-of-the-Mie-potential","page":"Examples","title":"Variations of the Mie potential","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"The Mie potential is parameterised by m describing the attraction and n describing the repulsion. When m=6 and n=12 this is equivalent to the Lennard-Jones potential.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing GLMakie\n\nboundary = CubicBoundary(5.0)\na1, a2 = Atom(σ=0.3, ϵ=0.5), Atom(σ=0.3, ϵ=0.5)\ndists = collect(0.2:0.005:0.8)\n\nfunction energies(m, n)\n inter = Mie(m=m, n=n)\n return map(dists) do dist\n c1 = SVector(1.0, 1.0, 1.0)\n c2 = SVector(dist + 1.0, 1.0, 1.0)\n vec = vector(c1, c2, boundary)\n potential_energy(inter, vec, c1, c2, a1, a2, boundary)\n end\nend\n\nf = Figure(resolution=(600, 400))\nax = Axis(\n f[1, 1],\n xlabel=\"Distance / nm\",\n ylabel=\"Potential energy / kJ * mol^-1\",\n title=\"Variations of the Mie potential\",\n)\nfor m in [4, 6]\n for n in [10, 12]\n lines!(\n ax,\n dists,\n energies(Float64(m), Float64(n)),\n label=\"m=$m, n=$n\",\n )\n end\nend\nxlims!(ax, low=0.2)\nylims!(ax, -0.6, 0.3)\naxislegend(position=:rb)\nsave(\"mie.png\", f)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Mie)","category":"page"},{"location":"examples/#Variations-of-the-soft-core-LJ-potential","page":"Examples","title":"Variations of the soft-core LJ potential","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"The soft-core Lennard-Jones potential is parameterised by alpha, lambda and p in addition to the standard Lennard-Jones parameters. These parameters shift the value of r_ij to left(r_ij^6 + sigma_ij alpha lambda^p right)^frac16. This gives a soft core, i.e. the potential does not diverge for r_ij rightarrow 0.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing GLMakie\n\nboundary = CubicBoundary(5.0)\na1, a2 = Atom(σ=0.3, ϵ=0.5), Atom(σ=0.3, ϵ=0.5)\ndists = collect(0.05:0.005:0.8)\n\nfunction energies(α, λ, p)\n inter = LennardJonesSoftCore(α=α, λ=λ, p=p)\n return map(dists) do dist\n c1 = SVector(1.0, 1.0, 1.0)\n c2 = SVector(dist + 1.0, 1.0, 1.0)\n vec = vector(c1, c2, boundary)\n potential_energy(inter, vec, c1, c2, a1, a2, boundary)\n end\nend\n\nf = Figure(resolution=(600, 400))\nax = Axis(\n f[1, 1],\n xlabel=\"Distance / nm\",\n ylabel=\"Potential energy / kJ * mol^-1\",\n title=\"Variations of the soft-core Lennard-Jones potential\",\n)\nfor λ in [0.8, 0.9]\n for α in [0.2, 0.4]\n for p in [2]\n lines!(\n ax,\n dists,\n energies(α, λ, p),\n label=\"α=$α, λ=$λ, p=$p\",\n )\n end\n end\nend\n\nlines!(ax, dists, energies(0, 1, 2), label=\"standard LJ potential\")\n\nylims!(-5, 25)\naxislegend(position=:rt)\nsave(\"lennard_jones_sc.png\", f)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Lennard-Jones Softcore)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"The form of the potential is approximately the same as standard Lennard-Jones for r_ij sigma_ij if some fractional values are used for lambda and alpha.","category":"page"},{"location":"examples/#Crystal-structures","page":"Examples","title":"Crystal structures","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"Molly can make use of SimpleCrystals.jl to generate crystal structures for simulation. All 3D Bravais lattices and most 2D Bravais lattices are supported as well as user-defined crystals through the SimpleCrystals API. The only unsupported crystal types are those with a triclinic 2D simulation domain or crystals with lattice angles larger than 90°.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Molly provides a constructor for System that takes in a Crystal struct:","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nimport SimpleCrystals\n\na = 0.52468u\"nm\" # Lattice parameter for FCC Argon at 10 K\natom_mass = 39.948u\"g/mol\"\ntemp = 10.0u\"K\"\nfcc_crystal = SimpleCrystals.FCC(a, atom_mass, SVector(4, 4, 4))\n\nn_atoms = length(fcc_crystal)\nvelocities = [random_velocity(atom_mass, temp) for i in 1:n_atoms]\n\nr_cut = 0.85u\"nm\"\nsys = System(\n fcc_crystal;\n velocities=velocities,\n pairwise_inters=(LennardJones(\n cutoff=ShiftedForceCutoff(r_cut),\n energy_units=u\"kJ * mol^-1\",\n force_units=u\"kJ * mol^-1 * nm^-1\",\n ),),\n loggers=(\n kinetic_eng=KineticEnergyLogger(100),\n pot_eng=PotentialEnergyLogger(100),\n ),\n energy_units=u\"kJ * mol^-1\",\n force_units=u\"kJ * mol^-1 * nm^-1\",\n)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Certain potentials such as LennardJones and Buckingham require extra atomic paramaters (e.g. σ) that are not implemented by the SimpleCrystals API. These paramaters must be added to the System manually by making use of the copy constructor:","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"σ = 0.34u\"nm\"\nϵ = (4.184 * 0.24037)u\"kJ * mol^-1\"\nupdated_atoms = []\n\nfor i in eachindex(sys)\n push!(updated_atoms, Atom(index=sys.atoms[i].index, charge=sys.atoms[i].charge,\n mass=sys.atoms[i].mass, σ=σ, ϵ=ϵ, solute=sys.atoms[i].solute))\nend\n\nsys = System(sys; atoms=[updated_atoms...])","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Now the system can be simulated using any of the available simulators:","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"simulator = Langevin(\n dt=2.0u\"fs\",\n temperature=temp,\n friction=1.0u\"ps^-1\",\n)\nsimulate!(sys, simulator, 200_000)","category":"page"},{"location":"examples/#Constrained-dynamics","page":"Examples","title":"Constrained dynamics","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"Molly supports the SHAKE and RATTLE constraint algorithms. The code below shows an example where molecules of hydrogen are randomly placed in a box and constrained during a simulation.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing Test\n\nr_cut = 8.5u\"Å\"\ntemp = 300.0u\"K\"\natom_mass = 1.00794u\"g/mol\"\n\nn_atoms_half = 200\natoms = [Atom(index=i, mass=atom_mass, σ=2.8279u\"Å\", ϵ=0.074u\"kcal* mol^-1\") for i in 1:n_atoms_half]\nmax_coord = 200.0u\"Å\"\ncoords = [max_coord .* rand(SVector{3}) for i in 1:n_atoms_half]\nboundary = CubicBoundary(200.0u\"Å\")\nlj = LennardJones(\n cutoff=ShiftedPotentialCutoff(r_cut),\n use_neighbors=true,\n energy_units=u\"kcal * mol^-1\",\n force_units=u\"kcal * mol^-1 * Å^-1\",\n)\n\n# Add bonded atoms\nbond_length = 0.74u\"Å\" # Hydrogen bond length\nconstraints = []\nfor j in 1:n_atoms_half\n push!(atoms, Atom(index=(j + n_atoms_half), mass=atom_mass, σ=2.8279u\"Å\", ϵ=0.074u\"kcal* mol^-1\"))\n push!(coords, coords[j] .+ SVector(bond_length, 0.0u\"Å\", 0.0u\"Å\"))\n push!(constraints, DistanceConstraint(j, j + n_atoms_half, bond_length))\nend\n\nshake = SHAKE_RATTLE([constraints...], length(atoms), 1e-8u\"Å\", 1e-8u\"Å^2 * ps^-1\")\n\nneighbor_finder = DistanceNeighborFinder(\n eligible=trues(length(atoms), length(atoms)),\n dist_cutoff=1.5*r_cut,\n)\ndisable_constrained_interactions!(neighbor_finder, shake.clusters)\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n pairwise_inters=(lj,),\n neighbor_finder=neighbor_finder,\n constraints=(shake,),\n energy_units=u\"kcal * mol^-1\",\n force_units=u\"kcal * mol^-1 * Å^-1\",\n)\n\nrandom_velocities!(sys, temp)\n\nsimulator = VelocityVerlet(dt=0.001u\"ps\")\n\nsimulate!(sys, simulator, 10_000)\n\n# Check that the constraints are satisfied at the end of the simulation\n@test check_position_constraints(sys, shake)\n@test check_velocity_constraints(sys, shake)","category":"page"},{"location":"differentiable/#Differentiable-simulation-with-Molly","page":"Differentiable simulation","title":"Differentiable simulation with Molly","text":"","category":"section"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"note: Note\nThere are still many rough edges when taking gradients through simulations. Please open an issue if you run into an error and remember the golden rule of AD: check your gradients against finite differencing if you want to make sure they are correct.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"In the last few years, the deep learning revolution has broadened to include the paradigm of differentiable programming. The concept of using automatic differentiation (AD) to obtain exact gradients through physical simulations has many interesting applications, including parameterising force fields and training neural networks to describe atomic potentials.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"There are some projects that explore differentiable molecular simulations - see Related software. However Julia provides a strong suite of AD tools, with Zygote.jl and Enzyme.jl allowing source-to-source transformations for much of the language. With Molly you can use the power of Zygote and Enzyme to obtain gradients through molecular simulations, even in the presence of complex interactions such as implicit solvation and stochasticity such as Langevin dynamics or the Andersen thermostat. Reverse mode AD can be used on the CPU with multithreading and on the GPU; performance is typically within an order of magnitude of the primal run. Forward mode AD can also be used on the CPU. Pairwise, specific and general interactions work, along with neighbor lists, and the same abstractions for running simulations are used as in the main package.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Differentiable simulation does not currently work with units and some components of the package. This is mentioned in the relevant docstrings. It is memory intensive on the GPU so using gradient checkpointing will likely be required for larger simulations.","category":"page"},{"location":"differentiable/#Pairwise-interactions","page":"Differentiable simulation","title":"Pairwise interactions","text":"","category":"section"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"First, we show how taking gradients through a simulation can be used to optimise an atom property in a Lennard-Jones fluid. In this type of simulation each atom has a σ value that determines how close it likes to get to other atoms. We are going to find the σ value that results in a desired distance of each atom to its closest neighbor. First we need a function to obtain the mean distance of each atom to its closest neighbor:","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"using Molly\n\nfunction mean_min_separation(final_coords, boundary)\n n_atoms = length(final_coords)\n sum_dists = 0.0\n for i in 1:n_atoms\n min_dist = 100.0\n for j in 1:n_atoms\n i == j && continue\n dist = sqrt(sum(abs2, vector(final_coords[i], final_coords[j], boundary)))\n min_dist = min(dist, min_dist)\n end\n sum_dists += min_dist\n end\n return sum_dists / n_atoms\nend","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Now we can set up and run the simulation in a similar way to that described in the Molly documentation. The difference is that we wrap the simulation in a loss function. This returns a single value that we want to obtain gradients with respect to, in this case the difference between the value of the above function at the end of the simulation and a target distance. The Zygote.ignore() block allows us to ignore code for the purposes of obtaining gradients; you could add the visualize function there for example.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"using Zygote\nusing Format\n\ndist_true = 0.5\nscale_σ_to_dist = 2 ^ (1 / 6)\nσtrue = dist_true / scale_σ_to_dist\n\nn_atoms = 50\nn_steps = 500\natom_mass = 10.0\nboundary = CubicBoundary(3.0)\ntemp = 1.0\nneighbor_finder = DistanceNeighborFinder(\n eligible=trues(n_atoms, n_atoms),\n n_steps=10,\n dist_cutoff=1.8,\n)\nlj = LennardJones(\n cutoff=DistanceCutoff(1.5),\n use_neighbors=true,\n force_units=NoUnits,\n energy_units=NoUnits,\n)\npairwise_inters = (lj,)\ncoords = place_atoms(n_atoms, boundary; min_dist=0.6)\nvelocities = [random_velocity(atom_mass, temp) for i in 1:n_atoms]\nsimulator = VelocityVerlet(\n dt=0.02,\n coupling=RescaleThermostat(temp),\n)\n\nfunction loss(σ, coords, velocities)\n atoms = [Atom(0, 0.0, atom_mass, σ, 0.2, false) for i in 1:n_atoms]\n loggers = (coords=CoordinateLogger(Float64, 10),)\n\n sys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=pairwise_inters,\n neighbor_finder=neighbor_finder,\n loggers=loggers,\n force_units=NoUnits,\n energy_units=NoUnits,\n )\n\n mms_start = mean_min_separation(Array(sys.coords), boundary)\n simulate!(sys, simulator, n_steps)\n mms_end = mean_min_separation(Array(sys.coords), boundary)\n loss_val = abs(mms_end - dist_true)\n\n Zygote.ignore() do\n printfmt(\"σ {:6.3f} | Mean min sep expected {:6.3f} | Mean min sep end {:6.3f} | Loss {:6.3f} | \",\n σ, σ * (2 ^ (1 / 6)), mms_end, loss_val)\n end\n\n return loss_val\nend","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"We can obtain the gradient of loss with respect to the atom property σ.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"grad = gradient(loss, σtrue, coords, velocities)[1]","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"This gradient can be used in a training loop to optimise σ, starting from an arbitrary value.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"function train()\n σlearn = 0.60 / scale_σ_to_dist\n n_epochs = 15\n\n for epoch_n in 1:n_epochs\n printfmt(\"Epoch {:>2} | \", epoch_n)\n coords = place_atoms(n_atoms, boundary; min_dist=0.6)\n velocities = [random_velocity(atom_mass, temp) for i in 1:n_atoms]\n grad = gradient(loss, σlearn, coords, velocities)[1]\n printfmt(\"Grad {:6.3f}\\n\", grad)\n σlearn -= grad * 1e-2\n end\nend\n\ntrain()","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Epoch 1 | σ 0.535 | Mean min sep expected 0.600 | Mean min sep end 0.587 | Loss 0.087 | Grad 0.793\nEpoch 2 | σ 0.527 | Mean min sep expected 0.591 | Mean min sep end 0.581 | Loss 0.081 | Grad 1.202\nEpoch 3 | σ 0.515 | Mean min sep expected 0.578 | Mean min sep end 0.568 | Loss 0.068 | Grad 1.558\nEpoch 4 | σ 0.499 | Mean min sep expected 0.560 | Mean min sep end 0.551 | Loss 0.051 | Grad 0.766\nEpoch 5 | σ 0.491 | Mean min sep expected 0.552 | Mean min sep end 0.543 | Loss 0.043 | Grad 1.068\nEpoch 6 | σ 0.481 | Mean min sep expected 0.540 | Mean min sep end 0.531 | Loss 0.031 | Grad 0.757\nEpoch 7 | σ 0.473 | Mean min sep expected 0.531 | Mean min sep end 0.526 | Loss 0.026 | Grad 0.781\nEpoch 8 | σ 0.465 | Mean min sep expected 0.522 | Mean min sep end 0.518 | Loss 0.018 | Grad 1.549\nEpoch 9 | σ 0.450 | Mean min sep expected 0.505 | Mean min sep end 0.504 | Loss 0.004 | Grad 0.030\nEpoch 10 | σ 0.450 | Mean min sep expected 0.505 | Mean min sep end 0.504 | Loss 0.004 | Grad 0.066\nEpoch 11 | σ 0.449 | Mean min sep expected 0.504 | Mean min sep end 0.503 | Loss 0.003 | Grad 0.313\nEpoch 12 | σ 0.446 | Mean min sep expected 0.500 | Mean min sep end 0.501 | Loss 0.001 | Grad 0.636\nEpoch 13 | σ 0.439 | Mean min sep expected 0.493 | Mean min sep end 0.497 | Loss 0.003 | Grad -0.181\nEpoch 14 | σ 0.441 | Mean min sep expected 0.495 | Mean min sep end 0.498 | Loss 0.002 | Grad -0.758\nEpoch 15 | σ 0.449 | Mean min sep expected 0.504 | Mean min sep end 0.503 | Loss 0.003 | Grad 0.281","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"The final value we get is 0.449, close to the theoretical value of 0.445 if all atoms have a neighbor at the minimum pairwise energy distance. The RDF looks as follows, with the purple line corresponding to the desired distance to the closest neighbor. (Image: LJ RDF)","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"To make this run on the GPU the appropriate objects should be transferred to the GPU with CuArray: coords, velocities, atoms and the eligible matrix for the neighbor finder. If using custom interactions or some built-in interactions you may need to define methods of zero and + for your interaction type.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"It is common to require a loss function formed from values throughout a simulation. In this case it is recommended to split up the simulation into a set of short simulations in the loss function, each starting from the previous final coordinates and velocities. This runs an identical simulation but makes the intermediate coordinates and velocities available for use in calculating the final loss. For example, the RMSD could be calculated from the coordinates every 100 steps and added to a variable that is then divided by the number of chunks to get a loss value corresponding to the mean RMSD over the simulation. Loggers are ignored for gradient calculation and should not be used in the loss function.","category":"page"},{"location":"differentiable/#Specific-interactions","page":"Differentiable simulation","title":"Specific interactions","text":"","category":"section"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Next we look at obtaining gradients through simulations with specific interactions, e.g. bonds or angles between specified atoms. We will simulate two triatomic molecules and search for a minimum energy bond angle that gives a desired distance between the atoms at the end of the simulation.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"using Molly\nusing Zygote\nusing Format\nusing LinearAlgebra\n\ndist_true = 1.0\n\nn_steps = 150\natom_mass = 10.0\nboundary = CubicBoundary(3.0)\ntemp = 0.05\ncoords = [\n SVector(0.8, 0.75, 1.5), SVector(1.5, 0.70, 1.5), SVector(2.3, 0.75, 1.5),\n SVector(0.8, 2.25, 1.5), SVector(1.5, 2.20, 1.5), SVector(2.3, 2.25, 1.5),\n]\nn_atoms = length(coords)\nvelocities = zero(coords)\nsimulator = VelocityVerlet(\n dt=0.05,\n coupling=BerendsenThermostat(temp, 0.5),\n)\n\nfunction loss(θ)\n atoms = [Atom(0, 0.0, atom_mass, 0.0, 0.0, false) for i in 1:n_atoms]\n loggers = (coords=CoordinateLogger(Float64, 2),)\n specific_inter_lists = (\n InteractionList2Atoms(\n [1, 2, 4, 5],\n [2, 3, 5, 6],\n [HarmonicBond(100.0, 0.7) for _ in 1:4],\n ),\n InteractionList3Atoms(\n [1, 4],\n [2, 5],\n [3, 6],\n [HarmonicAngle(10.0, θ), HarmonicAngle(10.0, θ)],\n ),\n )\n\n sys = System(\n atoms=atoms,\n coords=deepcopy(coords),\n boundary=boundary,\n velocities=deepcopy(velocities),\n specific_inter_lists=specific_inter_lists,\n loggers=loggers,\n force_units=NoUnits,\n energy_units=NoUnits,\n )\n\n simulate!(sys, simulator, n_steps)\n\n d1 = norm(vector(sys.coords[1], sys.coords[3], boundary))\n d2 = norm(vector(sys.coords[4], sys.coords[6], boundary))\n dist_end = 0.5 * (d1 + d2)\n loss_val = abs(dist_end - dist_true)\n\n Zygote.ignore() do\n printfmt(\"θ {:5.1f}° | Final dist {:4.2f} | Loss {:5.3f} | \",\n rad2deg(θ), dist_end, loss_val)\n end\n\n return loss_val\nend\n\nfunction train()\n θlearn = deg2rad(110.0)\n n_epochs = 20\n\n for epoch_n in 1:n_epochs\n printfmt(\"Epoch {:>2} | \", epoch_n)\n grad = gradient(loss, θlearn)[1]\n printfmt(\"Grad {:6.3f}\\n\", round(grad; digits=2))\n θlearn -= grad * 0.1\n end\nend\n\ntrain()","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Epoch 1 | θ 110.0° | Final dist 1.16 | Loss 0.155 | Grad 0.410\nEpoch 2 | θ 107.7° | Final dist 1.14 | Loss 0.138 | Grad 0.430\nEpoch 3 | θ 105.2° | Final dist 1.12 | Loss 0.119 | Grad 0.450\nEpoch 4 | θ 102.6° | Final dist 1.10 | Loss 0.099 | Grad 0.470\nEpoch 5 | θ 100.0° | Final dist 1.08 | Loss 0.077 | Grad 0.490\nEpoch 6 | θ 97.2° | Final dist 1.05 | Loss 0.049 | Grad 0.710\nEpoch 7 | θ 93.1° | Final dist 1.01 | Loss 0.012 | Grad 0.520\nEpoch 8 | θ 90.1° | Final dist 0.98 | Loss 0.015 | Grad -0.540\nEpoch 9 | θ 93.2° | Final dist 1.01 | Loss 0.013 | Grad 0.520\nEpoch 10 | θ 90.2° | Final dist 0.99 | Loss 0.015 | Grad -0.540\nEpoch 11 | θ 93.3° | Final dist 1.01 | Loss 0.014 | Grad 0.520\nEpoch 12 | θ 90.3° | Final dist 0.99 | Loss 0.014 | Grad -0.540\nEpoch 13 | θ 93.4° | Final dist 1.01 | Loss 0.015 | Grad 0.520\nEpoch 14 | θ 90.4° | Final dist 0.99 | Loss 0.013 | Grad -0.540\nEpoch 15 | θ 93.5° | Final dist 1.02 | Loss 0.016 | Grad 0.520\nEpoch 16 | θ 90.5° | Final dist 0.99 | Loss 0.012 | Grad -0.540\nEpoch 17 | θ 93.6° | Final dist 1.02 | Loss 0.016 | Grad 0.520\nEpoch 18 | θ 90.6° | Final dist 0.99 | Loss 0.011 | Grad -0.530\nEpoch 19 | θ 93.7° | Final dist 1.02 | Loss 0.017 | Grad 0.520\nEpoch 20 | θ 90.7° | Final dist 0.99 | Loss 0.010 | Grad -0.530","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"The final value we get is 90.7°, close to the theoretical value of 91.2° which can be calculated with trigonometry. The final simulation looks like this: (Image: Angle simulation) In the presence of other forces this value would not be so trivially obtainable. We can record the gradients for different values of θ:","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"θs = collect(0:3:180)[2:end]\ngrads = [gradient(loss, deg2rad(θ))[1] for θ in θs]","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"The plot of these shows that the gradient has the expected sign either side of the correct value: (Image: Angle gradient)","category":"page"},{"location":"differentiable/#Neural-network-potentials","page":"Differentiable simulation","title":"Neural network potentials","text":"","category":"section"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Since gradients can be computed with Zygote, Flux models can also be incorporated into simulations. Here we show a neural network in the force function, though they can also be used in other parts of the simulation. This example also shows how gradients for multiple parameters can be obtained, in this case the parameters of the neural network. The jump from single to multiple parameters is important because single parameters can be optimised using finite differencing, whereas differentiable simulation is well-placed to optimise many parameters simultaneously.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"We set up three pseudo-atoms and train a network to imitate the Julia logo by moving the bottom two atoms:","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"using Molly\nusing GLMakie\nusing Zygote\nusing Flux\nimport AtomsCalculators\nusing Format\nusing LinearAlgebra\n\ndist_true = 1.0f0\n\nmodel = Chain(\n Dense(1, 5, relu),\n Dense(5, 1, tanh),\n)\nps = Flux.params(model)\n\nstruct NNBonds end\n\nfunction AtomsCalculators.forces(sys, inter::NNBonds; kwargs...)\n vec_ij = vector(sys.coords[1], sys.coords[3], sys.boundary)\n dist = norm(vec_ij)\n f = model([dist])[1] * normalize(vec_ij)\n fs = [f, zero(f), -f]\n return fs\nend\n\nn_steps = 400\nmass = 10.0f0\nboundary = CubicBoundary(5.0f0)\ntemp = 0.01f0\ncoords = [\n SVector(2.3f0, 2.07f0, 0.0f0),\n SVector(2.5f0, 2.93f0, 0.0f0),\n SVector(2.7f0, 2.07f0, 0.0f0),\n]\nn_atoms = length(coords)\nvelocities = zero(coords)\nsimulator = VelocityVerlet(\n dt=0.02f0,\n coupling=BerendsenThermostat(temp, 0.5f0),\n)\n\nfunction loss()\n atoms = [Atom(0, 0.0f0, mass, 0.0f0, 0.0f0, false) for i in 1:n_atoms]\n loggers = (coords=CoordinateLogger(Float32, 10),)\n general_inters = (NNBonds(),)\n\n sys = System(\n atoms=atoms,\n coords=deepcopy(coords),\n boundary=boundary,\n velocities=deepcopy(velocities),\n general_inters=general_inters,\n loggers=loggers,\n force_units=NoUnits,\n energy_units=NoUnits,\n )\n\n simulate!(sys, simulator, n_steps)\n\n dist_end = (norm(vector(sys.coords[1], sys.coords[2], boundary)) +\n norm(vector(sys.coords[2], sys.coords[3], boundary)) +\n norm(vector(sys.coords[3], sys.coords[1], boundary))) / 3\n loss_val = abs(dist_end - dist_true)\n\n Zygote.ignore() do\n printfmt(\"Dist end {:6.3f} | Loss {:6.3f}\\n\", dist_end, loss_val)\n visualize(sys.loggers.coords, boundary, \"sim.mp4\"; show_boundary=false)\n end\n\n return loss_val\nend","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Before training the result looks like this: (Image: Logo before)","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"function train()\n n_epochs = 20\n opt = ADAM(0.02, (0.9, 0.999))\n\n for epoch_n in 1:n_epochs\n printfmt(\"Epoch {:>2} | \", epoch_n)\n Flux.train!(loss, ps, ((),), opt)\n end\nend\n\ntrain()","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Epoch 1 | Dist end 0.757 | Loss 0.243\nEpoch 2 | Dist end 0.773 | Loss 0.227\nEpoch 3 | Dist end 0.794 | Loss 0.206\nEpoch 4 | Dist end 0.817 | Loss 0.183\nEpoch 5 | Dist end 0.843 | Loss 0.157\nEpoch 6 | Dist end 0.870 | Loss 0.130\nEpoch 7 | Dist end 0.898 | Loss 0.102\nEpoch 8 | Dist end 0.927 | Loss 0.073\nEpoch 9 | Dist end 0.957 | Loss 0.043\nEpoch 10 | Dist end 0.988 | Loss 0.012\nEpoch 11 | Dist end 1.018 | Loss 0.018\nEpoch 12 | Dist end 1.038 | Loss 0.038\nEpoch 13 | Dist end 1.050 | Loss 0.050\nEpoch 14 | Dist end 1.055 | Loss 0.055\nEpoch 15 | Dist end 1.054 | Loss 0.054\nEpoch 16 | Dist end 1.049 | Loss 0.049\nEpoch 17 | Dist end 1.041 | Loss 0.041\nEpoch 18 | Dist end 1.030 | Loss 0.030\nEpoch 19 | Dist end 1.017 | Loss 0.017\nEpoch 20 | Dist end 1.003 | Loss 0.003","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"After training it looks much better: (Image: Logo after) You could replace the simple network here with a much more complicated model and it would theoretically be able to train, even if it might prove practically difficult (see discussion below).","category":"page"},{"location":"differentiable/#Biomolecular-force-fields","page":"Differentiable simulation","title":"Biomolecular force fields","text":"","category":"section"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Molly was used to train the GB99dms force field for implicit solvent molecular dynamics of proteins. This involved doing differentiable simulations of one million steps with a loss function based on the residue-residue distance match to explicit solvent simulations. The code is available.","category":"page"},{"location":"differentiable/#Molecular-loss-functions","page":"Differentiable simulation","title":"Molecular loss functions","text":"","category":"section"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Ultimately, you need some objective function in order to calculate the gradient for each parameter. Here are some ideas for loss functions suitable for differentiable molecular simulations:","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"The distance between atoms at the end of the simulation compared to some reference state. This loss is used in the examples given here, is physically reasonable, and has obvious bounds.\nThe distance between atoms throughout the simulation.\nThe radial distribution function of atoms.\nRMSD between atoms and a reference state - this would be suitable for macromolecules.\ndRMSD, the distance between a distance map and a reference distance map.\nThe radius of gyration of a molecule.\nThe flexibility of a set of atoms over the simulation.\nSupramolecular geometry, for example assembly of molecules into straight fibres.\nThe correlation of different velocities over the simulation.\nThe energy of the system.\nThe temperature of the system.\nSome measure of phase change or a critical point.\nA combination of the above, for example to obtain a force field relevant to both ordered and disordered proteins.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Some of these are currently not possible in Molly as the loggers are ignored for gradient purposes, but this will hopefully change in future.","category":"page"},{"location":"differentiable/#Tips-and-tricks","page":"Differentiable simulation","title":"Tips and tricks","text":"","category":"section"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"The magnitude of gradients may be less important than the sign. Consider sampling gradients across different sources of stochasticity, such as starting velocities and conformations.\nExploding gradients prove a problem when using the velocity Verlet integrator in the NVE ensemble. This is why the velocity rescaling and Berendsen thermostats were used in the above examples. Langevin dynamics also seems to work. It is likely that the development of suitable simulation strategies and thermostats will be necessary to unlock the potential of differentiable simulation.\nDo you really need a neural network to describe your potential? Think about learning a smaller number of physically-meaningful parameters before you put in a large neural network and expect it to learn. Whilst it is true that neural networks are universal function approximators, it does not follow that you will be able to train one by differentiating through a long simulation. A 1,000-step simulation with a 10-layer network at each step is analogous to training a 10,000 layer network (with shared weights).\nForward mode AD holds much promise for differentiable simulation, provided that the number of parameters is small, because the memory requirement is constant in the number of simulation steps. However, if the code runs slower than non-differentiable alternatives then the best approach is likely to use finite differencing with the simulation as a black box. Adjoint sensitivity is another approach to getting gradients which is not yet available in Molly.jl.","category":"page"},{"location":"","page":"Home","title":"Home","text":"\"Molly","category":"page"},{"location":"","page":"Home","title":"Home","text":"(Image: Build status) (Image: Coverage status) (Image: Latest release) (Image: License) (Image: Documentation stable) (Image: Documentation dev)","category":"page"},{"location":"","page":"Home","title":"Home","text":"Much of science can be explained by the movement and interaction of molecules. Molecular dynamics (MD) is a computational technique used to explore these phenomena, from noble gases to biological macromolecules. Molly.jl is a pure Julia package for MD, and for the simulation of physical systems more broadly.","category":"page"},{"location":"","page":"Home","title":"Home","text":"The package is described in a talk at JuliaCon 2024 and an earlier talk at Enzyme Conference 2023. Slides are also available for a tutorial in September 2023.","category":"page"},{"location":"","page":"Home","title":"Home","text":"Implemented features include:","category":"page"},{"location":"","page":"Home","title":"Home","text":"Non-bonded interactions - Lennard-Jones Van der Waals/repulsion force, electrostatic Coulomb potential and reaction field, gravitational potential, soft sphere potential, Mie potential, Buckingham potential, soft core variants.\nBonded interactions - harmonic and Morse bonds, bond angles, torsion angles, harmonic position restraints, FENE bonds.\nInterface to allow definition of new interactions, simulators, thermostats, neighbor finders, loggers etc.\nRead in OpenMM force field files and coordinate files supported by Chemfiles.jl. There is also experimental support for Gromacs files.\nVerlet, velocity Verlet, Störmer-Verlet, flexible Langevin and Nosé-Hoover integrators.\nAndersen, Berendsen and velocity rescaling thermostats.\nMonte Carlo barostat and variants.\nSteepest descent energy minimization.\nReplica exchange molecular dynamics.\nMonte Carlo simulation.\nPeriodic, triclinic and infinite boundary conditions.\nConstraints with SHAKE and RATTLE\nFlexible loggers to track arbitrary properties throughout simulations.\nCutoff algorithms for non-bonded interactions.\nVarious neighbor list implementations to speed up the calculation of non-bonded forces, including the use of CellListMap.jl.\nImplicit solvent GBSA methods.\nUnitful.jl compatibility so numbers have physical meaning.\nSet up crystal systems using SimpleCrystals.jl.\nAutomatic multithreading.\nGPU acceleration on CUDA-enabled devices.\nRun with Float64, Float32 or other float types.\nSome analysis functions, e.g. RDF.\nVisualise simulations as animations with Makie.jl.\nCompatibility with AtomsBase.jl and AtomsCalculators.jl.\nInterface to use Python ASE calculators.\nDifferentiable molecular simulation. This is a unique feature of the package and the focus of its current development.","category":"page"},{"location":"","page":"Home","title":"Home","text":"Features not yet implemented include:","category":"page"},{"location":"","page":"Home","title":"Home","text":"High GPU performance.\nEwald or particle mesh Ewald summation.\nFull support for constrained bonds and angles.\nProtein preparation - solvent box, add hydrogens etc.\nSimulators such as metadynamics.\nQuantum mechanical modelling.\nDomain decomposition algorithms.\nAlchemical free energy calculations.\nHigh test coverage.\nAPI stability.","category":"page"},{"location":"#Installation","page":"Home","title":"Installation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Julia is required, with Julia v1.9 or later required to get the latest version of Molly. It is recommended to run on the current stable Julia release for the best performance. Install Molly from the Julia REPL. Enter the package mode by pressing ] and run add Molly.","category":"page"},{"location":"#Usage","page":"Home","title":"Usage","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Some examples are given here, see the documentation for more on how to use the package.","category":"page"},{"location":"","page":"Home","title":"Home","text":"Simulation of a Lennard-Jones fluid:","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Molly\n\nn_atoms = 100\nboundary = CubicBoundary(2.0u\"nm\")\ntemp = 298.0u\"K\"\natom_mass = 10.0u\"g/mol\"\n\natoms = [Atom(mass=atom_mass, σ=0.3u\"nm\", ϵ=0.2u\"kJ * mol^-1\") for i in 1:n_atoms]\ncoords = place_atoms(n_atoms, boundary; min_dist=0.3u\"nm\")\nvelocities = [random_velocity(atom_mass, temp) for i in 1:n_atoms]\npairwise_inters = (LennardJones(),)\nsimulator = VelocityVerlet(\n dt=0.002u\"ps\",\n coupling=AndersenThermostat(temp, 1.0u\"ps\"),\n)\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=pairwise_inters,\n loggers=(temp=TemperatureLogger(100),),\n)\n\nsimulate!(sys, simulator, 10_000)","category":"page"},{"location":"","page":"Home","title":"Home","text":"Simulation of a protein:","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Molly\n\nsys = System(\n joinpath(dirname(pathof(Molly)), \"..\", \"data\", \"5XER\", \"gmx_coords.gro\"),\n joinpath(dirname(pathof(Molly)), \"..\", \"data\", \"5XER\", \"gmx_top_ff.top\");\n loggers=(\n temp=TemperatureLogger(10),\n writer=StructureWriter(10, \"traj_5XER_1ps.pdb\"),\n ),\n)\n\ntemp = 298.0u\"K\"\nrandom_velocities!(sys, temp)\nsimulator = VelocityVerlet(\n dt=0.0002u\"ps\",\n coupling=AndersenThermostat(temp, 1.0u\"ps\"),\n)\n\nsimulate!(sys, simulator, 5_000)","category":"page"},{"location":"","page":"Home","title":"Home","text":"The above 1 ps simulation looks something like this when you view it in VMD: (Image: MD simulation)","category":"page"},{"location":"#Contributing","page":"Home","title":"Contributing","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Contributions are very welcome including reporting bugs, fixing bugs, adding new features, improving performance, adding tests and improving documentation. Feel free to open an issue or use the channels below to discuss your contribution. New features will generally require tests to be added as well. See the roadmap issue for some discussion of recent progress and future plans.","category":"page"},{"location":"","page":"Home","title":"Home","text":"Join the #juliamolsim channel on the Julia Slack, the #molly channel on the JuliaMolSim Zulip or post on the Julia Discourse to discuss the usage and development of Molly.jl.","category":"page"},{"location":"","page":"Home","title":"Home","text":"Molly.jl follows the Contributor Covenant code of conduct.","category":"page"},{"location":"#Citation","page":"Home","title":"Citation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"If you use Molly, please cite the following paper (bib entry here):","category":"page"},{"location":"","page":"Home","title":"Home","text":"Greener JG. Differentiable simulation to develop molecular dynamics force fields for disordered proteins, Chemical Science 15, 4897-4909 (2024)","category":"page"},{"location":"","page":"Home","title":"Home","text":"A paper involving more contributors with further details on the software will be written at some point.","category":"page"},{"location":"#Interested?","page":"Home","title":"Interested?","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"There is the possibility of a postdoc position involving the development and use of Molly. Contact Joe Greener with informal enquiries.","category":"page"},{"location":"documentation/#Molly-documentation","page":"Documentation","title":"Molly documentation","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"This documentation will first introduce the main features of the package with some examples, then will give details on each component of a simulation. There are further examples in the Molly examples section. For more information on specific types or functions, see the Molly API section or call ?function_name in Julia. The Differentiable simulation with Molly section describes taking gradients through simulations.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The package takes a modular approach to molecular simulation. To run a simulation you create a System object and call simulate! on it. The different components of the system and simulation can be used as defined by the package, or you can define your own versions. An important principle of the package is that your custom components, particularly force and potential energy functions, should be easy to define and just as performant as the built-in versions.","category":"page"},{"location":"documentation/#Simulation-basics","page":"Documentation","title":"Simulation basics","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Let's look at the simulation of a fluid acting under the Lennard-Jones potential to start with. First, we'll need some atoms with the relevant parameters defined.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"using Molly\n\nn_atoms = 100\natom_mass = 10.0u\"g/mol\"\natoms = [Atom(mass=atom_mass, σ=0.3u\"nm\", ϵ=0.2u\"kJ * mol^-1\") for i in 1:n_atoms]","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"See the Unitful.jl documentation for more information on the unit annotations. Molly re-exports Unitful.jl and StaticArrays.jl since they are often required to run simulations. You can use your own atom types in Molly, provided that the mass function is defined and any fields required by the interactions are present. Next, we'll need some starting coordinates and velocities.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"boundary = CubicBoundary(2.0u\"nm\") # Periodic boundary conditions with a 2 nm cube\ncoords = place_atoms(n_atoms, boundary; min_dist=0.3u\"nm\") # Random placement without clashing\n\ntemp = 100.0u\"K\"\nvelocities = [random_velocity(atom_mass, temp) for i in 1:n_atoms]","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"We store the coordinates and velocities as static arrays for performance. They can be of 2 or 3 dimensions and of any number type, e.g. Float64 or Float32. Setting individual dimensions in a CubicBoundary to Inf * u\"nm\" makes the simulation have no boundary in that dimension. You can also use a TriclinicBoundary. Simulations in 2 dimensions should use a RectangularBoundary.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The vector function calculates the vector from one coordinate to another accounting for periodic boundary conditions by using the minimum image convention:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"vector(coords[1], coords[2], boundary)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"3-element SVector{3, Quantity{Float64, 𝐋, Unitful.FreeUnits{(nm,), 𝐋, nothing}}} with indices SOneTo(3):\n -0.8619698558762985 nm\n -0.9475020064484192 nm\n 0.8359421827141784 nm","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Now we can define our pairwise interactions, i.e. those between most or all atom pairs. Because we have defined the relevant parameters for the atoms, we can use the built-in LennardJones type.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"pairwise_inters = (LennardJones(),) # Don't forget the trailing comma!","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Finally, we can define the system and run the simulation. We use an AndersenThermostat to keep a constant temperature, and we log the temperature and coordinates every 10 steps. Periodic boundary conditions are automatically used with the cubic box we defined earlier.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"sys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=pairwise_inters,\n loggers=(\n temp=TemperatureLogger(10),\n coords=CoordinateLogger(10),\n ),\n)\n\nsimulator = VelocityVerlet(\n dt=0.002u\"ps\",\n coupling=AndersenThermostat(temp, 1.0u\"ps\"),\n)\n\nsimulate!(sys, simulator, 1_000)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"atoms, coords and boundary are the minimum required properties to define a System, though you would generally want to add interactions to a System to do something useful with it. The data keyword argument can give arbitrary data to the System that can be accessed with sys.data, for example metadata or properties to be used with a custom simulator. System implements the AbstractSystem interface from AtomsBase.jl. Various functions can be used on a System:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"potential_energy(sys) # -40.91077282719628 kJ mol^-1\nkinetic_energy(sys) # 119.47758705080862 kJ mol^-1\ntotal_energy(sys) # 78.56681422361234 kJ mol^-1\n\nforces(sys)\naccelerations(sys)\n\nmasses(sys)\ntemperature(sys) # 96.76667184796673 K\nrandom_velocities(sys, 300.0u\"K\")\n\nfloat_type(sys) # Float64\nis_on_gpu(sys) # false\n\n# Access properties\nsys.atoms\nsys.coords\nsys.boundary\nsys.velocities\nsys.topology\nsys.pairwise_inters\nsys.constraints\nsys.neighbor_finder\nsys.loggers\n\n# For certain systems\nvirial(sys)\npressure(sys)\n\n# AtomsBase.jl interface\nimport AtomsBase\nAtomsBase.mass(sys, 5) # 10.0 u\nAtomsBase.position(sys, 10)\n\n# Define a new system with certain properties changed\nSystem(sys; coords=(sys.coords .* 0.5))","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"By default the simulation is run in parallel on the number of threads available to Julia, but this behaviour can be changed by giving the keyword argument n_threads to simulate!. For example, n_threads=1 uses no parallelization.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The values stored by the loggers can be accessed using values, e.g. values(sys.loggers.coords). An animation of the stored coordinates can be saved by using visualize, which is available when GLMakie.jl is imported.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"using GLMakie\nvisualize(sys.loggers.coords, boundary, \"sim_lj.mp4\")","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"(Image: LJ simulation)","category":"page"},{"location":"documentation/#GPU-acceleration","page":"Documentation","title":"GPU acceleration","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To run simulations on the GPU you will need to have a CUDA-compatible device. CUDA.jl is used to run on the device. Simulation setup is similar to above, but with the coordinates, velocities and atoms moved to the GPU. This example also shows setting up a simulation to run with Float32, which gives better performance on GPUs. Of course, you will need to determine whether this level of numerical accuracy is appropriate in your case.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"using Molly\nusing CUDA\n\nn_atoms = 100\natom_mass = 10.0f0u\"g/mol\"\nboundary = CubicBoundary(2.0f0u\"nm\")\ntemp = 100.0f0u\"K\"\natoms = CuArray([Atom(mass=atom_mass, σ=0.3f0u\"nm\", ϵ=0.2f0u\"kJ * mol^-1\") for i in 1:n_atoms])\ncoords = CuArray(place_atoms(n_atoms, boundary; min_dist=0.3u\"nm\"))\nvelocities = CuArray([random_velocity(atom_mass, temp) for i in 1:n_atoms])\nsimulator = VelocityVerlet(dt=0.002f0u\"ps\")\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=(LennardJones(),),\n loggers=(\n temp=TemperatureLogger(typeof(1.0f0u\"K\"), 10),\n coords=CoordinateLogger(typeof(1.0f0u\"nm\"), 10),\n ),\n)\n\nsimulate!(deepcopy(sys), simulator, 20) # Compile function\nsimulate!(sys, simulator, 1_000)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The device to run on can be changed with device!, e.g. device!(1). The GPU code path is currently designed to be compatible with differentiable simulation and runs slower than related software, but this is an active area of development. Nonetheless, GPU performance is significantly better than CPU performance and is good enough for many applications.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The number of GPU threads used for the GPU kernels can be tuned with the environmental variables MOLLY_GPUNTHREADS_PAIRWISE, MOLLY_GPUNTHREADS_SPECIFIC, MOLLY_GPUNTHREADS_DISTANCENF and MOLLY_GPUNTHREADS_IMPLICIT. In general these should only be changed if GPU memory errors occur on smaller GPUs.","category":"page"},{"location":"documentation/#Simulating-diatomic-molecules","page":"Documentation","title":"Simulating diatomic molecules","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"If we want to define specific interactions between atoms, for example bonds, we can do this as well. Using the same definitions as the first example, let's set up the coordinates so that paired atoms are 1 Å apart.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"atoms = [Atom(mass=atom_mass, σ=0.3u\"nm\", ϵ=0.2u\"kJ * mol^-1\") for i in 1:n_atoms]\n\ncoords = place_atoms(n_atoms ÷ 2, boundary; min_dist=0.3u\"nm\")\nfor i in 1:length(coords)\n push!(coords, coords[i] .+ [0.1, 0.0, 0.0]u\"nm\")\nend\n\nvelocities = [random_velocity(atom_mass, temp) for i in 1:n_atoms]","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"We could have used place_diatomics instead here. Now we can use the built-in interaction list and bond types to place harmonic bonds between paired atoms.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"bonds = InteractionList2Atoms(\n collect(1:(n_atoms ÷ 2)), # First atom indices\n collect((1 + n_atoms ÷ 2):n_atoms), # Second atom indices\n [HarmonicBond(k=300_000.0u\"kJ * mol^-1 * nm^-2\", r0=0.1u\"nm\") for i in 1:(n_atoms ÷ 2)],\n)\n\nspecific_inter_lists = (bonds,)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"This time we are also going to use a neighbor list to speed up the Lennard-Jones calculation since we don't care about interactions beyond a certain distance. We can use the built-in DistanceNeighborFinder. The arguments are a 2D array of eligible interacting pairs, the number of steps between each update and the distance cutoff to be classed as a neighbor. Since the neighbor finder is run every 10 steps we should also use a distance cutoff for the neighbor list that is larger than the cutoff for the interaction.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"# All pairs apart from bonded pairs are eligible for non-bonded interactions\neligible = trues(n_atoms, n_atoms)\nfor i in 1:(n_atoms ÷ 2)\n eligible[i, i + (n_atoms ÷ 2)] = false\n eligible[i + (n_atoms ÷ 2), i] = false\nend\n\nneighbor_finder = DistanceNeighborFinder(\n eligible=eligible,\n n_steps=10,\n dist_cutoff=1.5u\"nm\",\n)\n\ncutoff = DistanceCutoff(1.2u\"nm\")\npairwise_inters = (LennardJones(use_neighbors=true, cutoff=cutoff),)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Now we can simulate as before.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"sys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=pairwise_inters,\n specific_inter_lists=specific_inter_lists,\n neighbor_finder=neighbor_finder,\n loggers=(\n temp=TemperatureLogger(10),\n coords=CoordinateLogger(10),\n ),\n)\n\nsimulator = VelocityVerlet(\n dt=0.002u\"ps\",\n coupling=AndersenThermostat(temp, 1.0u\"ps\"),\n)\nsimulate!(sys, simulator, 1_000)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"This time when we view the trajectory we can add lines to show the bonds.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"visualize(\n sys.loggers.coords,\n boundary,\n \"sim_diatomic.mp4\";\n connections=[(i, i + (n_atoms ÷ 2)) for i in 1:(n_atoms ÷ 2)],\n)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"(Image: Diatomic simulation) The neighbors can be found using find_neighbors(sys), which returns a NeighborList. When using a neighbor finder, functions such as forces and potential_energy require the neighbors to be passed as a second argument, e.g. forces(sys, find_neighbors(sys)).","category":"page"},{"location":"documentation/#Simulating-gravity","page":"Documentation","title":"Simulating gravity","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly is geared primarily to molecular simulation, but can also be used to simulate other physical systems. Let's set up a gravitational simulation. This example also shows a 2D simulation and no specified units.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"atoms = [Atom(mass=1.0f0), Atom(mass=1.0f0)]\ncoords = [SVector(0.3f0, 0.5f0), SVector(0.7f0, 0.5f0)]\nvelocities = [SVector(0.0f0, 1.0f0), SVector(0.0f0, -1.0f0)]\npairwise_inters = (Gravity(use_neighbors=false, G=1.5f0),)\nsimulator = VelocityVerlet(dt=0.002f0)\nboundary = RectangularBoundary(1.0f0)\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=pairwise_inters,\n loggers=(coords=CoordinateLogger(Float32, 10; dims=2),),\n force_units=NoUnits,\n energy_units=NoUnits,\n)\n\nsimulate!(sys, simulator, 2_000)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"When we view the simulation we can use some extra options:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"visualize(\n sys.loggers.coords,\n boundary,\n \"sim_gravity.mp4\";\n trails=4,\n framerate=15,\n color=[:orange, :lightgreen],\n)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"(Image: Gravity simulation)","category":"page"},{"location":"documentation/#Simulating-a-protein","page":"Documentation","title":"Simulating a protein","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The recommended way to run a macromolecular simulation is to read in a force field in OpenMM XML format to a MolecularForceField and then read in a coordinate file in a format supported by Chemfiles.jl. Files for common force fields can be found at OpenMM and OpenMM force fields. This sets up a system in the same data structures as above and that is simulated in the same way. Here we carry out an energy minimization, simulate with a Langevin integrator in the NPT ensemble and use a StructureWriter to write the trajectory as a PDB file.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"data_dir = joinpath(dirname(pathof(Molly)), \"..\", \"data\")\nff = MolecularForceField(\n joinpath(data_dir, \"force_fields\", \"ff99SBildn.xml\"),\n joinpath(data_dir, \"force_fields\", \"tip3p_standard.xml\"),\n joinpath(data_dir, \"force_fields\", \"his.xml\"),\n)\n\nsys = System(\n joinpath(data_dir, \"6mrr_equil.pdb\"),\n ff;\n loggers=(\n energy=TotalEnergyLogger(10),\n writer=StructureWriter(10, \"traj_6mrr_1ps.pdb\", [\"HOH\"]),\n ),\n gpu=false,\n)\n\nminimizer = SteepestDescentMinimizer()\nsimulate!(sys, minimizer)\n\ntemp = 298.0u\"K\"\nrandom_velocities!(sys, temp)\nsimulator = Langevin(\n dt=0.001u\"ps\",\n temperature=temp,\n friction=1.0u\"ps^-1\",\n coupling=MonteCarloBarostat(1.0u\"bar\", temp, sys.boundary),\n)\n\nsimulate!(sys, simulator, 5_000)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The OpenMM setup procedure is tested against OpenMM in terms of matching forces and energies. However it is not thoroughly tested with respect to ligands or special residues and requires that atom names exactly match residue templates. By default, terminal residues are renamed to match the appropriate templates. For example, the first (N-terminal) residue could be changed from \"MET\" to \"NMET\". This can be turned off by giving rename_terminal_res=false to System if the residue names in the input file are appropriate. Currently atom classes are not supported, only atom types. Residue patches, virtual sites, file includes and any force types other than HarmonicBondForce/HarmonicAngleForce/PeriodicTorsionForce/NonbondedForce are currently ignored.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"tip: Obtaining compatible structure files\nFuture work will increase the features and robustness when reading in structure files. In the mean time, the following tips may help you to read in a file correctly and without errors:Make sure there are no missing residues or heavy atoms. Tools such as MODELLER and SCWRL4 can fix these issues.\nRemove the hydrogen atoms and add them back using OpenMM, which will ensure they have atom names compatible with the OpenMM force field files.\nMake sure that all residue names match the corresponding residue template name and that all atom names match the appropriate atom in the residue template.\nNon-standard residues also require CONECT records for Chemfiles to assign bonds correctly, see for example a compatible alanine dipeptide file.Some PDB files that read in fine can be found here.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To run on the GPU, set gpu=true. You can use an implicit solvent method by giving the implicit_solvent keyword argument to System. The options are \"obc1\", \"obc2\" and \"gbn2\", corresponding to the Onufriev-Bashford-Case GBSA model with parameter set I or II and the GB-Neck2 model. Other options include overriding the boundary dimensions in the file (boundary) and modifying the non-bonded interaction and neighbor list cutoff distances (dist_cutoff and dist_neighbors).","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly also has a rudimentary parser of Gromacs topology and coordinate files. For example:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"sys = System(\n joinpath(dirname(pathof(Molly)), \"..\", \"data\", \"5XER\", \"gmx_coords.gro\"),\n joinpath(dirname(pathof(Molly)), \"..\", \"data\", \"5XER\", \"gmx_top_ff.top\");\n loggers=(\n temp=TemperatureLogger(10),\n writer=StructureWriter(10, \"traj_5XER_1ps.pdb\"),\n ),\n)\n\ntemp = 298.0u\"K\"\nrandom_velocities!(sys, temp)\nsimulator = Verlet(\n dt=0.0002u\"ps\",\n coupling=BerendsenThermostat(temp, 1.0u\"ps\"),\n)\n\nsimulate!(sys, simulator, 5_000)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Harmonic position restraints can be added to a System for equilibration using add_position_restraints:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"sys_res = add_position_restraints(\n sys,\n 100_000.0u\"kJ * mol^-1 * nm^-2\";\n atom_selector=is_heavy_atom,\n)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The Gromacs setup procedure should be considered experimental. Currently Ewald summation methods, constraint algorithms and high GPU performance are missing from the package, so Molly is not suitable for production simulations of biomolecules. Stay tuned for developments in this area.","category":"page"},{"location":"documentation/#Enhanced-sampling","page":"Documentation","title":"Enhanced sampling","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly has the ReplicaSystem struct and simulators such as TemperatureREMD to carry out replica exchange molecular dynamics (REMD). On CPU these are run in parallel by dividing up the number of available threads. For example, to run temperature REMD on a protein with 4 replicas and attempt exchanges every 1 ps:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"using Statistics\n\ndata_dir = joinpath(dirname(pathof(Molly)), \"..\", \"data\")\nff = MolecularForceField(\n joinpath(data_dir, \"force_fields\", \"ff99SBildn.xml\"),\n joinpath(data_dir, \"force_fields\", \"tip3p_standard.xml\"),\n joinpath(data_dir, \"force_fields\", \"his.xml\"),\n)\n\nsys = System(joinpath(data_dir, \"6mrr_equil.pdb\"), ff)\n\nminimizer = SteepestDescentMinimizer()\nsimulate!(sys, minimizer)\n\nn_replicas = 4\n\nrep_sys = ReplicaSystem(\n atoms=sys.atoms,\n replica_coords=[copy(sys.coords) for _ in 1:n_replicas],\n boundary=sys.boundary,\n n_replicas=n_replicas,\n atoms_data=sys.atoms_data,\n pairwise_inters=sys.pairwise_inters,\n specific_inter_lists=sys.specific_inter_lists,\n general_inters=sys.general_inters,\n neighbor_finder=sys.neighbor_finder,\n replica_loggers=[(temp=TemperatureLogger(10),) for _ in 1:n_replicas],\n)\n\ntemps = [240.0u\"K\", 280.0u\"K\", 320.0u\"K\", 360.0u\"K\"]\ndt = 0.0005u\"ps\"\nsimulators = [Langevin(dt=dt, temperature=temp, friction=1.0u\"ps^-1\") for temp in temps]\n\nsim = TemperatureREMD(\n dt=dt,\n temperatures=temps,\n simulators=simulators,\n exchange_time=1.0u\"ps\",\n)\n\nsimulate!(rep_sys, sim, 40_000; assign_velocities=true)\n\nprintln(rep_sys.exchange_logger.n_attempts)\n# 30\n\nfor i in 1:n_replicas\n final_temps = values(rep_sys.replicas[i].loggers.temp)[(end - 10):end]\n println(mean(final_temps))\nend\n# 240.1691457033836 K\n# 281.3783250460198 K\n# 320.44985840482974 K\n# 357.710520769689 K","category":"page"},{"location":"documentation/#Monte-Carlo-sampling","page":"Documentation","title":"Monte Carlo sampling","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly has the MetropolisMonteCarlo simulator to carry out Monte Carlo sampling with Metropolis selection rates. For example, to perform simulated annealing on charged particles to form a crystal lattice:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"n_atoms = 100\natoms = [Atom(mass=10.0u\"g/mol\", charge=1.0) for i in 1:n_atoms]\nboundary = RectangularBoundary(4.0u\"nm\")\n\ncoords = place_atoms(n_atoms, boundary; min_dist=0.2u\"nm\")\npairwise_inters = (Coulomb(),)\n\ntemperatures = [1198.0, 798.0, 398.0, 198.0, 98.0, 8.0]u\"K\"\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n pairwise_inters=pairwise_inters,\n loggers=(\n coords=CoordinateLogger(n_atoms, dims=n_dimensions(boundary)),\n montecarlo=MonteCarloLogger(),\n ),\n)\n\ntrial_args = Dict(:shift_size => 0.1u\"nm\")\nfor t in temperatures\n sim = MetropolisMonteCarlo(; \n temperature=t,\n trial_moves=random_uniform_translation!,\n trial_args=trial_args,\n )\n\n simulate!(sys, sim, 10_000)\nend\n\nprintln(sys.loggers.montecarlo.n_accept)\n# 15234\n\nvisualize(sys.loggers.coords, boundary, \"sim_montecarlo.gif\")","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"(Image: Monte Carlo simulation)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"trial_moves should be a function that takes a System as its argument and optional keyword arguments trial_args. It should modify the coordinates as appropriate, accounting for any boundary conditions. random_uniform_translation! and random_normal_translation! are provided as common trial move functions. MonteCarloLogger records various properties throughout the simulation.","category":"page"},{"location":"documentation/#Units","page":"Documentation","title":"Units","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly is fairly opinionated about using Unitful.jl units as shown above: you don't have to use them, but it is better if you do. Any consistent unit scheme can be used, or no units at all. Molly is most strict about the mixture of molar and non-molar types. For example, if your energy and force units are molar then your atom masses should be g/mol or similar. If you are not using units then no quantities can have Unitful annotations and you are responsible for ensuring a consistent unit system. Whilst you occasionally may run into friction with dimension mismatches, using units has the major advantages of catching whole classes of errors and letting you physically interpret the numbers in your system. The performance overhead of using units is minimal. Units are not currently compatible with differentiable simulations.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"All your interaction types need to return the same units of force and energy or the simulation will not run. By default these are kJ * mol^-1 * nm^-1 for force and kJ * mol^-1 for energy, but this can be changed using the force_units and energy_units arguments to System and some interactions. These arguments should be NoUnits if you are not using units. If you need to strip units for downstream analysis, use the ustrip or ustrip_vec functions. It should be noted that charges are stored as dimensionless, i.e. 1.0 represents an atomic charge of +1.","category":"page"},{"location":"documentation/#Atom-types","page":"Documentation","title":"Atom types","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly has a built-in Atom type with a few properties commonly used in molecular simulation defined. The mass and charge functions can be used on an Atom. Custom atom types can be used just as effectively provided that either the mass function is defined on the type or the type has a mass field (the fallback for the mass function). The type should also have all fields required by any interactions. The list of atoms passed to the System constructor should be concretely typed.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Custom atom types should generally be bits types, i.e. isbitstype(MyAtom) should be true, to work on the GPU. Additional non-bits type data for the atoms that is not directly used when calculating the interactions can be passed to the System constructor with the atoms_data keyword argument. For example the built-in AtomData type contains fields such as the atom name that are useful when writing trajectories.","category":"page"},{"location":"documentation/#Forces-and-energies","page":"Documentation","title":"Forces and energies","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Interactions define how different parts of the system interact. The force on each particle in the system is derived from the potential corresponding to the interaction.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"vecF_i = -sum_j fracdV_ij(r_ij)dr_ijfracvecr_ijr_ij","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"In Molly there are three types of interactions:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"PairwiseInteractions are present between all or most atom pairs, and account for example for non-bonded terms in molecular mechanics force fields.\nSpecificInteractions are present between specific atoms, and account for example for bonded terms in molecular mechanics force fields.\nGeneral interactions are a free-form interaction type that can access the whole system and outputs forces for all atoms. This is useful for neural network potentials, implicit solvent models and other cases that require maximum flexibility. General interactions should be compatible with the AtomsCalculators.jl interface.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The available pairwise interactions are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"LennardJones\nLennardJonesSoftCore\nSoftSphere\nMie\nBuckingham\nCoulomb\nCoulombSoftCore\nCoulombReactionField\nGravity","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The available specific interactions are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"HarmonicPositionRestraint - 1 atom\nHarmonicBond - 2 atoms\nMorseBond - 2 atoms\nFENEBond - 2 atoms\nHarmonicAngle - 3 atoms\nCosineAngle - 3 atoms\nPeriodicTorsion - 4 atoms\nRBTorsion - 4 atoms","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The available general interactions are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"ImplicitSolventOBC\nImplicitSolventGBN2\nMullerBrown\nASECalculator","category":"page"},{"location":"documentation/#Pairwise-interactions","page":"Documentation","title":"Pairwise interactions","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To define your own PairwiseInteraction, first define the struct:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"struct MyPairwiseInter <: PairwiseInteraction\n # Any properties, e.g. constants for the interaction or cutoff parameters\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"You can also define a use_neighbors method, which determines whether the neighbor list is used to omit distant atoms (true) or whether all atom pairs are always considered (false):","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly.use_neighbors(inter::MyPairwiseInter) = true","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"This is false by default. If it is true, you must specify a neighbor finder when setting up the System. For built-in interactions this function accesses the use_neighbors field of the struct. To work on the GPU the struct should be a bits type, i.e. isbitstype(MyPairwiseInter) should be true.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Next, you need to define a method for the force function acting between a pair of atoms. This has a set series of arguments:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function Molly.force(inter::MyPairwiseInter,\n vec_ij,\n coord_i,\n coord_j,\n atom_i,\n atom_j,\n boundary)\n # Replace this with your force calculation\n # A positive force causes the atoms to move apart\n f = 0.0\n\n # Obtain a vector for the force\n fdr = f * normalize(vec_ij)\n return fdr\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"vec_ij is the vector between the closest images of atoms i and j accounting for the periodic boundary conditions. Atom properties can be accessed, e.g. atom_i.σ. Typically the force function is where most computation time is spent during the simulation, so consider optimising this function if you want high performance. One nice feature of Molly is that this function will work on both the CPU and the GPU.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"An optional final argument special is a Bool determining whether the atom pair interaction should be treated as special. This is specified during neighbor finder construction. When simulating molecules, for example, non-bonded interactions for atoms in a 1-4 bonding arrangement (i-x-x-j) are often weighted by a factor such as 0.5. For interactions where this is relevant, special can be used to apply this weighting in the interaction. It can have a variety of uses depending on the context, for example if you have multiple interactions and want to exclude certain atom pairs from one of the interactions only.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To use your custom interaction in a simulation, add it to the list of pairwise interactions:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"pairwise_inters = (MyPairwiseInter(),)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Then create a System and simulate as above. Note that you can also use a named tuple instead of a tuple if you want to access interactions by name:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"pairwise_inters = (MyPairwiseInter=MyPairwiseInter(),)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"For performance reasons it is best to avoid containers with abstract type parameters, such as Vector{PairwiseInteraction}.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"If you wish to calculate potential energies or log the energy throughout a simulation, you will need to define a method for the potential_energy function. This has the same arguments as force and should return a single value corresponding to the potential energy.","category":"page"},{"location":"documentation/#Specific-interactions","page":"Documentation","title":"Specific interactions","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To define your own SpecificInteraction, first define the struct:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"struct MySpecificInter <: SpecificInteraction\n # Properties, e.g. a bond distance corresponding to the energy minimum\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Next, you need to define a method for the force function. The form of this will depend on whether the interaction involves 1, 2, 3 or 4 atoms. For example in the 2 atom case:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function Molly.force(inter::MySpecificInter, coords_i, coords_j, boundary)\n dr = vector(coords_i, coords_j, boundary)\n\n # Replace this with your force calculation\n # A positive force causes the atoms to move apart\n f = 0.0\n\n fdr = f * normalize(dr)\n return SpecificForce2Atoms(-fdr, fdr)\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The 3 atom case would define Molly.force(inter::MySpecificInter, coords_i, coords_j, coords_k, boundary) and return SpecificForce3Atoms(f1, f2, f3). To use your custom interaction, add it to the specific interaction lists along with the atom indices:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"specific_inter_lists = (\n InteractionList2Atoms(\n [1, 3],\n [2, 4],\n [MySpecificInter(), MySpecificInter()],\n ),\n)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"For 3 atom interactions use InteractionList3Atoms and pass 3 sets of indices. If using the GPU, the inner list of indices and interactions should be moved to the GPU with CuArray. The number in the interaction list and the return type from force must match, e.g. InteractionList3Atoms must always return SpecificForce3Atoms from the corresponding force function. If some atoms are required in the interaction for force calculation but have no force applied to them by the interaction, give a zero force vector for those atoms. Again a method for the potential_energy function with the same arguments can be defined.","category":"page"},{"location":"documentation/#General-interactions","page":"Documentation","title":"General interactions","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To define your own general interaction, first define the struct:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"struct MyGeneralInter\n # Properties, e.g. a neural network model\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Next, you need to define a method for the AtomsCalculators.forces function (note this is different to the force function above).","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"import AtomsCalculators\n\nfunction AtomsCalculators.forces(sys,\n inter::MyGeneralInter;\n neighbors=nothing,\n n_threads=Threads.nthreads(),\n kwargs...)\n # kwargs... is required, neighbors and n_threads can be omitted if not used\n\n # Calculate the forces on all atoms using the interaction and the system\n # The output should have the same shape as the coordinates\n # For example, a neural network might do something like this\n return inter.model(sys.coords, sys.atoms)\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The neighbors calculated from the neighbor list are available in this function, but may or may not be used depending on context. You could carry out your own neighbor finding in this function if required. Note that this function calculates forces not accelerations; if you have a neural network that calculates accelerations you should multiply these by masses(sys) to get the forces according to F=ma.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"A method for the AtomsCalculators.potential_energy function that takes the same arguments and returns a single value can also be defined. A method for the virial function can also be defined, allowing virial and pressure calculation when using custom general interactions. To use your custom interaction in a simulation, add it to the list of general interactions:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"general_inters = (MyGeneralInter(),)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"general_inters=general_inters can be given as a keyword argument when setting up the System.","category":"page"},{"location":"documentation/#Cutoffs","page":"Documentation","title":"Cutoffs","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The total potential energy of a system is given as a sum of the individual inter-particle potentials","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"V(vecr_1 dotsc vecr_N) = sum_ijV_ij(r_ij)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The forces acting on the particles are given by","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"vecF_i = -sum_j fracdV_ij(r_ij)dr_ijfracvecr_ijr_ij","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"In the case of the Lennard-Jones potential, the inter-particle potential is given by","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"V_ij(r_ij) = 4varepsilon_ij leftleft(fracsigma_ijr_ijright)^12 - left(fracsigma_ijr_ijright)^6right","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"and the forces are given by","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"beginaligned\nvecF_i = 24varepsilon_ij left(2fracsigma_ij^12r_ij^13 - fracsigma_ij^6r_ij^7right) fracvecr_ijr_ij \n= frac24varepsilon_ijr_ij^2 left2left(fracsigma_ij^6r_ij^6right)^2 -left(fracsigma_ijr_ijright)^6right vecr_ij\nendaligned","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"As the potential, and thus also the force decreases rapidly with the distance, in almost every implementation of the Lennard-Jones force calculation there is a cutoff radius beyond which the force is set to 0.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"While this is often a sensible approach, it introduces a discontinuity in the force function and it requires us to also modify the potential, as beyond the cutoff radius the force would be 0, but the derivative of the unmodified potential is not. One way to truncate the potential is to shift the potential by its cutoff value.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"beginaligned\nvecF_SP(vecr) = begincases\nvecF(vecr) r r_c \n0 r r_c\nendcases \nV_SP(r) = begincases\nV(r) - V(r_c) r le r_c \n0 r r_c\nendcases\nendaligned","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"This way the potential function is continuous and the relation between forces and potentials is satisfied. This truncation method is called shifted potential cutoff.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Another option is to shift the force in order to make it continuous","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"beginaligned\nF_SF(r) = begincases\nF(r) - F(r_c) r le r_c \n0 r r_c\nendcases \nV_SF(r) = begincases\nV(r) - (r-r_c) V(r_c) - V(r_c) r le r_c \n0 r r_c\nendcases\nendaligned","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"This requires a more complicated change in the potential in order to satisfy the relation between them. This method is called the shifted force cutoff. The continuity of the force is desirable as it may give better energy conservation properties as shown in Toxvaerd 2011.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"There are also more complicated truncation methods that interpolate between the original potential and 0, but we will consider those two for the moment. The truncation approximations that we use can significantly alter the qualitative features of the simulation as shown in many articles in the molecular dynamics literature (Fitzner 2017, van der Spoel 2006 and others).","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Since the truncation algorithm is independent of the interaction for which is used, each compatible interaction is defined without including cutoffs. The corresponding interaction constructor has a cutoff field (default NoCutoff) which is then used via dispatch to apply the chosen cutoff, e.g. SoftSphere(cutoff=ShiftedPotentialCutoff(1.2u\"nm\")). The available cutoffs are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"NoCutoff\nDistanceCutoff\nShiftedPotentialCutoff\nShiftedForceCutoff\nCubicSplineCutoff","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The following interactions can use a cutoff:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"LennardJones\nLennardJonesSoftCore\nSoftSphere\nMie\nBuckingham\nCoulomb\nCoulombSoftCore","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"In addition, CoulombReactionField and the implicit solvent models have a dist_cutoff argument for a cutoff distance.","category":"page"},{"location":"documentation/#Boundaries","page":"Documentation","title":"Boundaries","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly allows the use of various periodic and infinite boundary conditions. The available 3D boundaries are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"CubicBoundary\nTriclinicBoundary","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The available 2D boundaries are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"RectangularBoundary","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Some examples of using boundaries:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"CubicBoundary(2.0u\"nm\" ) # Periodic cube with 2 nm sides\nCubicBoundary(2.0u\"nm\" , 2.0u\"nm\" , 2.0u\"nm\" ) # Periodic cube with 2 nm sides\nCubicBoundary(4.0u\"nm\" , 5.0u\"nm\" , 6.0u\"nm\" ) # Periodic cuboid\nCubicBoundary(2.0u\"nm\" , 2.0u\"nm\" , Inf * u\"nm\") # Infinite boundary in z direction\nCubicBoundary(Inf * u\"nm\", Inf * u\"nm\", Inf * u\"nm\") # Infinite boundary, no periodicity\nCubicBoundary(Inf * u\"nm\" ) # Infinite boundary, no periodicity\n\nRectangularBoundary(2.0u\"nm\" ) # Periodic square\nRectangularBoundary(4.0u\"nm\", 5.0u\"nm\" ) # Periodic rectangle\nRectangularBoundary(2.0u\"nm\", Inf * u\"nm\") # Infinite boundary in y direction\n\n# Periodic triclinic from basis vectors\nTriclinicBoundary(SVector(\n SVector(2.2 , 0.0 , 0.0 )u\"nm\",\n SVector(1.0 , 1.7320508, 0.0 )u\"nm\",\n SVector(1.37888 , 0.5399122, 1.0233204)u\"nm\",\n))\n\n# Periodic triclinic from basis vector lengths and angles α/β/γ\nb = TriclinicBoundary(\n SVector(2.2, 2.0, 1.8)u\"nm\",\n deg2rad.(SVector(50.0, 40.0, 60.0)),\n)\n\n# Volume of bounding box\nbox_volume(b) # 3.8993746318188633 nm^3\n\n# Random coordinate uniformly distributed within boundary\nrandom_coord(b) # SVector(2.651062310435411, 2.1702306804433973, 0.9518105403051831)u\"nm\"\n\n# Wrap a coordinate back into the boundary if it is outside\nwrap_coords(SVector(1.0, 1.0, 1.0)u\"nm\", b) # SVector(3.2, 1.0, 1.0)u\"nm\"","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The box_center, AtomsBase.n_dimensions, float_type, place_atoms and place_diatomics functions are also available for boundaries.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The appropriate boundary to use will depend on your simulation. For example, having different lengths in each dimension would usually only make sense in a situation where forces or restraints depended on the dimension.","category":"page"},{"location":"documentation/#Simulators","page":"Documentation","title":"Simulators","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Simulators define what type of simulation is run. This could be anything from a simple energy minimization to complicated replica exchange MD. The available simulators are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"SteepestDescentMinimizer\nVelocityVerlet\nVerlet\nStormerVerlet\nLangevin\nLangevinSplitting\nOverdampedLangevin\nNoseHoover\nTemperatureREMD\nHamiltonianREMD\nMetropolisMonteCarlo","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Many of these require a time step dt as an argument. Many also remove the center of mass motion every time step, which can be tuned with the remove_CM_motion argument.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The LangevinSplitting simulator can be used to define a variety of integrators such as velocity Verlet (splitting \"BAB\"), the Langevin implementation in Langevin (\"BAOA\"), and symplectic Euler integrators (\"AB\" and \"BA\").","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To define your own simulator, first define a struct:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"struct MySimulator\n # Any properties, e.g. the time step or coupling methods\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Then, define a method for simulate! that carries out the simulation. This example shows some helper functions that you can use:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function Molly.simulate!(sys,\n sim::MySimulator,\n n_steps::Integer;\n n_threads::Integer=Threads.nthreads(),\n run_loggers=true)\n # Find neighbors like this\n neighbors = find_neighbors(sys, sys.neighbor_finder; n_threads=n_threads)\n\n for step_n in 1:n_steps\n # Calculate accelerations like this\n accels_t = accelerations(sys, neighbors; n_threads=n_threads)\n\n # Ensure coordinates stay within the simulation box like this\n sys.coords = wrap_coords.(sys.coords, (sys.boundary,))\n\n # Example velocity update\n # Includes appropriate unit conversion for when the force units are per mol\n sys.velocities += (accels_t .+ accels_t_dt) .* sim.dt / 2\n\n # Apply coupling like this\n recompute_forces = apply_coupling!(sys, sim.coupling, sim, neighbors, step_n;\n n_threads=n_threads)\n\n # Remove center of mass motion like this\n remove_CM_motion!(sys)\n\n # Apply the loggers like this\n # Computed quantities can also be given as keyword arguments to run_loggers!\n run_loggers!(sys, neighbors, step_n, run_loggers; n_threads=n_threads)\n\n # Find new neighbors like this\n neighbors = find_neighbors(sys, sys.neighbor_finder, neighbors, step_n, recompute_forces;\n n_threads=n_threads)\n end\n\n return sys\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"See more in the source code, for example how to apply constraints to coordinates and velocities. To use your custom simulator, give it as the second argument when calling simulate!.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To define your own replica exchange simulator, first define a struct:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"struct MyREMDSimulator\n dt # Time step\n exchange_time # Time between exchanges\n simulators # A list of simulators to use for each replica e.g. Langevin\n # Other properties of the simulation\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Then define the function that carries out the exchange, remd_exchange!:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function Molly.remd_exchange!(sys::ReplicaSystem,\n sim::MyREMDSimulator,\n n::Integer,\n m::Integer;\n n_threads::Integer=Threads.nthreads(),\n rng=Random.GLOBAL_RNG)\n # Attempt to exchange the replicas with index n and m\n # First define Δ for the REMD scheme\n make_exchange = Δ <= 0 || rand(rng) < exp(-Δ) # Example of Metropolis acceptance rate\n if make_exchange\n # Exchange coordinates and velocities of replicas\n # Also scale the velocities to match the temperature if required\n end\n\n return Δ, make_exchange\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The above function returns Δ, the argument of the acceptance rate that is logged by ReplicaExchangeLogger, and a boolean indicating whether the exchange was successful.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Then, define a method for the simulate! function to perform the parallel simulation. This does any initial setup such as assigning velocities then uses simulate_remd! to run the simulation:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function Molly.simulate!(sys::ReplicaSystem,\n sim::MyREMDSimulator,\n n_steps::Integer;\n rng=Random.GLOBAL_RNG,\n n_threads::Integer=Threads.nthreads())\n # Do any initial setup if necessary\n simulate_remd!(sys, sim, n_steps; rng=rng, n_threads=n_threads)\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Under the hood there are two implementations for the forces function, used by accelerations, and for potential_energy: a version geared towards CPUs and parallelism, and a version geared towards GPUs. You can define different versions of a simulator for CPU and GPU systems by dispatching on System{D, false} or System{D, true} respectively. This also applies to coupling methods, neighbor finders and analysis functions. You do not have to define two versions though: you may only intend to use the simulator one way, or one version may be performant in all cases.","category":"page"},{"location":"documentation/#Coupling","page":"Documentation","title":"Coupling","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Some simulators can be modified by adding coupling methods to allow properties like temperature and pressure to be controlled during a simulation. The available couplers are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"AndersenThermostat\nRescaleThermostat\nBerendsenThermostat\nMonteCarloBarostat\nMonteCarloAnisotropicBarostat\nMonteCarloMembraneBarostat","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Currently the VelocityVerlet, Verlet, StormerVerlet, Langevin and NoseHoover simulators support coupling methods, with the default being NoCoupling. Couplers are given to the coupling keyword argument during simulator construction:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"temp = 300.0u\"K\"\npress = 1.0u\"bar\"\nthermostat = AndersenThermostat(temp, 1.0u\"ps\")\nbarostat = MonteCarloBarostat(press, temp, sys.boundary)\n\n# Velocity Verlet with Andersen thermostat\nVelocityVerlet(dt=0.001u\"ps\", coupling=thermostat)\n\n# Velocity Verlet with Andersen thermostat and Monte Carlo barostat\nVelocityVerlet(dt=0.001u\"ps\", coupling=(thermostat, barostat))","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The appropriate coupling to use will depend on the situation. For example, the MonteCarloBarostat for controlling pressure assumes a constant temperature but does not actively control the temperature. It should be used alongside a temperature coupling method such as the Langevin simulator or AndersenThermostat coupling.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To define your own coupling method, first define the struct:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"struct MyCoupler\n # Any properties, e.g. a target temperature or coupling constant\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Then, define the function that implements the coupling every time step:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function Molly.apply_coupling!(sys, coupling::MyCoupler, sim, neighbors, step_n;\n n_threads=Threads.nthreads())\n # Do something to the simulation, e.g. scale the velocities\n # Return whether the coupling has invalidated the currently stored forces,\n # for example by changing the coordinates\n recompute_forces = false\n return recompute_forces\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The functions random_velocity, maxwell_boltzmann and temperature may be useful here. To use your custom coupler, give it as the coupling argument to the simulator as above.","category":"page"},{"location":"documentation/#Loggers","page":"Documentation","title":"Loggers","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Loggers record properties of the simulation to allow monitoring and analysis. The available loggers are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"GeneralObservableLogger\nTemperatureLogger\nCoordinateLogger\nVelocityLogger\nTotalEnergyLogger\nKineticEnergyLogger\nPotentialEnergyLogger\nForceLogger\nStructureWriter\nTimeCorrelationLogger\nAutoCorrelationLogger\nAverageObservableLogger\nReplicaExchangeLogger\nMonteCarloLogger","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Many of the loggers can be initialised with just the number of steps between recorded values, e.g. CoordinateLogger(10). An optional first argument is the type of the recorded value; the above is equivalent to CoordinateLogger(typeof(1.0u\"nm\"), 10) but if the simulation did not use units then CoordinateLogger(Float64, 10) would be required. If the simulation is in 2D, giving dims=2 as a keyword argument is required for some loggers. A logger's history can be accessed with values(my_logger).","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To define your own logger, first define the struct and a method for values to access the stored values:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"struct MyLogger\n n_steps::Int\n history::Vector{Float64}\n # Any other properties\nend\n\nBase.values(logger::MyLogger) = logger.history","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Then, define the logging function that is called every step by the simulator:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function Molly.log_property!(logger::MyLogger,\n sys,\n neighbors,\n step_n;\n n_threads=Threads.nthreads(),\n kwargs...)\n if step_n % logger.n_steps == 0\n # Record some property or carry out some action\n end\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The use of n_steps is optional and is an example of how to record a property periodically throughout the simulation. To use your custom logger, add it to the named tuple of loggers given when creating the System:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"loggers = (mylogger=MyLogger(10, []),) # Don't forget the trailing comma!","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"In addition to being run at the end of each step, loggers are run before the first step, i.e. at step 0. This means that a logger that records a value every step for a simulation with 100 steps will end up with 101 values. Running loggers before the first step can be disabled by giving run_loggers=:skipzero as a keyword argument to simulate!, which can be useful when splitting up simulations into multiple simulate! calls. For example, this runs the loggers 301 times:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"simulate!(sys, simulator, 100) # Default run_loggers=true\nsimulate!(sys, simulator, 100; run_loggers=:skipzero)\nsimulate!(sys, simulator, 100; run_loggers=:skipzero)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Running loggers can be disabled entirely with run_loggers=false, which is the default for SteepestDescentMinimizer. Loggers are currently ignored for the purposes of taking gradients, so if a logger is used in the gradient calculation the gradients will appear to be nothing.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Many times, a logger will just record an observation to an Array containing a record of past observations. For this purpose, you can use the GeneralObservableLogger without defining a custom logging function. Define your observation function as","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function my_observable(sys::System, neighbors; n_threads::Integer, kwargs...)\n # Probe the system for some desired property\n return observation\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Keyword arguments current_forces and current_potential_energy can also be used here to avoid recomputing values that are passed from the simulator:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function my_pe_observable(sys::System, neighbors; n_threads::Integer,\n current_potential_energy=nothing, kwargs...)\n if isnothing(current_potential_energy)\n # Compute potential energy\n return potential_energy(sys, neighbors; n_threads=n_threads)\n else\n # Potential energy was passed from simulator, reuse\n return current_potential_energy\n end\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"These keyword arguments are also available in log_property!. Which values are passed depends on the simulator being used, for example SteepestDescentMinimizer passes current_potential_energy because it uses it for minimization. Note that loggers are called after apply_coupling!, so the coordinates may have changed since the potential energy or forces were computed.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"A logger which records the property every n_steps can be constructed through ","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"my_logger = GeneralObservableLogger(my_observable, T, n_steps)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"where T = typeof(observation) is the type of the return value for my_observable. AverageObservableLogger is similar but records a running average rather than storing observations.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The TimeCorrelationLogger logger can be used to compute correlation functions of the form","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"C(t) = fraclangle A_t cdot B_0 ranglesqrtlangle A^2 rangle langle B^2 rangle","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"where A and B are scalar or vector centered observables and the brackets are ensemble averages. This includes the computations of autocorrelation functions, which can be used to gather insight into the dynamical properties of the system, for instance using Green-Kubo formulas, or the statistical properties of a sampling method.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Let's look at a simple example, computing the velocity autocorrelation function for a simple system consisting of diatomic molecules defined by HarmonicBond potentials between pairs of atoms, and an additional SoftSphere potential between all pairs of atoms. Let's start by defining the system.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"n_atoms = 400\natom_mass = 10.0u\"g/mol\"\natoms = [Atom(mass=atom_mass, σ=0.2u\"nm\", ϵ=0.2u\"kJ * mol^-1\") for i in 1:n_atoms]\n\n# Initialization\nboundary = CubicBoundary(6.0u\"nm\")\ncoords = place_diatomics(n_atoms ÷ 2, boundary, 0.2u\"nm\"; min_dist=0.2u\"nm\")\n\ntemp = 50.0u\"K\"\nvelocities = [random_velocity(atom_mass, temp) .* 0.01 for i in 1:n_atoms]\n\n# Interaction potentials\npairwise_inters = (SoftSphere(use_neighbors=true, cutoff=DistanceCutoff(0.6u\"nm\")),)\n\nbonds = [HarmonicBond(k=10000u\"kJ * mol^-1 * nm^-2\", r0=0.2u\"nm\") for i in 1:(n_atoms ÷ 2)]\nspecific_inter_lists = (InteractionList2Atoms(\n collect(1:2:n_atoms),\n collect(2:2:n_atoms),\n bonds,\n),)\n\n# Define system\nnf = DistanceNeighborFinder(eligible=trues(n_atoms, n_atoms), dist_cutoff=0.6u\"nm\")\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=pairwise_inters,\n specific_inter_lists=specific_inter_lists,\n neighbor_finder=nf,\n)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"We leave the loggers empty until we thermalize the system using Langevin dynamics.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"dt = 0.002u\"ps\"\nsimulator = LangevinSplitting(\n dt=dt,\n temperature=temp,\n friction=10.0u\"g * mol^-1 * ps^-1\",\n splitting=\"BAOAB\",\n)\nsimulate!(sys, simulator, 10_000)\n@show temperature(sys)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"temperature(sys) = 48.76795299825687 K","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Good. Next we define our correlation logger, add it to the system's loggers and run a longer simulation. Note that we need to redeclare the system when adding a logger.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"# Velocity observable\n# args and kwargs because more complex observables may require neighbors and parallelism\nV(s::System, args...; kwargs...) = s.velocities\nV_Type = eltype(sys.velocities)\nlogger = TimeCorrelationLogger(V, V, V_Type, V_Type, n_atoms, 1_000)\n\nsys = System(\n atoms=atoms,\n coords=sys.coords,\n boundary=boundary,\n velocities=sys.velocities,\n pairwise_inters=pairwise_inters,\n specific_inter_lists=specific_inter_lists,\n neighbor_finder=nf,\n loggers=(velocity_autocorrelation=logger,)\n)\nsimulate!(sys, simulator, 100_000)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Check the output:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"show(sys.loggers)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"(velocity_autocorrelation = AutoCorrelationLogger with n_correlation 1000 and 100001 samples collected for observable V,)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Note we also could have used the convenience function AutoCorrelationLogger to define our logger since the two observables we are correlating are the same.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"using GLMakie\nf = Figure()\nax = Axis(f[1, 1], xlabel=\"Time / ps\", ylabel=\"Correlation\")\nlines!((1:1000) .* ustrip(dt), values(sys.loggers.velocity_autocorrelation))","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"(Image: Velocity Autocorrelations)\nAs expected, the velocities are highly correlated at small time offsets and the correlation decays rapidly. The oscillatory behavior is due to the harmonic bond interactions.","category":"page"},{"location":"documentation/#Constraints","page":"Documentation","title":"Constraints","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly implements SHAKE and its extension, RATTLE, to perform constrained molecular dynamics (see SHAKE_RATTLE). These methods are useful for maintaining bond lengths and angles during a simulation, often allowing the use of longer time steps and therefore more efficient use of computing resources. The constraints satisfied by SHAKE are solely on the atomic coordinates:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"beginaligned\nvecr_ij cdot vecr_ij = d^2_ij\nendaligned","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"whereas RATTLE also constrains the velocities:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"beginaligned\nvecr_ij cdot vecv_ij = 0\nendaligned","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Here vecr_ij is the vector between atoms i and j in a constraint, d_ij is the bond length to be maintained and vecv_ij is the difference in the velocity vectors for atoms i and j. SHAKE was originally derived for the Verlet integration scheme (Ryckaert et al. 1977) with RATTLE extending SHAKE to work for velocity Verlet where the velocities are also integrated (Andersen 1983).","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Currently, constraints are supported by the following simulators:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"SteepestDescentMinimizer\nVelocityVerlet\nVerlet\nStormerVerlet\nLangevin","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Simulators incompatible with constraints will print a warning and continue when used with systems containing constraints. Constraints are not currently compatible with GPU simulation.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"In Molly, the SHAKE constraints for diatomic molecules are solved analytically while all larger constraints are solved iteratively. The velocity constraints imposed by RATTLE form a linear system of equations which could be solved exactly; however, this operation is expensive for clusters of more than 4 constraints. Therefore, RATTLE constraints can be solved by direct matrix inversion for small clusters (4 or fewer constraints) and iteratively otherwise (currently only solved iteratively). The number of constraints here does not refer to the total number of constraints in the system, rather to the total number of constraints in an independent cluster/molecule. For example, a water molecule can be constrained by 2 distance constraints and 1 angle constraint which is only 3 constraints. However, a C-C backbone of an organic molecule like octane would need 7 constraints to maintain all the C-C bond lengths. Constraining large clusters will result in a performance penalty.","category":"page"},{"location":"documentation/#Neighbor-finders","page":"Documentation","title":"Neighbor finders","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Neighbor finders find close atoms periodically throughout the simulation, saving on computation time by allowing the force calculation between distant atoms to be omitted. When using a neighbor finder you should in general also use a cutoff (see Cutoffs) with a cutoff distance less than the neighbor finder distance. The available neighbor finders are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"NoNeighborFinder\nCellListMapNeighborFinder\nTreeNeighborFinder\nDistanceNeighborFinder","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To define your own neighbor finder, first define the struct:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"struct MyNeighborFinder\n eligible::BitArray{2}\n special::BitArray{2}\n n_steps::Int\n # Any other properties, e.g. a distance cutoff\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Examples of three useful properties are given here: a matrix indicating atom pairs eligible for pairwise interactions, a matrix indicating atoms in a special arrangement such as 1-4 bonding, and a value determining how many time steps occur between each evaluation of the neighbor finder. Then, define the neighbor finding function that is called every step by the simulator:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function Molly.find_neighbors(sys,\n nf::MyNeighborFinder,\n current_neighbors=nothing,\n step_n::Integer=0,\n force_recompute::Bool=false;\n n_threads::Integer=Threads.nthreads())\n if force_recompute || step_n % nf.n_steps == 0\n if isnothing(current_neighbors)\n neighbors = NeighborList()\n else\n neighbors = current_neighbors\n end\n empty!(neighbors)\n # Add to neighbors, for example\n push!(neighbors, (1, 2, false)) # atom i, atom j and whether they are in a special interaction\n return neighbors\n else\n return current_neighbors\n end\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To use your custom neighbor finder, give it as the neighbor_finder argument when creating the System.","category":"page"},{"location":"documentation/#Analysis","page":"Documentation","title":"Analysis","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly contains some tools for analysing the results of simulations. Functions that may be useful for analysis include:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"visualize\nrdf\ndistances\ndisplacements\nrmsd\nradius_gyration\nhydrodynamic_radius\nbond_angle\ntorsion_angle","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Julia is a language well-suited to implementing all kinds of analysis for molecular simulations.","category":"page"},{"location":"exercises/#Molly-exercises","page":"Exercises","title":"Molly exercises","text":"","category":"section"},{"location":"exercises/","page":"Exercises","title":"Exercises","text":"A notebook with exercises covering various parts of Molly is available here, with answers available here.","category":"page"},{"location":"exercises/","page":"Exercises","title":"Exercises","text":"Another notebook with exercises on computing transport coefficients with Molly is available here.","category":"page"}] +[{"location":"api/#Molly-API","page":"API","title":"Molly API","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"The API reference can be found here.","category":"page"},{"location":"api/","page":"API","title":"API","text":"Molly re-exports StaticArrays.jl and Unitful.jl, making the likes of SVector and 1.0u\"nm\" available when you call using Molly.","category":"page"},{"location":"api/","page":"API","title":"API","text":"Package extensions are used in order to reduce the number of dependencies:","category":"page"},{"location":"api/","page":"API","title":"API","text":"To use visualize, call using GLMakie.\nTo use ASECalculator, call using PythonCall.\nTo use rdf, call using KernelDensity.","category":"page"},{"location":"api/#Exported-names","page":"API","title":"Exported names","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"Order = [:module, :type, :constant, :function, :macro]","category":"page"},{"location":"api/#Docstrings","page":"API","title":"Docstrings","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"Modules = [Molly]\nPrivate = false\nOrder = [:module, :type, :constant, :function, :macro]","category":"page"},{"location":"api/#Molly.ASECalculator","page":"API","title":"Molly.ASECalculator","text":"ASECalculator(; )\n\nA Python ASE calculator.\n\nThis calculator is only available when PythonCall is imported. It is the user's responsibility to have the required Python packages installed. This includes ASE and any packages providing the calculator.\n\nContrary to the rest of Molly, unitless quantities are assumed to have ASE units: Å for length, eV for energy, u for mass, and Å sqrt(u/eV) for time. Unitful quantities will be converted as appropriate.\n\nNot currently compatible with TriclinicBoundary.\n\nArguments\n\nase_calc: the ASE calculator created with PythonCall.\natoms: the atoms, or atom equivalents, in the system.\ncoords: the coordinates of the atoms in the system. Typically a vector of SVectors of 2 or 3 dimensions.\nboundary: the bounding box in which the simulation takes place.\nelements=nothing: vector of atom elements as a string, either elements or atoms_data (which contains element data) must be provided.\natoms_data=nothing: other data associated with the atoms.\nvelocities=nothing: the velocities of the atoms in the system, only required if the velocities contribute to the potential energy or forces.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.AbstractGBSA","page":"API","title":"Molly.AbstractGBSA","text":"Generalized Born (GB) implicit solvent models augmented with the hydrophobic solvent accessible surface area (SA) term.\n\nCustom GBSA methods should sub-type this abstract type.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.AndersenThermostat","page":"API","title":"Molly.AndersenThermostat","text":"AndersenThermostat(temperature, coupling_const)\n\nThe Andersen thermostat for controlling temperature.\n\nThe velocity of each atom is randomly changed each time step with probability dt / coupling_const to a velocity drawn from the Maxwell-Boltzmann distribution. See Andersen 1980.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.Atom","page":"API","title":"Molly.Atom","text":"Atom(; )\n\nAn atom and its associated information.\n\nProperties unused in the simulation or in analysis can be left with their default values. The types used should be bits types if the GPU is going to be used.\n\nArguments\n\nindex::Int: the index of the atom in the system.\natom_type::T: the type of the atom.\nmass::M=1.0u\"g/mol\": the mass of the atom.\ncharge::C=0.0: the charge of the atom, used for electrostatic interactions.\nσ::S=0.0u\"nm\": the Lennard-Jones finite distance at which the inter-particle potential is zero.\nϵ::E=0.0u\"kJ * mol^-1\": the Lennard-Jones depth of the potential well.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.AtomData","page":"API","title":"Molly.AtomData","text":"AtomData(atom_type, atom_name, res_number, res_name)\n\nData associated with an atom.\n\nStoring this separately allows the Atom types to be bits types and hence work on the GPU.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.AtomType","page":"API","title":"Molly.AtomType","text":"AtomType(type, class, element, charge, mass, σ, ϵ)\n\nAn atom type.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.AverageObservableLogger","page":"API","title":"Molly.AverageObservableLogger","text":"AverageObservableLogger(observable::Function, T::DataType, n_steps::Integer;\n n_blocks::Integer=1024)\n\nA logger that periodically records observations of a system and keeps a running empirical average.\n\nWhile GeneralObservableLogger holds a full record of observations, AverageObservableLogger does not. In addition, calling values(logger::AverageObservableLogger; std::Bool=true) returns two values: the current running average, and an estimate of the standard deviation for this average based on the block averaging method described in Flyvbjerg and Petersen 1989.\n\nArguments\n\nobservable::Function: the observable whose mean is recorded, must support the method observable(s::System, neighbors; n_threads::Integer).\nT::DataType: the type returned by observable.\nn_steps::Integer: number of simulation steps between observations.\nn_blocks::Integer=1024: the number of blocks used in the block averaging method, should be an even number.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.BerendsenThermostat","page":"API","title":"Molly.BerendsenThermostat","text":"BerendsenThermostat(temperature, coupling_const)\n\nThe Berendsen thermostat for controlling temperature.\n\nThe scaling factor for the velocities each step is\n\nlambda^2 = 1 + fracdelta ttau left( fracT_0T - 1 right)\n\nThis thermostat should be used with caution as it can lead to simulation artifacts.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.Buckingham","page":"API","title":"Molly.Buckingham","text":"Buckingham(; cutoff, use_neighbors, shortcut, A_mixing, B_mixing,\n C_mixing, weight_special)\n\nThe Buckingham interaction between two atoms.\n\nThe potential energy is defined as\n\nV(r_ij) = A_ij exp(-B_ij r_ij) - fracC_ijr_ij^6\n\nand the force on each atom by\n\nvecF_i = left( A_ij B_ij exp(-B_ij r_ij) - 6 fracC_ijr_ij^7 right) fracvecr_ijr_ij\n\nThe parameters are derived from the atom parameters according to\n\nbeginaligned\nA_ij = (A_ii A_jj)^12 \nB_ij = frac2frac1B_ii + frac1B_jj \nC_ij = (C_ii C_jj)^12\nendaligned\n\nso atoms that use this interaction should have fields A, B and C available.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.CellListMapNeighborFinder","page":"API","title":"Molly.CellListMapNeighborFinder","text":"CellListMapNeighborFinder(; eligible, dist_cutoff, special, n_steps, x0, unit_cell)\n\nFind close atoms by distance using a cell list algorithm from CellListMap.jl.\n\nx0 and unit_cell are optional initial coordinates and system unit cell that improve the first approximation of the cell list structure. Can not be used if one or more dimensions has infinite boundaries.\n\nExample\n\njulia> coords\n15954-element Vector{SVector{3, Quantity{Float64, 𝐋, Unitful.FreeUnits{(nm,), 𝐋, nothing}}}}:\n [2.5193063341012127 nm, 3.907448346081021 nm, 4.694954671434135 nm]\n [2.4173958848835233 nm, 3.916034913604175 nm, 4.699661024574953 nm]\n ⋮\n [1.818842280373283 nm, 5.592152965227421 nm, 4.992100424805031 nm]\n [1.7261366568663976 nm, 5.610326185704369 nm, 5.084523386833478 nm]\n\njulia> boundary\nCubicBoundary{Quantity{Float64, 𝐋, Unitful.FreeUnits{(nm,), 𝐋, nothing}}}(Quantity{Float64, 𝐋, Unitful.FreeUnits{(nm,), 𝐋, nothing}}[5.676 nm, 5.6627 nm, 6.2963 nm])\n\njulia> neighbor_finder = CellListMapNeighborFinder(\n eligible=s.neighbor_finder.eligible, dist_cutoff=1.2u\"nm\",\n special=s.neighbor_finder.special, n_steps=10,\n x0=coords, unit_cell=boundary,\n )\nCellListMapNeighborFinder{Quantity{Float64, 𝐋, Unitful.FreeUnits{(nm,), 𝐋, nothing}}, 3, Float64}\n Size of eligible matrix = (15954, 15954)\n n_steps = 10\n dist_cutoff = 1.2 nm\n\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.CosineAngle","page":"API","title":"Molly.CosineAngle","text":"CosineAngle(; k, θ0)\n\nA cosine bond angle between three atoms.\n\nθ0 is in radians. The potential energy is defined as\n\nV(theta) = k(1 + cos(theta - theta_0))\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.Coulomb","page":"API","title":"Molly.Coulomb","text":"Coulomb(; cutoff, use_neighbors, weight_special, coulomb_const)\n\nThe Coulomb electrostatic interaction between two atoms.\n\nThe potential energy is defined as\n\nV(r_ij) = fracq_i q_j4 pi varepsilon_0 r_ij\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.CoulombReactionField","page":"API","title":"Molly.CoulombReactionField","text":"CoulombReactionField(; dist_cutoff, solvent_dielectric, use_neighbors, weight_special,\n coulomb_const)\n\nThe Coulomb electrostatic interaction modified using the reaction field approximation between two atoms.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.CoulombSoftCore","page":"API","title":"Molly.CoulombSoftCore","text":"CoulombSoftCore(; cutoff, α, λ, p, use_neighbors, σ_mixing, weight_special, coulomb_const)\n\nThe Coulomb electrostatic interaction between two atoms with a soft core.\n\nThe potential energy is defined as\n\nV(r_ij) = fracq_i q_j4 pi varepsilon_0 (r_ij^6 + alpha sigma_ij^6 lambda^p)^frac16\n\nIf alpha or lambda are zero this gives the standard Coulomb potential.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.CubicBoundary","page":"API","title":"Molly.CubicBoundary","text":"CubicBoundary(x, y, z)\nCubicBoundary(x)\n\nCubic 3D bounding box defined by three side lengths.\n\nIf one length is given then all three sides will have that length. Setting one or more values to Inf gives no boundary in that dimension.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.CubicSplineCutoff","page":"API","title":"Molly.CubicSplineCutoff","text":"CubicSplineCutoff(dist_activation, dist_cutoff)\n\nCutoff that interpolates the true potential and zero between an activation point and a cutoff point, using a cubic Hermite spline.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.DistanceConstraint","page":"API","title":"Molly.DistanceConstraint","text":"DistanceConstraint(i, j, dist)\n\nConstraint between two atoms that maintains a fixed distance between the two atoms.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.DistanceCutoff","page":"API","title":"Molly.DistanceCutoff","text":"DistanceCutoff(dist_cutoff)\n\nCutoff that sets the potential and force to be zero past a specified cutoff point.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.DistanceNeighborFinder","page":"API","title":"Molly.DistanceNeighborFinder","text":"DistanceNeighborFinder(; eligible, dist_cutoff, special, n_steps)\n\nFind close atoms by distance.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.FENEBond","page":"API","title":"Molly.FENEBond","text":"FENEBond(; k, r0, σ, ϵ)\n\nA finitely extensible non-linear elastic (FENE) bond between two atoms, see Kremer and Grest 1990.\n\nThe potential energy is defined as\n\nV(r) = -frac12 k r^2_0 ln left( 1 - left( fracrr_0 right) ^2 right) + V_textWCA(r)\n\nwhere the WCA contribution is given by\n\nV_textWCA(r) =\n begincases\n 4varepsilon left left( fracsigmar right) ^12 - left( fracsigmar right) ^6 right + varepsilon r 2^16sigma\n 0 r geq 2^16sigma\n endcases\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.GeneralObservableLogger","page":"API","title":"Molly.GeneralObservableLogger","text":"GeneralObservableLogger(observable::Function, T, n_steps)\n\nA logger which holds a record of regularly sampled observations of a system.\n\nobservable should return an object of type T and support the method observable(s::System, neighbors; n_threads::Integer)::T.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.Gravity","page":"API","title":"Molly.Gravity","text":"Gravity(; G, use_neighbors)\n\nThe gravitational interaction between two atoms.\n\nThe potential energy is defined as\n\nV(r_ij) = -fracG m_i m_jr_ij\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.HamiltonianREMD","page":"API","title":"Molly.HamiltonianREMD","text":"HamiltonianREMD(; )\n\nA simulator for a parallel Hamiltonian replica exchange MD (H-REMD) simulation on a ReplicaSystem.\n\nThe replicas are expected to have different Hamiltonians, i.e. different interactions. When calling simulate!, the assign_velocities keyword argument determines whether to assign random velocities at the appropriate temperature for each replica.\n\nArguments\n\ndt::DT: the time step of the simulation.\ntemperature::T: the temperatures of the simulation.\nsimulators::ST: individual simulators for simulating each replica.\nexchange_time::ET: the time interval between replica exchange attempts.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.HarmonicAngle","page":"API","title":"Molly.HarmonicAngle","text":"HarmonicAngle(; k, θ0)\n\nA harmonic bond angle between three atoms.\n\nθ0 is in radians. The second atom is the middle atom. The potential energy is defined as\n\nV(theta) = frac12 k (theta - theta_0)^2\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.HarmonicBond","page":"API","title":"Molly.HarmonicBond","text":"HarmonicBond(; k, r0)\n\nA harmonic bond between two atoms.\n\nThe potential energy is defined as\n\nV(r) = frac12 k (r - r_0)^2\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.HarmonicPositionRestraint","page":"API","title":"Molly.HarmonicPositionRestraint","text":"HarmonicPositionRestraint(; k, x0)\n\nA harmonic position restraint on an atom to coordinate x0.\n\nThe potential energy is defined as\n\nV(boldsymbolx) = frac12 k boldsymbolx - boldsymbolx_0^2\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.ImplicitSolventGBN2","page":"API","title":"Molly.ImplicitSolventGBN2","text":"ImplicitSolventGBN2(atoms, atoms_data, bonds)\n\nGBn2 solvation model implemented as an AtomsCalculators.jl calculator.\n\nShould be used along with a Coulomb or CoulombReactionField interaction.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.ImplicitSolventOBC","page":"API","title":"Molly.ImplicitSolventOBC","text":"ImplicitSolventOBC(atoms, atoms_data, bonds)\n\nOnufriev-Bashford-Case GBSA model implemented as an AtomsCalculators.jl calculator.\n\nShould be used along with a Coulomb or CoulombReactionField interaction. The keyword argument use_OBC2 determines whether to use parameter set I (false, the default) or II (true).\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.InteractionList1Atoms","page":"API","title":"Molly.InteractionList1Atoms","text":"InteractionList1Atoms(is, inters)\nInteractionList1Atoms(is, inters, types)\nInteractionList1Atoms(inter_type)\n\nA list of specific interactions that involve one atom such as position restraints.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.InteractionList2Atoms","page":"API","title":"Molly.InteractionList2Atoms","text":"InteractionList2Atoms(is, js, inters)\nInteractionList2Atoms(is, js, inters, types)\nInteractionList2Atoms(inter_type)\n\nA list of specific interactions that involve two atoms such as bond potentials.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.InteractionList3Atoms","page":"API","title":"Molly.InteractionList3Atoms","text":"InteractionList3Atoms(is, js, ks, inters)\nInteractionList3Atoms(is, js, ks, inters, types)\nInteractionList3Atoms(inter_type)\n\nA list of specific interactions that involve three atoms such as bond angle potentials.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.InteractionList4Atoms","page":"API","title":"Molly.InteractionList4Atoms","text":"InteractionList4Atoms(is, js, ks, ls, inters)\nInteractionList4Atoms(is, js, ks, ls, inters, types)\nInteractionList4Atoms(inter_type)\n\nA list of specific interactions that involve four atoms such as torsion potentials.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.Langevin","page":"API","title":"Molly.Langevin","text":"Langevin(; )\n\nThe Langevin integrator, based on the Langevin Middle Integrator in OpenMM.\n\nThis is a leapfrog integrator, so the velocities are offset by half a time step behind the positions.\n\nArguments\n\ndt::S: the time step of the simulation.\ntemperature::K: the equilibrium temperature of the simulation.\nfriction::F: the friction coefficient of the simulation.\ncoupling::C=NoCoupling(): the coupling which applies during the simulation.\nremove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.LangevinSplitting","page":"API","title":"Molly.LangevinSplitting","text":"LangevinSplitting(; )\n\nThe Langevin simulator using a general splitting scheme.\n\nThis consists of a succession of A, B and O steps, corresponding respectively to updates in position, velocity for the potential part, and velocity for the thermal fluctuation-dissipation part. The Langevin and VelocityVerlet simulators without coupling correspond to the BAOA and BAB schemes respectively. For more information on the sampling properties of splitting schemes, see Fass et al. 2018.\n\nNot currently compatible with constraints, will print a warning and continue without applying constraints.\n\nArguments\n\ndt::S: the time step of the simulation.\ntemperature::K: the equilibrium temperature of the simulation.\nfriction::F: the friction coefficient. If units are used, it should have a dimensionality of mass per time.\nsplitting::W: the splitting specifier. Should be a string consisting of the characters A, B and O. Strings with no Os reduce to deterministic symplectic schemes.\nremove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.LennardJones","page":"API","title":"Molly.LennardJones","text":"LennardJones(; cutoff, use_neighbors, shortcut, σ_mixing, ϵ_mixing, weight_special)\n\nThe Lennard-Jones 6-12 interaction between two atoms.\n\nThe potential energy is defined as\n\nV(r_ij) = 4varepsilon_ij leftleft(fracsigma_ijr_ijright)^12 - left(fracsigma_ijr_ijright)^6right\n\nand the force on each atom by\n\nbeginaligned\nvecF_i = 24varepsilon_ij left(2fracsigma_ij^12r_ij^13 - fracsigma_ij^6r_ij^7right) fracvecr_ijr_ij \n= frac24varepsilon_ijr_ij^2 left2left(fracsigma_ij^6r_ij^6right)^2 -left(fracsigma_ijr_ijright)^6right vecr_ij\nendaligned\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.LennardJonesSoftCore","page":"API","title":"Molly.LennardJonesSoftCore","text":"LennardJonesSoftCore(; cutoff, α, λ, p, use_neighbors, shortcut, σ_mixing, ϵ_mixing,\n weight_special)\n\nThe Lennard-Jones 6-12 interaction between two atoms with a soft core.\n\nThe potential energy is defined as\n\nV(r_ij) = 4varepsilon_ij leftleft(fracsigma_ijr_ij^textscright)^12 - left(fracsigma_ijr_ij^textscright)^6right\n\nand the force on each atom by\n\nvecF_i = 24varepsilon_ij left(2fracsigma_ij^12(r_ij^textsc)^13 - fracsigma_ij^6(r_ij^textsc)^7right) left(fracr_ijr_ij^textscright)^5 fracvecr_ijr_ij\n\nwhere\n\nr_ij^textsc = left(r_ij^6 + alpha sigma_ij^6 lambda^p right)^16\n\nIf alpha or lambda are zero this gives the standard LennardJones potential.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MetropolisMonteCarlo","page":"API","title":"Molly.MetropolisMonteCarlo","text":"MetropolisMonteCarlo(; )\n\nA Monte Carlo simulator that uses the Metropolis algorithm to sample the configuration space.\n\nArguments\n\ntemperature::T: the temperature of the system.\ntrial_moves::M: a function that performs the trial moves.\ntrial_args::Dict: a dictionary of arguments to be passed to the trial move function.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.Mie","page":"API","title":"Molly.Mie","text":"Mie(; m, n, cutoff, use_neighbors, shortcut, σ_mixing, ϵ_mixing)\n\nThe Mie generalized interaction between two atoms.\n\nWhen m equals 6 and n equals 12 this is equivalent to the Lennard-Jones interaction. The potential energy is defined as\n\nV(r_ij) = C varepsilon_ij leftleft(fracsigma_ijr_ijright)^n - left(fracsigma_ijr_ijright)^mright\n\nwhere\n\nC = fracnn - m left( fracnm right) ^fracmn - m\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MolecularForceField","page":"API","title":"Molly.MolecularForceField","text":"MolecularForceField(ff_files...; units=true)\nMolecularForceField(T, ff_files...; units=true)\nMolecularForceField(atom_types, residue_types, bond_types, angle_types,\n torsion_types, torsion_order, weight_14_coulomb,\n weight_14_lj, attributes_from_residue)\n\nA molecular force field.\n\nRead one or more OpenMM force field XML files by passing them to the constructor.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MolecularTopology","page":"API","title":"Molly.MolecularTopology","text":"MolecularTopology(bond_is, bond_js, n_atoms)\nMolecularTopology(atom_molecule_inds, molecule_atom_counts)\n\nTopology information for a system.\n\nStores the index of the molecule each atom belongs to and the number of atoms in each molecule.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MollyCalculator","page":"API","title":"Molly.MollyCalculator","text":"MollyCalculator(; )\n\nA calculator for use with the AtomsCalculators.jl interface.\n\nneighbors can optionally be given as a keyword argument when calling the calculation functions to save on computation when the neighbors are the same for multiple calls. In a similar way, n_threads can be given to determine the number of threads to use when running the calculation function. Note that this calculator is designed for using Molly in other contexts; if you want to use another calculator in Molly it can be given as general_inters when creating a System.\n\nNot currently compatible with virial calculation. Not currently compatible with using atom properties such as σ and ϵ.\n\nArguments\n\npairwise_inters::PI=(): the pairwise interactions in the system, i.e. interactions between all or most atom pairs such as electrostatics. Typically a Tuple.\nspecific_inter_lists::SI=(): the specific interactions in the system, i.e. interactions between specific atoms such as bonds or angles. Typically a Tuple.\ngeneral_inters::GI=(): the general interactions in the system, i.e. interactions involving all atoms such as implicit solvent. Each should implement the AtomsCalculators.jl interface. Typically a Tuple.\nneighbor_finder::NF=NoNeighborFinder(): the neighbor finder used to find close atoms and save on computation.\nforce_units::F=u\"kJ * mol^-1 * nm^-1\": the units of force of the system. Should be set to NoUnits if units are not being used.\nenergy_units::E=u\"kJ * mol^-1\": the units of energy of the system. Should be set to NoUnits if units are not being used.\nk::K=Unitful.k or Unitful.k * Unitful.Na: the Boltzmann constant, which may be modified in some simulations. k is chosen based on the energy_units given.\ndims::Integer=3: the number of dimensions in the system.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MonteCarloAnisotropicBarostat","page":"API","title":"Molly.MonteCarloAnisotropicBarostat","text":"MonteCarloAnisotropicBarostat(pressure, temperature, boundary; n_steps=30,\n n_iterations=1, scale_factor=0.01, scale_increment=1.1,\n max_volume_frac=0.3, trial_find_neighbors=false)\n\nThe Monte Carlo anisotropic barostat for controlling pressure.\n\nFor 3D systems, pressure is a SVector of length 3 with components pressX, pressY, and pressZ representing the target pressure in each axis. For 2D systems, pressure is a SVector of length 2 with components pressX and pressY. To keep an axis fixed, set the corresponding pressure to nothing.\n\nSee Chow and Ferguson 1995, Åqvist et al. 2004 and the OpenMM source code. At regular intervals a Monte Carlo step is attempted by scaling the coordinates and the bounding box by a randomly chosen amount in a randomly selected axis. The step is accepted or rejected based on\n\nDelta W = Delta E + P Delta V - N k_B T ln left( fracV + Delta VV right)\n\nwhere ΔE is the change in potential energy, P is the equilibrium pressure along the selected axis, ΔV is the change in volume, N is the number of molecules in the system, T is the equilibrium temperature and V is the system volume. If ΔW ≤ 0 the step is always accepted, if ΔW > 0 the step is accepted with probability exp(-ΔW/kT).\n\nThe scale factor is modified over time to maintain an acceptance rate of around half. If the topology of the system is set then molecules are moved as a unit so properties such as bond lengths do not change.\n\nThe barostat assumes that the simulation is being run at a constant temperature but does not actively control the temperature. It should be used alongside a temperature coupling method such as the Langevin simulator or AndersenThermostat coupling. The neighbor list is not updated when making trial moves or after accepted moves. Note that the barostat can change the bounding box of the system.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MonteCarloBarostat","page":"API","title":"Molly.MonteCarloBarostat","text":"MonteCarloBarostat(pressure, temperature, boundary; n_steps=30, n_iterations=1,\n scale_factor=0.01, scale_increment=1.1, max_volume_frac=0.3,\n trial_find_neighbors=false)\n\nThe Monte Carlo barostat for controlling pressure.\n\nSee Chow and Ferguson 1995, Åqvist et al. 2004 and the OpenMM source code. At regular intervals a Monte Carlo step is attempted by scaling the coordinates and the bounding box by a randomly chosen amount. The step is accepted or rejected based on\n\nDelta W = Delta E + P Delta V - N k_B T ln left( fracV + Delta VV right)\n\nwhere ΔE is the change in potential energy, P is the equilibrium pressure, ΔV is the change in volume, N is the number of molecules in the system, T is the equilibrium temperature and V is the system volume. If ΔW ≤ 0 the step is always accepted, if ΔW > 0 the step is accepted with probability exp(-ΔW/kT).\n\nThe scale factor is modified over time to maintain an acceptance rate of around half. If the topology of the system is set then molecules are moved as a unit so properties such as bond lengths do not change.\n\nThe barostat assumes that the simulation is being run at a constant temperature but does not actively control the temperature. It should be used alongside a temperature coupling method such as the Langevin simulator or AndersenThermostat coupling. The neighbor list is not updated when making trial moves or after accepted moves. Note that the barostat can change the bounding box of the system.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MonteCarloLogger","page":"API","title":"Molly.MonteCarloLogger","text":"MonteCarloLogger()\nMonteCarloLogger(T)\n\nA logger that records acceptances in a Monte Carlo simulation.\n\nThe logged quantities include the number of new selections (n_select), the number of successful acceptances (n_accept), an array named energy_rates which stores the value of fracEk_B T i.e. the argument of the Boltzmann factor for the states, and a BitVector named state_changed that stores whether a new state was accepted for the logged step.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MonteCarloMembraneBarostat","page":"API","title":"Molly.MonteCarloMembraneBarostat","text":"MonteCarloMembraneBarostat(pressure, tension, temperature, boundary; n_steps=30,\n n_iterations=1, scale_factor=0.01, scale_increment=1.1,\n max_volume_frac=0.3, trial_find_neighbors=false,\n xy_isotropy=false, z_axis_fixed=false, constant_volume=false)\n\nThe Monte Carlo membrane barostat for controlling pressure.\n\nSet the xy_isotropy flag to true to scale the x and y axes isotropically. Set the z_axis_fixed flag to true to uncouple the z-axis and keep it fixed. Set the constant_volume flag to true to keep the system volume constant by scaling the z-axis accordingly. The z_axis_fixed and constant_volume flags cannot be true simultaneously.\n\nSee Chow and Ferguson 1995, Åqvist et al. 2004 and the OpenMM source code. At regular intervals a Monte Carlo step is attempted by scaling the coordinates and the bounding box by a randomly chosen amount in a randomly selected axis. The step is accepted or rejected based on\n\nDelta W = Delta E + P Delta V - gamma Delta A - N k_B T ln left( fracV + Delta VV right)\n\nwhere ΔE is the change in potential energy, P is the equilibrium pressure along the selected axis, ΔV is the change in volume, γ is the surface tension, ΔA is the change in surface area, N is the number of molecules in the system, T is the equilibrium temperature and V is the system volume. If ΔW ≤ 0 the step is always accepted, if ΔW > 0 the step is accepted with probability exp(-ΔW/kT).\n\nThe scale factor is modified over time to maintain an acceptance rate of around half. If the topology of the system is set then molecules are moved as a unit so properties such as bond lengths do not change.\n\nThe barostat assumes that the simulation is being run at a constant temperature but does not actively control the temperature. It should be used alongside a temperature coupling method such as the Langevin simulator or AndersenThermostat coupling. The neighbor list is not updated when making trial moves or after accepted moves. Note that the barostat can change the bounding box of the system.\n\nThis barostat is only available for 3D systems.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MorseBond","page":"API","title":"Molly.MorseBond","text":"MorseBond(; D, a, r0)\n\nA Morse potential bond between two atoms.\n\nThe potential energy is defined as\n\nV(r) = D(1 - e^-a(r - r_0))^2\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.MullerBrown","page":"API","title":"Molly.MullerBrown","text":"MullerBrown(; A, a, b, c, x0, y0, force_units, energy_units)\n\nThe Müller-Brown potential energy surface implemented as an AtomsCalculators.jl calculator.\n\nThe potential energy is defined as\n\nV(xy) = sum_n=1^4 A_k expa_k(x-x_k^0)^2 + b_k(x-x_k^0)(y-y_k^0) + c_k(y-y_k^0)^2\n\nwhere A, a, b, c, x0, y0 are 4-element SVectors with standard defaults.\n\nThis potential is only compatible with 2D systems. It is often used for testing algorithms that find transition states or explore minimum energy pathways. There are 3 minima and 2 saddle points with the default parameters.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.NeighborList","page":"API","title":"Molly.NeighborList","text":"NeighborList(n, list)\nNeighborList()\n\nStructure to contain neighbor lists.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.NoCoupling","page":"API","title":"Molly.NoCoupling","text":"NoCoupling()\n\nPlaceholder coupler that does nothing.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.NoCutoff","page":"API","title":"Molly.NoCutoff","text":"NoCutoff()\n\nPlaceholder cutoff that does not alter forces or potentials.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.NoNeighborFinder","page":"API","title":"Molly.NoNeighborFinder","text":"NoNeighborFinder()\n\nPlaceholder neighbor finder that returns no neighbors.\n\nWhen using this neighbor finder, ensure that use_neighbors for the interactions returns false.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.NoseHoover","page":"API","title":"Molly.NoseHoover","text":"NoseHoover(; )\n\nThe Nosé-Hoover integrator, a NVT simulator that extends velocity Verlet to control the temperature of the system.\n\nSee Evans and Holian 1985. The current implementation is limited to ergodic systems.\n\nNot currently compatible with constraints, will print a warning and continue without applying constraints.\n\nArguments\n\ndt::T: the time step of the simulation.\ntemperature::K: the equilibrium temperature of the simulation.\ndamping::D=100*dt: the temperature damping time scale.\ncoupling::C=NoCoupling(): the coupling which applies during the simulation.\nremove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.OverdampedLangevin","page":"API","title":"Molly.OverdampedLangevin","text":"OverdampedLangevin(; )\n\nSimulates the overdamped Langevin equation using the Euler-Maruyama method.\n\nNot currently compatible with constraints, will print a warning and continue without applying constraints.\n\nArguments\n\ndt::S: the time step of the simulation.\ntemperature::K: the equilibrium temperature of the simulation.\nfriction::F: the friction coefficient of the simulation.\nremove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.PeriodicTorsion","page":"API","title":"Molly.PeriodicTorsion","text":"PeriodicTorsion(; periodicities, phases, ks, proper)\n\nA periodic torsion angle between four atoms.\n\nphases are in radians. The potential energy is defined as\n\nV(phi) = sum_n=1^N k_n (1 + cos(n phi - phi_sn))\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.PeriodicTorsionType","page":"API","title":"Molly.PeriodicTorsionType","text":"PeriodicTorsionType(periodicities, phases, ks, proper)\n\nA periodic torsion type.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.RBTorsion","page":"API","title":"Molly.RBTorsion","text":"RBTorsion(; f1, f2, f3, f4)\n\nA Ryckaert-Bellemans torsion angle between four atoms.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.RectangularBoundary","page":"API","title":"Molly.RectangularBoundary","text":"RectangularBoundary(x, y)\nRectangularBoundary(x)\n\nRectangular 2D bounding box defined by two side lengths.\n\nIf one length is given then both sides will have that length. Setting one or more values to Inf gives no boundary in that dimension.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.ReplicaExchangeLogger","page":"API","title":"Molly.ReplicaExchangeLogger","text":"ReplicaExchangeLogger(n_replicas)\nReplicaExchangeLogger(T, n_replicas)\n\nA logger that records exchanges in a replica exchange simulation.\n\nThe logged quantities include the number of exchange attempts (n_attempts), number of successful exchanges (n_exchanges), exchanged replica indices (indices), exchange steps (steps) and the value of Δ i.e. the argument of Metropolis rate for the exchanges (deltas).\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.ReplicaSystem","page":"API","title":"Molly.ReplicaSystem","text":"ReplicaSystem(; )\n\nA wrapper for replicas in a replica exchange simulation.\n\nEach individual replica is a System. Properties unused in the simulation or in analysis can be left with their default values. The minimal required arguments are atoms, replica_coords, boundary and n_replicas. atoms and the elements in replica_coords should have the same length, along with atoms_data and the elements in replica_velocities if these are provided. The number of elements in replica_coords, replica_velocities, replica_loggers and the interaction arguments replica_pairwise_inters, replica_specific_inter_lists, replica_general_inters and replica_constraints should be equal to n_replicas. This is a sub-type of AbstractSystem from AtomsBase.jl and implements the interface described there.\n\nWhen using ReplicaSystem with CellListMapNeighborFinder, the number of threads used for both the simulation of replicas and the neighbor finder should be set to be the same. This can be done by passing nbatches=(min(n, 8), n) to CellListMapNeighborFinder during construction where n is the number of threads to be used per replica.\n\nArguments\n\natoms::A: the atoms, or atom equivalents, in the system. Can be of any type but should be a bits type if the GPU is used.\nreplica_coords: the coordinates of the atoms in each replica.\nboundary::B: the bounding box in which the simulation takes place.\nn_replicas::Integer: the number of replicas of the system.\nreplica_velocities=[zero(replica_coords[1]) * u\"ps^-1\" for _ in 1:n_replicas]: the velocities of the atoms in each replica.\natoms_data::AD: other data associated with the atoms, allowing the atoms to be bits types and hence work on the GPU.\ntopology::TO=nothing: topological information about the system such as which atoms are in the same molecule (to be used if the same for all replicas). This is only used if no value is passed to the argument replica_topology.\nreplica_topology=[nothing for _ in 1:n_replicas]: the topological information for each replica.\npairwise_inters=(): the pairwise interactions in the system, i.e. interactions between all or most atom pairs such as electrostatics (to be used if the same for all replicas). Typically a Tuple. This is only used if no value is passed to the argument replica_pairwise_inters.\nreplica_pairwise_inters=[() for _ in 1:n_replicas]: the pairwise interactions for each replica.\nspecific_inter_lists=(): the specific interactions in the system, i.e. interactions between specific atoms such as bonds or angles (to be used if the same for all replicas). Typically a Tuple. This is only used if no value is passed to the argument replica_specific_inter_lists.\nreplica_specific_inter_lists=[() for _ in 1:n_replicas]: the specific interactions in each replica.\ngeneral_inters=(): the general interactions in the system, i.e. interactions involving all atoms such as implicit solvent (to be used if the same for all replicas). Each should implement the AtomsCalculators.jl interface. Typically a Tuple. This is only used if no value is passed to the argument replica_general_inters.\nreplica_general_inters=[() for _ in 1:n_replicas]: the general interactions for each replica.\nconstraints::CN=(): the constraints for bonds and angles in the system (to be used if the same for all replicas). Typically a Tuple. This is only used if no value is passed to the argument replica_constraints.\nreplica_constraints=[() for _ in 1:n_replicas]: the constraints for bonds and angles in each replica.\nneighbor_finder::NF=NoNeighborFinder(): the neighbor finder used to find close atoms and save on computation. It is duplicated for each replica.\nreplica_loggers=[() for _ in 1:n_replicas]: the loggers for each replica that record properties of interest during a simulation.\nexchange_logger::EL=ReplicaExchangeLogger(n_replicas): the logger used to record the exchange of replicas.\nforce_units::F=u\"kJ * mol^-1 * nm^-1\": the units of force of the system. Should be set to NoUnits if units are not being used.\nenergy_units::E=u\"kJ * mol^-1\": the units of energy of the system. Should be set to NoUnits if units are not being used.\nk::K=Unitful.k or Unitful.k * Unitful.Na: the Boltzmann constant, which may be modified in some simulations. k is chosen based on the energy_units given.\ndata::DA=nothing: arbitrary data associated with the replica system.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.RescaleThermostat","page":"API","title":"Molly.RescaleThermostat","text":"RescaleThermostat(temperature)\n\nThe velocity rescaling thermostat for controlling temperature.\n\nVelocities are immediately rescaled to match a target temperature. The scaling factor for the velocities each step is\n\nlambda = sqrtfracT_0T\n\nThis thermostat should be used with caution as it can lead to simulation artifacts.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.ResidueType","page":"API","title":"Molly.ResidueType","text":"ResidueType(name, types, charges, indices)\n\nA residue type.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.SHAKE_RATTLE","page":"API","title":"Molly.SHAKE_RATTLE","text":"SHAKE_RATTLE(constraints, n_atoms, dist_tolerance, vel_tolerance)\n\nConstrain distances during a simulation using the SHAKE and RATTLE algorithms.\n\nVelocity constraints will be imposed for simulators that integrate velocities such as VelocityVerlet. See Ryckaert et al. 1977 for SHAKE, Andersen 1983 for RATTLE and Elber et al. 2011 for a derivation of the linear system solved to satisfy the RATTLE algorithm.\n\nNot currently compatible with GPU simulation.\n\nArguments\n\nconstraints: a vector of constraints to be imposed on the system.\nn_atoms::Integer: the number of atoms in the system.\ndist_tolerance: the tolerance used to end the iterative procedure when calculating position constraints, should have the same units as the coordinates.\nvel_tolerance: the tolerance used to end the iterative procedure when calculating velocity constraints, should have the same units as the velocities * the coordinates.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.ShiftedForceCutoff","page":"API","title":"Molly.ShiftedForceCutoff","text":"ShiftedForceCutoff(dist_cutoff)\n\nCutoff that shifts the force to be continuous at a specified cutoff point.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.ShiftedPotentialCutoff","page":"API","title":"Molly.ShiftedPotentialCutoff","text":"ShiftedPotentialCutoff(dist_cutoff)\n\nCutoff that shifts the potential to be continuous at a specified cutoff point.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.SoftSphere","page":"API","title":"Molly.SoftSphere","text":"SoftSphere(; cutoff, use_neighbors, shortcut, σ_mixing, ϵ_mixing)\n\nThe soft-sphere potential.\n\nThe potential energy is defined as\n\nV(r_ij) = 4varepsilon_ij left(fracsigma_ijr_ijright)^12\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.SpecificForce1Atoms","page":"API","title":"Molly.SpecificForce1Atoms","text":"SpecificForce1Atoms(f1)\n\nForce on one atom arising from an interaction such as a position restraint.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.SpecificForce2Atoms","page":"API","title":"Molly.SpecificForce2Atoms","text":"SpecificForce2Atoms(f1, f2)\n\nForces on two atoms arising from an interaction such as a bond potential.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.SpecificForce3Atoms","page":"API","title":"Molly.SpecificForce3Atoms","text":"SpecificForce3Atoms(f1, f2, f3)\n\nForces on three atoms arising from an interaction such as a bond angle potential.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.SpecificForce4Atoms","page":"API","title":"Molly.SpecificForce4Atoms","text":"SpecificForce4Atoms(f1, f2, f3, f4)\n\nForces on four atoms arising from an interaction such as a torsion potential.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.SteepestDescentMinimizer","page":"API","title":"Molly.SteepestDescentMinimizer","text":"SteepestDescentMinimizer(; )\n\nSteepest descent energy minimization.\n\nArguments\n\nstep_size::D=0.01u\"nm\": the initial maximum displacement.\nmax_steps::Int=1000: the maximum number of steps.\ntol::F=1000.0u\"kJ * mol^-1 * nm^-1\": the maximum force below which to finish minimization.\nlog_stream::L=devnull: stream to print minimization progress to.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.StormerVerlet","page":"API","title":"Molly.StormerVerlet","text":"StormerVerlet(; )\n\nThe Störmer-Verlet integrator.\n\nThe velocity calculation is accurate to O(dt).\n\nDoes not currently work with coupling methods that alter the velocity. Does not currently remove the center of mass motion.\n\nArguments\n\ndt::T: the time step of the simulation.\ncoupling::C=NoCoupling(): the coupling which applies during the simulation.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.StructureWriter","page":"API","title":"Molly.StructureWriter","text":"StructureWriter(n_steps, filepath, excluded_res=String[])\n\nWrite 3D output structures to a file in the PDB format throughout a simulation.\n\nThe System should have atoms_data defined.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.System","page":"API","title":"Molly.System","text":"System(; )\n\nA physical system to be simulated.\n\nProperties unused in the simulation or in analysis can be left with their default values. The minimal required arguments are atoms, coords and boundary. atoms and coords should have the same length, along with velocities and atoms_data if these are provided. This is a sub-type of AbstractSystem from AtomsBase.jl and implements the interface described there.\n\nArguments\n\natoms::A: the atoms, or atom equivalents, in the system. Can be of any type but should be a bits type if the GPU is used.\ncoords::C: the coordinates of the atoms in the system. Typically a vector of SVectors of 2 or 3 dimensions.\nboundary::B: the bounding box in which the simulation takes place.\nvelocities::V=zero(coords) * u\"ps^-1\": the velocities of the atoms in the system.\natoms_data::AD=[]: other data associated with the atoms, allowing the atoms to be bits types and hence work on the GPU.\ntopology::TO=nothing: topological information about the system such as which atoms are in the same molecule.\npairwise_inters::PI=(): the pairwise interactions in the system, i.e. interactions between all or most atom pairs such as electrostatics. Typically a Tuple.\nspecific_inter_lists::SI=(): the specific interactions in the system, i.e. interactions between specific atoms such as bonds or angles. Typically a Tuple.\ngeneral_inters::GI=(): the general interactions in the system, i.e. interactions involving all atoms such as implicit solvent. Each should implement the AtomsCalculators.jl interface. Typically a Tuple.\nconstraints::CN=(): the constraints for bonds and angles in the system. Typically a Tuple.\nneighbor_finder::NF=NoNeighborFinder(): the neighbor finder used to find close atoms and save on computation.\nloggers::L=(): the loggers that record properties of interest during a simulation.\nforce_units::F=u\"kJ * mol^-1 * nm^-1\": the units of force of the system. Should be set to NoUnits if units are not being used.\nenergy_units::E=u\"kJ * mol^-1\": the units of energy of the system. Should be set to NoUnits if units are not being used.\nk::K=Unitful.k or Unitful.k * Unitful.Na: the Boltzmann constant, which may be modified in some simulations. k is chosen based on the energy_units given.\ndata::DA=nothing: arbitrary data associated with the system.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.System-Tuple{AbstractString, MolecularForceField}","page":"API","title":"Molly.System","text":"System(coordinate_file, force_field; )\n\nRead a coordinate file in a file format readable by Chemfiles and apply a force field to it.\n\nAtom names should exactly match residue templates - no searching of residue templates is carried out.\n\nSystem(coordinate_file, topology_file; )\nSystem(T, coordinate_file, topology_file; )\n\nRead a Gromacs coordinate file and a Gromacs topology file with all includes collapsed into one file.\n\nGromacs file reading should be considered experimental. The implicit_solvent, kappa and rename_terminal_res keyword arguments are not available when reading Gromacs files.\n\nArguments\n\nboundary=nothing: the bounding box used for simulation, read from the file by default.\nvelocities=nothing: the velocities of the atoms in the system, set to zero by default.\nloggers=(): the loggers that record properties of interest during a simulation.\nunits::Bool=true: whether to use Unitful quantities.\ngpu::Bool=false: whether to move the relevant parts of the system onto the GPU.\ndist_cutoff=1.0u\"nm\": cutoff distance for long-range interactions.\ndist_neighbors=1.2u\"nm\": cutoff distance for the neighbor list, should be greater than dist_cutoff.\ncenter_coords::Bool=true: whether to center the coordinates in the simulation box.\nuse_cell_list::Bool=true: whether to use CellListMapNeighborFinder on CPU. If false, DistanceNeighborFinder is used.\ndata=nothing: arbitrary data associated with the system.\nimplicit_solvent=nothing: specify a string to add an implicit solvent model, options are \"obc1\", \"obc2\" and \"gbn2\".\nkappa=0.0u\"nm^-1\": the kappa value for the implicit solvent model if one is used.\nrename_terminal_res=true: whether to rename the first and last residues to match the appropriate atom templates, for example the first (N-terminal) residue could be changed from \"MET\" to \"NMET\".\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.System-Tuple{System}","page":"API","title":"Molly.System","text":"System(sys; )\n\nConvenience constructor for changing properties in a System.\n\nA copy of the System is returned with the provided keyword arguments modified.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.System-Union{Tuple{AtomsBase.AbstractSystem{D}}, Tuple{D}} where D","page":"API","title":"Molly.System","text":"System(abstract_system; force_units=u\"kJ * mol^-1 * nm^-1\", energy_units=u\"kJ * mol^-1\")\n\nConvert an AtomsBase AbstractSystem to a Molly System.\n\nforce_units and energy_units should be set as appropriate. To add properties not present in the AtomsBase interface (e.g. pair potentials) use the convenience constructor System(sys::System).\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.System-Union{Tuple{SimpleCrystals.Crystal{D, A, B} where {A, B<:(AbstractVector{<:SimpleCrystals.Atom{D}})}}, Tuple{D}} where D","page":"API","title":"Molly.System","text":"System(crystal; )\n\nConstruct a System from a SimpleCrystals.jl Crystal struct.\n\nProperties unused in the simulation or in analysis can be left with their default values. atoms, atoms_data, coords and boundary are automatically calcualted from the Crystal struct. Extra atom paramaters like σ have to be added manually after construction using the convenience constructor System(sys; ).\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.TemperatureREMD","page":"API","title":"Molly.TemperatureREMD","text":"TemperatureREMD(; )\n\nA simulator for a parallel temperature replica exchange MD (T-REMD) simulation on a ReplicaSystem.\n\nSee Sugita and Okamoto 1999. The corresponding ReplicaSystem should have the same number of replicas as the number of temperatures in the simulator. When calling simulate!, the assign_velocities keyword argument determines whether to assign random velocities at the appropriate temperature for each replica.\n\nArguments\n\ndt::DT: the time step of the simulation.\ntemperatures::TP: the temperatures corresponding to the replicas.\nsimulators::ST: individual simulators for simulating each replica.\nexchange_time::ET: the time interval between replica exchange attempts.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.TimeCorrelationLogger","page":"API","title":"Molly.TimeCorrelationLogger","text":"TimeCorrelationLogger(observableA::Function, observableB::Function,\n TA::DataType, TB::DataType,\n observable_length::Integer, n_correlation::Integer)\n\nA time correlation logger.\n\nEstimates statistical correlations of normalized form\n\nC(t)=fraclangle A_tcdot B_0rangle -langle Aranglecdot langle Branglesqrtlangle A^2ranglelangle B^2rangle\n\nor unnormalized form\n\nC(t)=langle A_tcdot B_0rangle -langle A ranglecdot langle Brangle\n\nThese can be used to estimate statistical error, or to compute transport coefficients from Green-Kubo type formulas. A and B are observables, functions of the form observable(sys::System, neighbors; n_threads::Integer). The return values of A and B can be of scalar or vector type (including Vector{SVector{...}}, like positions or velocities) and must implement dot.\n\nn_correlation should typically be chosen so that dt * n_correlation > t_corr, where dt is the simulation timestep and t_corr is the decorrelation time for the considered system and observables. For the purpose of numerical stability, the logger internally records sums instead of running averages. The normalized and unnormalized form of the correlation function can be retrieved through values(logger::TimeCorrelationLogger; normalize::Bool).\n\nArguments\n\nobservableA::Function: the function corresponding to observable A.\nobservableB::Function: the function corresponding to observable B.\nTA::DataType: the type returned by observableA, supporting zero(TA).\nTB::DataType: the type returned by observableB, supporting zero(TB).\nobservable_length::Integer: the length of the observables if they are vectors, or 1 if they are scalar-valued.\nn_correlation::Integer: the length of the computed correlation vector.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.TreeNeighborFinder","page":"API","title":"Molly.TreeNeighborFinder","text":"TreeNeighborFinder(; eligible, dist_cutoff, special, n_steps)\n\nFind close atoms by distance using a tree search.\n\nCan not be used if one or more dimensions has infinite boundaries. Can not be used with TriclinicBoundary.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.TriclinicBoundary","page":"API","title":"Molly.TriclinicBoundary","text":"TriclinicBoundary(v1, v2, v3; approx_images=true)\nTriclinicBoundary(SVector(v1, v2, v3); approx_images=true)\nTriclinicBoundary(SVector(l1, l2, l3), SVector(α, β, γ); approx_images=true)\nTriclinicBoundary(arr; approx_images=true)\n\nTriclinic 3D bounding box defined by 3 SVector{3} basis vectors or basis vector lengths and angles α/β/γ in radians.\n\nThe first basis vector must point along the x-axis and the second must lie in the xy plane. An approximation is used to find the closest periodic image when using the minimum image convention. The approximation is correct for distances shorter than half the shortest box height/width. Setting the keyword argument approx_images to false means the exact closest image is found, which is slower.\n\nNot currently able to simulate a cubic box, use CubicBoundary or small offsets instead. Not currently compatible with infinite boundaries.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.VelocityVerlet","page":"API","title":"Molly.VelocityVerlet","text":"VelocityVerlet(; )\n\nThe velocity Verlet integrator.\n\nArguments\n\ndt::T: the time step of the simulation.\ncoupling::C=NoCoupling(): the coupling which applies during the simulation.\nremove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.\n\n\n\n\n\n","category":"type"},{"location":"api/#Molly.Verlet","page":"API","title":"Molly.Verlet","text":"Verlet(; )\n\nThe leapfrog Verlet integrator.\n\nThis is a leapfrog integrator, so the velocities are offset by half a time step behind the positions.\n\nArguments\n\ndt::T: the time step of the simulation.\ncoupling::C=NoCoupling(): the coupling which applies during the simulation.\nremove_CM_motion=1: remove the center of mass motion every this number of steps, set to false or 0 to not remove center of mass motion.\n\n\n\n\n\n","category":"type"},{"location":"api/#Base.values-Tuple{GeneralObservableLogger}","page":"API","title":"Base.values","text":"values(logger)\nvalues(logger::TimeCorrelationLogger; normalize::Bool=true)\nvalues(logger::AverageObservableLogger; std::Bool=true)\n\nAccess the stored observations in a logger.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.AutoCorrelationLogger-Tuple{Any, Any, Integer, Integer}","page":"API","title":"Molly.AutoCorrelationLogger","text":"AutoCorrelationLogger(observable::Function, TA::DataType,\n observable_length::Integer, n_correlation::Integer)\n\nAn autocorrelation logger, equivalent to a TimeCorrelationLogger in the case that observableA == observableB.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.CoordinatesLogger-Tuple{Any, Integer}","page":"API","title":"Molly.CoordinatesLogger","text":"CoordinatesLogger(n_steps; dims=3)\nCoordinatesLogger(T, n_steps; dims=3)\n\nLog the coordinates throughout a simulation.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.DensityLogger-Tuple{Type, Integer}","page":"API","title":"Molly.DensityLogger","text":"DensityLogger(n_steps)\nDensityLogger(T, n_steps)\n\nLog the density of a system throughout a simulation.\n\nNot compatible with infinite boundaries.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.ForcesLogger-Tuple{Any, Integer}","page":"API","title":"Molly.ForcesLogger","text":"ForcesLogger(n_steps; dims=3)\nForcesLogger(T, n_steps; dims=3)\n\nLog the forces throughout a simulation.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.KineticEnergyLogger-Tuple{Type, Integer}","page":"API","title":"Molly.KineticEnergyLogger","text":"KineticEnergyLogger(n_steps)\nKineticEnergyLogger(T, n_steps)\n\nLog the kinetic_energy of a system throughout a simulation.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.PotentialEnergyLogger-Tuple{Type, Integer}","page":"API","title":"Molly.PotentialEnergyLogger","text":"PotentialEnergyLogger(n_steps)\nPotentialEnergyLogger(T, n_steps)\n\nLog the potential_energy of a system throughout a simulation.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.PressureLogger-Tuple{Type, Integer}","page":"API","title":"Molly.PressureLogger","text":"PressureLogger(n_steps)\nPressureLogger(T, n_steps)\n\nLog the pressure of a system throughout a simulation.\n\nThis should only be used on systems containing just pairwise interactions, or where the specific interactions, general interactions and constraints do not contribute to the pressure.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.TemperatureLogger-Tuple{DataType, Integer}","page":"API","title":"Molly.TemperatureLogger","text":"TemperatureLogger(n_steps)\nTemperatureLogger(T, n_steps)\n\nLog the temperature throughout a simulation.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.TotalEnergyLogger-Tuple{DataType, Any}","page":"API","title":"Molly.TotalEnergyLogger","text":"TotalEnergyLogger(n_steps)\nTotalEnergyLogger(T, n_steps)\n\nLog the total_energy of a system throughout a simulation.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.VelocitiesLogger-Tuple{Any, Integer}","page":"API","title":"Molly.VelocitiesLogger","text":"VelocitiesLogger(n_steps; dims=3)\nVelocitiesLogger(T, n_steps; dims=3)\n\nLog the velocities throughout a simulation.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.VirialLogger-Tuple{Type, Integer}","page":"API","title":"Molly.VirialLogger","text":"VirialLogger(n_steps)\nVirialLogger(T, n_steps)\n\nLog the virial of a system throughout a simulation.\n\nThis should only be used on systems containing just pairwise interactions, or where the specific interactions, general interactions and constraints do not contribute to the virial.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.VolumeLogger-Tuple{Type, Integer}","page":"API","title":"Molly.VolumeLogger","text":"VolumeLogger(n_steps)\nVolumeLogger(T, n_steps)\n\nLog the volume of a system throughout a simulation.\n\nNot compatible with infinite boundaries.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.accelerations-Tuple{Any}","page":"API","title":"Molly.accelerations","text":"accelerations(system, neighbors=find_neighbors(sys), step_n=0; n_threads=Threads.nthreads())\n\nCalculate the accelerations of all atoms in a system using the pairwise, specific and general interactions and Newton's second law of motion.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.add_position_restraints-Tuple{Any, Any}","page":"API","title":"Molly.add_position_restraints","text":"add_position_restraints(sys, k; atom_selector=is_any_atom, restrain_coords=sys.coords)\n\nReturn a copy of a System with HarmonicPositionRestraints added to restrain the atoms.\n\nThe force constant k can be a single value or an array of equal length to the number of atoms in the system. The atom_selector function takes in each atom and atom data and determines whether to restrain that atom. For example, is_heavy_atom means non-hydrogen atoms are restrained.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.apply_coupling!-Tuple{Any, Union{Tuple, NamedTuple}, Any, Any, Any}","page":"API","title":"Molly.apply_coupling!","text":"apply_coupling!(system, coupling, simulator, neighbors=nothing,\n step_n=0; n_threads=Threads.nthreads())\n\nApply a coupler to modify a simulation.\n\nReturns whether the coupling has invalidated the currently stored forces, for example by changing the coordinates. This information is useful for some simulators. If coupling is a tuple or named tuple then each coupler will be applied in turn. Custom couplers should implement this function.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.apply_loggers!","page":"API","title":"Molly.apply_loggers!","text":"apply_loggers!(system, neighbors=nothing, step_n=0, run_loggers=true;\n n_threads=Threads.nthreads(), kwargs...)\n\nRun the loggers associated with a system.\n\nrun_loggers can be true, false or :skipzero, in which case the loggers are not run before the first step. Additional keyword arguments can be passed to the loggers if required. Ignored for gradient calculation during automatic differentiation.\n\n\n\n\n\n","category":"function"},{"location":"api/#Molly.apply_position_constraints!-Tuple{Any, Any}","page":"API","title":"Molly.apply_position_constraints!","text":"apply_position_constraints!(sys, coord_storage; n_threads::Integer=Threads.nthreads())\napply_position_constraints!(sys, coord_storage, vel_storage, dt;\n n_threads::Integer=Threads.nthreads())\n\nApplies the system constraints to the coordinates.\n\nIf vel_storage and dt are provided then velocity corrections are applied as well.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.apply_velocity_constraints!-Tuple{Any}","page":"API","title":"Molly.apply_velocity_constraints!","text":"apply_velocity_constraints!(sys; n_threads::Integer=Threads.nthreads())\n\nApplies the system constraints to the velocities.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.bond_angle-NTuple{4, Any}","page":"API","title":"Molly.bond_angle","text":"bond_angle(coord_i, coord_j, coord_k, boundary)\nbond_angle(vec_ji, vec_jk)\n\nCalculate the bond or pseudo-bond angle in radians between three coordinates or two vectors.\n\nThe angle between j→i and j→k is returned in the range 0 to π.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.born_radii_and_grad-Union{Tuple{T}, Tuple{ImplicitSolventOBC{T}, Any, Any}} where T","page":"API","title":"Molly.born_radii_and_grad","text":"born_radii_and_grad(inter, coords, boundary)\n\nCalculate Born radii, gradients of Born radii and surface area overlap with respect to atomic distance.\n\nCustom GBSA methods should implement this function.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.box_center-Tuple{Union{CubicBoundary, RectangularBoundary}}","page":"API","title":"Molly.box_center","text":"box_center(boundary)\n\nCalculate the center of a bounding box.\n\nDimensions with infinite length return zero.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.charge-Tuple{Any}","page":"API","title":"Molly.charge","text":"charge(atom)\n\nThe partial charge of an Atom.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.charges-Tuple{Union{ReplicaSystem, System}}","page":"API","title":"Molly.charges","text":"charges(sys)\n\nThe partial charges of the atoms in a System or ReplicaSystem.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.check_position_constraints-Tuple{Any, SHAKE_RATTLE}","page":"API","title":"Molly.check_position_constraints","text":"check_position_constraints(sys, constraints)\n\nChecks if the position constraints are satisfied by the current coordinates of sys.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.check_velocity_constraints-Tuple{System, SHAKE_RATTLE}","page":"API","title":"Molly.check_velocity_constraints","text":"check_velocity_constraints(sys, constraints)\n\nChecks if the velocity constraints are satisfied by the current velocities of sys.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.density-Tuple{Any}","page":"API","title":"Molly.density","text":"density(sys)\n\nThe density of a System.\n\nReturns zero density for infinite boundaries.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.dipole_moment-Tuple{Any}","page":"API","title":"Molly.dipole_moment","text":"dipole_moment(sys)\n\nThe dipole moment μ of a system.\n\nRequires the charges on the atoms to be set.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.disable_constrained_interactions!-Tuple{Any, Any}","page":"API","title":"Molly.disable_constrained_interactions!","text":"disable_constrained_interactions!(neighbor_finder, constraint_clusters)\n\nDisables neighbor list interactions between atoms in a constraint.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.displacements-Tuple{Any, Any}","page":"API","title":"Molly.displacements","text":"displacements(coords, boundary)\n\nCalculate the pairwise vector displacements of a set of coordinates, accounting for the periodic boundary conditions.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.distances-Tuple{Any, Any}","page":"API","title":"Molly.distances","text":"distances(coords, boundary)\n\nCalculate the pairwise distances of a set of coordinates, accounting for the periodic boundary conditions.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.extract_parameters-Tuple{Any, Any}","page":"API","title":"Molly.extract_parameters","text":"extract_parameters(system, force_field)\n\nForm a Dict of all parameters in a System, allowing gradients to be tracked.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.find_neighbors-Tuple{System}","page":"API","title":"Molly.find_neighbors","text":"find_neighbors(system; n_threads=Threads.nthreads())\nfind_neighbors(system, neighbor_finder, current_neighbors=nothing, step_n=0,\n force_recompute=false; n_threads=Threads.nthreads())\n\nObtain a list of close atoms in a System.\n\nCustom neighbor finders should implement this function.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.float_type-Union{Tuple{Union{ReplicaSystem{D, G, T}, System{D, G, T}}}, Tuple{T}, Tuple{G}, Tuple{D}} where {D, G, T}","page":"API","title":"Molly.float_type","text":"float_type(sys)\nfloat_type(boundary)\n\nThe float type a System, ReplicaSystem or bounding box uses.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.force","page":"API","title":"Molly.force","text":"force(inter, vec_ij, atom_i, atom_j, force_units, special, coord_i, coord_j,\n boundary, velocity_i, velocity_j, step_n)\nforce(inter, coord_i, boundary, atom_i, force_units, velocity_i, step_n)\nforce(inter, coord_i, coord_j, boundary, atom_i, atom_j, force_units, velocity_i,\n velocity_j, step_n)\nforce(inter, coord_i, coord_j, coord_k, boundary, atom_i, atom_j, atom_k,\n force_units, velocity_i, velocity_j, velocity_k, step_n)\nforce(inter, coord_i, coord_j, coord_k, coord_l, boundary, atom_i, atom_j, atom_k,\n atom_l, force_units, velocity_i, velocity_j, velocity_k, velocity_l, step_n)\n\nCalculate the force between atoms due to a given interaction type.\n\nFor pairwise interactions returns a single force vector and for specific interactions returns a type such as SpecificForce2Atoms. Custom pairwise and specific interaction types should implement this function.\n\n\n\n\n\n","category":"function"},{"location":"api/#Molly.forces-Tuple{Any}","page":"API","title":"Molly.forces","text":"forces(system, neighbors=find_neighbors(sys), step_n=0; n_threads=Threads.nthreads())\n\nCalculate the forces on all atoms in a system using the pairwise, specific and general interactions.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.hydrodynamic_radius-Union{Tuple{T}, Tuple{D}, Tuple{AbstractArray{SVector{D, T}}, Any}} where {D, T}","page":"API","title":"Molly.hydrodynamic_radius","text":"hydrodynamic_radius(coords, boundary)\n\nCalculate the hydrodynamic radius of a set of coordinates.\n\nR_hyd is defined by\n\nfrac1R_hyd = frac12N^2sum_i neq j frac1r_ij\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.inject_gradients-Union{Tuple{G}, Tuple{D}, Tuple{System{D, G}, Any}} where {D, G}","page":"API","title":"Molly.inject_gradients","text":"inject_gradients(sys, params_dic)\n\nAdd parameters from a dictionary to a System.\n\nAllows gradients for individual parameters to be tracked. Returns atoms, pairwise interactions, specific interaction lists and general interactions.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.is_any_atom-Tuple{Any, Any}","page":"API","title":"Molly.is_any_atom","text":"is_any_atom(at, at_data)\n\nPlaceholder function that returns true, used to select any Atom.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.is_heavy_atom-Tuple{Any, Any}","page":"API","title":"Molly.is_heavy_atom","text":"is_heavy_atom(at, at_data)\n\nDetermines whether an Atom is a heavy atom, i.e. any element other than hydrogen.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.is_on_gpu-Union{Tuple{Union{ReplicaSystem{D, G}, System{D, G}}}, Tuple{G}, Tuple{D}} where {D, G}","page":"API","title":"Molly.is_on_gpu","text":"is_on_gpu(sys)\n\nWhether a System or ReplicaSystem is on the GPU.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.kinetic_energy-Union{Tuple{System{D, G, T}}, Tuple{T}, Tuple{G}, Tuple{D}} where {D, G, T}","page":"API","title":"Molly.kinetic_energy","text":"kinetic_energy(system)\n\nCalculate the kinetic energy of a system.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.log_property!","page":"API","title":"Molly.log_property!","text":"log_property!(logger, system, neighbors=nothing, step_n=0;\n n_threads=Threads.nthreads(), kwargs...)\n\nLog a property of a system throughout a simulation.\n\nCustom loggers should implement this function. Additional keyword arguments can be passed to the logger if required.\n\n\n\n\n\n","category":"function"},{"location":"api/#Molly.mass-Tuple{Any}","page":"API","title":"Molly.mass","text":"mass(atom)\n\nThe mass of an Atom.\n\nCustom atom types should implement this function unless they have a mass field defined, which the function accesses by default.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.masses-Tuple{System}","page":"API","title":"Molly.masses","text":"masses(sys)\n\nThe masses of the atoms in a System or ReplicaSystem.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.maxwell_boltzmann","page":"API","title":"Molly.maxwell_boltzmann","text":"maxwell_boltzmann(atom_mass::Unitful.Mass, temp::Unitful.Temperature,\n k::BoltzmannConstUnits=Unitful.k; rng=Random.GLOBAL_RNG)\nmaxwell_boltzmann(atom_mass::MolarMass, temp::Unitful.Temperature,\n k_molar::MolarBoltzmannConstUnits=(Unitful.k * Unitful.Na);\n rng=Random.GLOBAL_RNG)\nmaxwell_boltzmann(atom_mass::Real, temperature::Real,\n k::Real=ustrip(u\"u * nm^2 * ps^-2 * K^-1\", Unitful.k); rng=Random.GLOBAL_RNG)\n\nGenerate a random velocity along one dimension from the Maxwell-Boltzmann distribution, with optional custom Boltzmann constant.\n\n\n\n\n\n","category":"function"},{"location":"api/#Molly.molecule_centers-Union{Tuple{C}, Tuple{D}, Tuple{AbstractArray{SVector{D, C}}, Any, Any}} where {D, C}","page":"API","title":"Molly.molecule_centers","text":"molecule_centers(coords, boundary, topology)\n\nCalculate the coordinates of the center of each molecule in a system.\n\nAccounts for periodic boundary conditions by using the circular mean. If topology=nothing then the coordinates are returned.\n\nNot currently compatible with TriclinicBoundary if the topology is set.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.place_atoms-Tuple{Integer, Any}","page":"API","title":"Molly.place_atoms","text":"place_atoms(n_atoms, boundary; min_dist=nothing, max_attempts=100, rng=Random.GLOBAL_RNG)\n\nGenerate random coordinates.\n\nObtain n_atoms coordinates in bounding box boundary where no two points are closer than min_dist, accounting for periodic boundary conditions. The keyword argument max_attempts determines the number of failed tries after which to stop placing atoms. Can not be used if one or more dimensions has infinite boundaries.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.place_diatomics-Tuple{Integer, Any, Any}","page":"API","title":"Molly.place_diatomics","text":"place_diatomics(n_molecules, boundary, bond_length; min_dist=nothing,\n max_attempts=100, aligned=false, rng=Random.GLOBAL_RNG)\n\nGenerate random diatomic molecule coordinates.\n\nObtain coordinates for n_molecules diatomics in bounding box boundary where no two points are closer than min_dist and the bond length is bond_length, accounting for periodic boundary conditions. The keyword argument max_attempts determines the number of failed tries after which to stop placing atoms. The keyword argument aligned determines whether the bonds all point the same direction (true) or random directions (false). Can not be used if one or more dimensions has infinite boundaries.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.potential_energy-Tuple{Any}","page":"API","title":"Molly.potential_energy","text":"potential_energy(system, neighbors=find_neighbors(sys), step_n=0; n_threads=Threads.nthreads())\n\nCalculate the potential energy of a system using the pairwise, specific and general interactions.\n\npotential_energy(inter, vec_ij, atom_i, atom_j, energy_units, special, coord_i, coord_j,\n boundary, velocity_i, velocity_j, step_n)\npotential_energy(inter, coord_i, boundary, atom_i, energy_units, velocity_i, step_n)\npotential_energy(inter, coord_i, coord_j, boundary, atom_i, atom_j, energy_units,\n velocity_i, velocity_j, step_n)\npotential_energy(inter, coord_i, coord_j, coord_k, boundary, atom_i, atom_j, atom_k,\n energy_units, velocity_i, velocity_j, velocity_k, step_n)\npotential_energy(inter, coord_i, coord_j, coord_k, coord_l, boundary, atom_i, atom_j,\n atom_k, atom_l, energy_units, velocity_i, velocity_j, velocity_k,\n velocity_l, step_n)\n\nCalculate the potential energy due to a given interaction type.\n\nCustom interaction types should implement this function.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.pressure-Tuple{Any}","page":"API","title":"Molly.pressure","text":"pressure(sys, neighbors=find_neighbors(sys), step_n=0; n_threads=Threads.nthreads())\n\nCalculate the pressure of a system.\n\nThe pressure is defined as\n\nP = frac1V left( NkT - frac2D Xi right)\n\nwhere V is the system volume, N is the number of atoms, k is the Boltzmann constant, T is the system temperature, D is the number of dimensions and Ξ is the virial calculated using virial.\n\nThis should only be used on systems containing just pairwise interactions, or where the specific interactions, constraints and general interactions without virial defined do not contribute to the virial. Not compatible with infinite boundaries.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.radius_gyration-Tuple{Any, Any}","page":"API","title":"Molly.radius_gyration","text":"radius_gyration(coords, atoms)\n\nCalculate the radius of gyration of a set of coordinates.\n\nAssumes the coordinates do not cross the bounding box, i.e. all coordinates correspond to the same periodic image.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.random_coord-Tuple{CubicBoundary}","page":"API","title":"Molly.random_coord","text":"random_coord(boundary; rng=Random.GLOBAL_RNG)\n\nGenerate a random coordinate uniformly distributed within a bounding box.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.random_normal_translation!-Union{Tuple{System{D, G, T}}, Tuple{T}, Tuple{G}, Tuple{D}} where {D, G, T}","page":"API","title":"Molly.random_normal_translation!","text":"random_normal_translation!(sys::System; shift_size=oneunit(eltype(eltype(sys.coords))))\n\nPerforms a random translation of the coordinates of a randomly selected atom in a System.\n\nThe translation is generated using a uniformly chosen direction and length selected from the standard normal distribution i.e. with mean 0 and standard deviation 1, scaled by shift_size which should have appropriate length units.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.random_uniform_translation!-Union{Tuple{System{D, G, T}}, Tuple{T}, Tuple{G}, Tuple{D}} where {D, G, T}","page":"API","title":"Molly.random_uniform_translation!","text":"random_uniform_translation!(sys::System; shift_size=oneunit(eltype(eltype(sys.coords))))\n\nPerforms a random translation of the coordinates of a randomly selected atom in a System.\n\nThe translation is generated using a uniformly selected direction and uniformly selected length in range [0, 1) scaled by shift_size which should have appropriate length units.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.random_velocities!-Tuple{Any, Any}","page":"API","title":"Molly.random_velocities!","text":"random_velocities!(sys, temp)\nrandom_velocities!(vels, sys, temp)\n\nSet the velocities of a System, or a vector, to random velocities generated from the Maxwell-Boltzmann distribution.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.random_velocities-Tuple{AtomsBase.AbstractSystem{3}, Any}","page":"API","title":"Molly.random_velocities","text":"random_velocities(sys, temp)\n\nGenerate random velocities from the Maxwell-Boltzmann distribution for a System.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.random_velocity-Tuple{Union{Level{L, S, Quantity{T, 𝐌, U}} where {T, U, L, S}, Level{L, S, Quantity{T, 𝐌 𝐍^-1, U}} where {T, U, L, S}, Quantity{T, 𝐌} where T, Quantity{T, 𝐌 𝐍^-1} where T}, Union{Quantity{T, 𝚯, U}, Level{L, S, Quantity{T, 𝚯, U}} where {L, S}} where {T, U}}","page":"API","title":"Molly.random_velocity","text":"random_velocity(atom_mass::Union{Unitful.Mass, MolarMass}, temp::Unitful.Temperature;\n dims=3, rng=Random.GLOBAL_RNG)\nrandom_velocity(atom_mass::Union{Unitful.Mass, MolarMass}, temp::Unitful.Temperature,\n k::Union{BoltzmannConstUnits, MolarBoltzmannConstUnits};\n dims=3, rng=Random.GLOBAL_RNG)\nrandom_velocity(atom_mass::Real, temp::Real, k::Real=ustrip(u\"u * nm^2 * ps^-2 * K^-1\", Unitful.k);\n dims=3, rng=Random.GLOBAL_RNG)\n\nGenerate a random velocity from the Maxwell-Boltzmann distribution, with optional custom Boltzmann constant.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.rdf","page":"API","title":"Molly.rdf","text":"rdf(coords, boundary; npoints=200)\n\nCalculate the radial distribution function of a set of coordinates.\n\nThis function is only available when KernelDensity is imported. This describes how density varies as a function of distance from each atom. Returns a list of distance bin centers and a list of the corresponding densities.\n\n\n\n\n\n","category":"function"},{"location":"api/#Molly.remd_exchange!-Union{Tuple{T}, Tuple{G}, Tuple{D}, Tuple{ReplicaSystem{D, G, T}, TemperatureREMD, Integer, Integer}} where {D, G, T}","page":"API","title":"Molly.remd_exchange!","text":"remd_exchange!(sys, sim, n, m; rng=Random.GLOBAL_RNG, n_threads=Threads.nthreads())\n\nAttempt an exchange of replicas n and m in a ReplicaSystem during a REMD simulation.\n\nSuccessful exchanges should exchange coordinates and velocities as appropriate. Returns acceptance quantity Δ and a Bool indicating whether the exchange was successful.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.remove_CM_motion!-Tuple{Any}","page":"API","title":"Molly.remove_CM_motion!","text":"remove_CM_motion!(system)\n\nRemove the center of mass motion from a System.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.rmsd-Union{Tuple{T}, Tuple{D}, Tuple{AbstractArray{SVector{D, T}}, AbstractArray{SVector{D, T}}}} where {D, T}","page":"API","title":"Molly.rmsd","text":"rmsd(coords_1, coords_2)\n\nCalculate the root-mean-square deviation (RMSD) of two sets of 3D coordinates after superimposition by the Kabsch algorithm.\n\nAssumes the coordinates do not cross the bounding box, i.e. all coordinates in each set correspond to the same periodic image.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.scale_boundary-Tuple{CubicBoundary, Any}","page":"API","title":"Molly.scale_boundary","text":"scale_boundary(boundary, scale_factor)\n\nScale the sides of a bounding box by a scaling factor.\n\nThe scaling factor can be a single number or a SVector of the appropriate number of dimensions corresponding to the scaling factor for each axis. For a 3D bounding box the volume scales as the cube of the scaling factor.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.scale_coords!-Tuple{Any, Any}","page":"API","title":"Molly.scale_coords!","text":"scale_coords!(sys, scale_factor; ignore_molecules=false)\n\nScale the coordinates and bounding box of a system by a scaling factor.\n\nThe scaling factor can be a single number or a SVector of the appropriate number of dimensions corresponding to the scaling factor for each axis. Velocities are not scaled. If the topology of the system is set then atoms in the same molecule will be moved by the same amount according to the center of coordinates of the molecule. This can be disabled with ignore_molecules=true.\n\nNot currently compatible with TriclinicBoundary if the topology is set.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.simulate!-Tuple{Any, SteepestDescentMinimizer}","page":"API","title":"Molly.simulate!","text":"simulate!(system, simulator, n_steps; n_threads=Threads.nthreads(), run_loggers=true)\nsimulate!(system, simulator; n_threads=Threads.nthreads(), run_loggers=true)\n\nRun a simulation on a system according to the rules of the given simulator.\n\nrun_loggers can be true, false or :skipzero, in which case the loggers are not run before the first step. run_loggers is true by default except for SteepestDescentMinimizer, where it is false. Custom simulators should implement this function.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.simulate_remd!-Tuple{ReplicaSystem, Any, Integer}","page":"API","title":"Molly.simulate_remd!","text":"simulate_remd!(sys, remd_sim, n_steps; rng=Random.GLOBAL_RNG,\n n_threads=Threads.nthreads(), run_loggers=true)\n\nRun a REMD simulation on a ReplicaSystem using a REMD simulator.\n\nNot currently compatible with interactions that depend on step number.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.temperature-Tuple{Any}","page":"API","title":"Molly.temperature","text":"temperature(system)\n\nCalculate the temperature of a system from the kinetic energy of the atoms.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.torsion_angle-NTuple{5, Any}","page":"API","title":"Molly.torsion_angle","text":"torsion_angle(coord_i, coord_j, coord_k, coord_l, boundary)\ntorsion_angle(vec_ij, vec_jk, vec_kl)\n\nCalculate the torsion angle in radians defined by four coordinates or three vectors.\n\nThe angle between the planes defined by atoms (i, j, k) and (j, k, l) is returned in the range -π to π.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.total_energy-Tuple{Any}","page":"API","title":"Molly.total_energy","text":"total_energy(system, neighbors=find_neighbors(sys); n_threads=Threads.nthreads())\n\nCalculate the total energy of a system as the sum of the kinetic_energy and the potential_energy.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.use_neighbors-Tuple{Any}","page":"API","title":"Molly.use_neighbors","text":"use_neighbors(inter)\n\nWhether a pairwise interaction uses the neighbor list, default false.\n\nCustom pairwise interactions can define a method for this function. For built-in interactions such as LennardJones this function accesses the use_neighbors field of the struct.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.ustrip_vec-Tuple","page":"API","title":"Molly.ustrip_vec","text":"ustrip_vec(x)\nustrip_vec(u, x)\n\nBroadcasted form of ustrip from Unitful.jl, allowing e.g. ustrip_vec.(coords).\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.vector-Tuple{Any, Any, CubicBoundary}","page":"API","title":"Molly.vector","text":"vector(c1, c2, boundary)\n\nDisplacement between two coordinate values from c1 to c2, accounting for periodic boundary conditions.\n\nThe minimum image convention is used, so the displacement is to the closest version of the coordinates accounting for the periodic boundaries. For the TriclinicBoundary an approximation is used to find the closest version by default.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.vector_1D-Tuple{Any, Any, Any}","page":"API","title":"Molly.vector_1D","text":"vector_1D(c1, c2, side_length)\n\nDisplacement between two 1D coordinate values from c1 to c2, accounting for periodic boundary conditions in a CubicBoundary or RectangularBoundary.\n\nThe minimum image convention is used, so the displacement is to the closest version of the coordinate accounting for the periodic boundaries.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.virial-Tuple{Any}","page":"API","title":"Molly.virial","text":"virial(sys, neighbors=find_neighbors(sys), step_n=0; n_threads=Threads.nthreads())\nvirial(inter, sys, neighbors, step_n; n_threads=Threads.nthreads())\n\nCalculate the virial of a system or the virial resulting from a general interaction.\n\nThe virial is defined as\n\nXi = -frac12 sum_iji r_ij cdot F_ij\n\nCustom general interaction types can implement this function.\n\nThis should only be used on systems containing just pairwise interactions, or where the specific interactions, constraints and general interactions without virial defined do not contribute to the virial.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.visualize","page":"API","title":"Molly.visualize","text":"visualize(coord_logger, boundary, out_filepath; )\n\nVisualize a simulation as an animation.\n\nThis function is only available when GLMakie is imported. It can take a while to run, depending on the length of the simulation and the number of atoms.\n\nArguments\n\nconnections=Tuple{Int, Int}[]: pairs of atoms indices to link with bonds.\nconnection_frames: the frames in which bonds are shown. Should be a list of the same length as the number of frames, where each item is a list of Bools of the same length as connections. Defaults to always true.\ntrails::Integer=0: the number of preceding frames to show as transparent trails.\nframerate::Integer=30: the frame rate of the animation.\ncolor=:purple: the color of the atoms. Can be a single color or a list of colors of the same length as the number of atoms.\nconnection_color=:orange: the color of the bonds. Can be a single color or a list of colors of the same length as connections.\nmarkersize=0.05: the size of the atom markers, in the units of the data.\nlinewidth=2.0: the width of the bond lines.\ntransparency=true: whether transparency is active on the plot.\nshow_boundary::Bool=true: whether to show the bounding box as lines.\nboundary_linewidth=2.0: the width of the boundary lines.\nboundary_color=:black: the color of the boundary lines.\nkwargs...: other keyword arguments are passed to the point plotting function.\n\n\n\n\n\n","category":"function"},{"location":"api/#Molly.volume-Tuple{Any}","page":"API","title":"Molly.volume","text":"volume(sys)\nvolume(boundary)\n\nCalculate the volume (3D) or area (2D) of a System or bounding box.\n\nReturns infinite volume for infinite boundaries.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.wrap_coord_1D-Tuple{Any, Any}","page":"API","title":"Molly.wrap_coord_1D","text":"wrap_coord_1D(c, side_length)\n\nEnsure a 1D coordinate is within the bounding box and return the coordinate.\n\n\n\n\n\n","category":"method"},{"location":"api/#Molly.wrap_coords-Tuple{Any, Union{CubicBoundary, RectangularBoundary}}","page":"API","title":"Molly.wrap_coords","text":"wrap_coords(c, boundary)\n\nEnsure a coordinate is within the bounding box and return the coordinate.\n\n\n\n\n\n","category":"method"},{"location":"publications/#Publications","page":"Publications","title":"Publications","text":"","category":"section"},{"location":"publications/","page":"Publications","title":"Publications","text":"If you use Molly, please cite the following paper (bib entry here):","category":"page"},{"location":"publications/","page":"Publications","title":"Publications","text":"Greener JG. Differentiable simulation to develop molecular dynamics force fields for disordered proteins, Chemical Science 15, 4897-4909 (2024)","category":"page"},{"location":"publications/","page":"Publications","title":"Publications","text":"A paper involving more contributors with further details on the software will be written at some point.","category":"page"},{"location":"publications/","page":"Publications","title":"Publications","text":"Other papers that use, contribute to or are compatible with Molly are listed below:","category":"page"},{"location":"publications/","page":"Publications","title":"Publications","text":"Martínez L. CellListMap.jl: Efficient and customizable cell list implementation for calculation of pairwise particle properties within a cutoff, Comput Phys Commun 279, 108452 (2022)\nBlassel N and Stoltz G. Fixing the flux: A dual approach to computing transport coefficients, arXiv (2023)\nWitt WC et al. ACEpotentials.jl: A Julia implementation of the atomic cluster expansion, J Chem Phys 159, 164101 (2023)\nMonmarché P, Spacek R and Stoltz G. Transient subtraction: A control variate method for computing transport coefficients, arXiv (2024)","category":"page"},{"location":"related/#Related-software","page":"Related software","title":"Related software","text":"","category":"section"},{"location":"related/","page":"Related software","title":"Related software","text":"There are many mature packages for molecular simulation. Of particular note here are OpenMM and GROMACS, both of which influenced the implementation of Molly. Molly can be thought of as similar to OpenMM in that it exposes simulation internals in a high-level language, though it is written in one language all the way down rather than using multiple device-specific kernels. It also aims to be differentiable and work just as well with non-molecular physical simulations, though how much this impacts the ability to reach high simulation speeds remains to be seen.","category":"page"},{"location":"related/","page":"Related software","title":"Related software","text":"For differentiable simulations there are a number of related packages:","category":"page"},{"location":"related/","page":"Related software","title":"Related software","text":"Jax, M.D.\nTorchMD\nmdgrad\nDMFF\nTime Machine\nDiffTaichi","category":"page"},{"location":"related/","page":"Related software","title":"Related software","text":"In Julia there are a number of packages related to atomic simulation, some of which are involved with the JuliaMolSim organisation:","category":"page"},{"location":"related/","page":"Related software","title":"Related software","text":"AtomsBase.jl\nJuLIP.jl\nCellListMap.jl\nDFTK.jl\nACE.jl\nAtomicGraphNets.jl\nInteratomicPotentials.jl, Atomistic.jl and PotentialLearning.jl from the CESMIX project at MIT\nNBodySimulator.jl, DiffEqPhysics.jl and the SciML ecosystem more broadly","category":"page"},{"location":"development/#Development-documentation","page":"Development","title":"Development documentation","text":"","category":"section"},{"location":"development/#Running-tests","page":"Development","title":"Running tests","text":"","category":"section"},{"location":"development/","page":"Development","title":"Development","text":"The tests will automatically include multithreading and/or GPU tests if multiple threads and/or a CUDA-enabled GPU are available. test/runtests.jl does not include all the tests, see the test directory for more, though these extra tests do not need to be run for every change. Various environmental variables can be set to modify the tests:","category":"page"},{"location":"development/","page":"Development","title":"Development","text":"VISTESTS determines whether to run the GLMakie.jl plotting tests which will error on remote systems where a display is not available, default VISTESTS=1.\nGPUTESTS determines whether to run the GPU tests, default GPUTESTS=1.\nDEVICE determines which GPU to run the GPU tests on, default DEVICE=0.\nGROUP can be used to run a subset of the tests, options All/Protein/Gradients/NotGradients, default GROUP=All.","category":"page"},{"location":"development/","page":"Development","title":"Development","text":"The CI run does not carry out all tests - for example the GPU tests are not run - and this is reflected in the code coverage.","category":"page"},{"location":"development/#Benchmarks","page":"Development","title":"Benchmarks","text":"","category":"section"},{"location":"development/","page":"Development","title":"Development","text":"The benchmark directory contains some benchmarks for the package.","category":"page"},{"location":"examples/#Molly-examples","page":"Examples","title":"Molly examples","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"The best examples for learning how the package works are in the Molly documentation section. Here we give further examples showing what you can do with the package. Each is a self-contained block of code. Made something cool yourself? Make a PR to add it to this page.","category":"page"},{"location":"examples/#Simulated-annealing","page":"Examples","title":"Simulated annealing","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"You can change the thermostat temperature of a simulation by changing the simulator. Here we reduce the temperature of a simulation in stages from 300 K to 0 K.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing GLMakie\n\ndata_dir = joinpath(dirname(pathof(Molly)), \"..\", \"data\")\nff = MolecularForceField(\n joinpath(data_dir, \"force_fields\", \"ff99SBildn.xml\"),\n joinpath(data_dir, \"force_fields\", \"tip3p_standard.xml\"),\n joinpath(data_dir, \"force_fields\", \"his.xml\"),\n)\n\nsys = System(\n joinpath(data_dir, \"6mrr_equil.pdb\"),\n ff;\n loggers=(temp=TemperatureLogger(100),),\n)\n\nminimizer = SteepestDescentMinimizer()\nsimulate!(sys, minimizer)\n\ntemps = [300.0, 200.0, 100.0, 0.0]u\"K\"\nrandom_velocities!(sys, temps[1])\n\nfor temp in temps\n simulator = Langevin(\n dt=0.001u\"ps\",\n temperature=temp,\n friction=1.0u\"ps^-1\",\n )\n simulate!(sys, simulator, 5_000; run_loggers=:skipzero)\nend\n\nf = Figure(resolution=(600, 400))\nax = Axis(\n f[1, 1],\n xlabel=\"Step\",\n ylabel=\"Temperature\",\n title=\"Temperature change during simulated annealing\",\n)\nfor (i, temp) in enumerate(temps)\n lines!(\n ax,\n [5000 * i - 5000, 5000 * i],\n [ustrip(temp), ustrip(temp)],\n linestyle=\"--\",\n color=:orange,\n )\nend\nscatter!(\n ax,\n 100 .* (1:length(values(sys.loggers.temp))),\n ustrip.(values(sys.loggers.temp)),\n markersize=5,\n)\nsave(\"annealing.png\", f)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Annealing)","category":"page"},{"location":"examples/#Solar-system","page":"Examples","title":"Solar system","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"Orbits of the four closest planets to the sun can be simulated.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing GLMakie\n\n# Using get_body_barycentric_posvel from Astropy\ncoords = [\n SVector(-1336052.8665050615, 294465.0896030796 , 158690.88781384667)u\"km\",\n SVector(-58249418.70233503 , -26940630.286818042, -8491250.752464907 )u\"km\",\n SVector( 58624128.321813114, -81162437.2641475 , -40287143.05760552 )u\"km\",\n SVector(-99397467.7302648 , -105119583.06486066, -45537506.29775053 )u\"km\",\n SVector( 131714235.34070954, -144249196.60814604, -69730238.5084304 )u\"km\",\n]\n\nvelocities = [\n SVector(-303.86327859262457, -1229.6540090943934, -513.791218405548 )u\"km * d^-1\",\n SVector( 1012486.9596885007, -3134222.279236384 , -1779128.5093088674)u\"km * d^-1\",\n SVector( 2504563.6403826815, 1567163.5923297722, 546718.234192132 )u\"km * d^-1\",\n SVector( 1915792.9709661514, -1542400.0057833872, -668579.962254351 )u\"km * d^-1\",\n SVector( 1690083.43357355 , 1393597.7855017239, 593655.0037930267 )u\"km * d^-1\",\n]\n\nbody_masses = [\n 1.989e30u\"kg\",\n 0.330e24u\"kg\",\n 4.87e24u\"kg\" ,\n 5.97e24u\"kg\" ,\n 0.642e24u\"kg\",\n]\n\nboundary = CubicBoundary(1e9u\"km\")\n\n# Convert the gravitational constant to the appropriate units\ninter = Gravity(G=convert(typeof(1.0u\"km^3 * kg^-1 * d^-2\"), Unitful.G))\n\nsys = System(\n atoms=[Atom(mass=m) for m in body_masses],\n coords=coords .+ (SVector(5e8, 5e8, 5e8)u\"km\",),\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=(inter,),\n loggers=(coords=CoordinatesLogger(typeof(1.0u\"km\"), 10),),\n force_units=u\"kg * km * d^-2\",\n energy_units=u\"kg * km^2 * d^-2\",\n)\n\nsimulator = Verlet(\n dt=0.1u\"d\",\n remove_CM_motion=false,\n)\n\nsimulate!(sys, simulator, 3650) # 1 year\n\nvisualize(\n sys.loggers.coords,\n boundary,\n \"sim_planets.mp4\";\n trails=5,\n color=[:yellow, :grey, :orange, :blue, :red],\n markersize=[0.25, 0.08, 0.08, 0.08, 0.08],\n transparency=false,\n)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Planet simulation)","category":"page"},{"location":"examples/#Agent-based-modelling","page":"Examples","title":"Agent-based modelling","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"Agent-based modelling (ABM) is conceptually similar to molecular dynamics. Julia has Agents.jl for ABM, but Molly can also be used to simulate arbitrary agent-based systems in continuous space. Here we simulate a toy SIR model for disease spread. This example shows how atom properties can be mutable, i.e. change during the simulation, and includes custom forces and loggers (see below for more info).","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing GLMakie\n\n@enum Status susceptible infected recovered\n\n# Custom atom type\nmutable struct Person\n i::Int\n status::Status\n mass::Float64\n σ::Float64\n ϵ::Float64\nend\n\n# Custom pairwise interaction\nstruct SIRInteraction\n dist_infection::Float64\n prob_infection::Float64\n prob_recovery::Float64\nend\n\n# Custom force function\nfunction Molly.force(inter::SIRInteraction,\n vec_ij,\n atom_i,\n atom_j,\n args...)\n if (atom_i.status == infected && atom_j.status == susceptible) ||\n (atom_i.status == susceptible && atom_j.status == infected)\n # Infect close people randomly\n r2 = sum(abs2, vec_ij)\n if r2 < inter.dist_infection^2 && rand() < inter.prob_infection\n atom_i.status = infected\n atom_j.status = infected\n end\n end\n # Workaround to obtain a self-interaction\n if atom_i.i == (atom_j.i - 1)\n # Recover randomly\n if atom_i.status == infected && rand() < inter.prob_recovery\n atom_i.status = recovered\n end\n end\n return zero(vec_ij)\nend\n\n# Custom logger\nfunction fracs_SIR(s::System, args...; kwargs...)\n counts_sir = [\n count(p -> p.status == susceptible, s.atoms),\n count(p -> p.status == infected , s.atoms),\n count(p -> p.status == recovered , s.atoms)\n ]\n return counts_sir ./ length(s)\nend\n\nSIRLogger(n_steps) = GeneralObservableLogger(fracs_SIR, Vector{Float64}, n_steps)\n\ntemp = 1.0\nboundary = RectangularBoundary(10.0)\nn_steps = 1_000\nn_people = 500\nn_starting = 2\natoms = [Person(i, i <= n_starting ? infected : susceptible, 1.0, 0.1, 0.02) for i in 1:n_people]\ncoords = place_atoms(n_people, boundary; min_dist=0.1)\nvelocities = [random_velocity(1.0, temp; dims=2) for i in 1:n_people]\n\nlj = LennardJones(cutoff=DistanceCutoff(1.6), use_neighbors=true)\nsir = SIRInteraction(0.5, 0.06, 0.01)\npairwise_inters = (LennardJones=lj, SIR=sir)\nneighbor_finder = DistanceNeighborFinder(\n eligible=trues(n_people, n_people),\n n_steps=10,\n dist_cutoff=2.0,\n)\nsimulator = VelocityVerlet(\n dt=0.02,\n coupling=AndersenThermostat(temp, 5.0),\n)\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=pairwise_inters,\n neighbor_finder=neighbor_finder,\n loggers=(\n coords=CoordinatesLogger(Float64, 10; dims=2),\n SIR=SIRLogger(10),\n ),\n force_units=NoUnits,\n energy_units=NoUnits,\n)\n\nsimulate!(sys, simulator, n_steps)\n\nvisualize(sys.loggers.coords, boundary, \"sim_agent.mp4\"; markersize=0.1)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Agent simulation)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"We can use the logger to plot the fraction of people susceptible, infected and recovered over the course of the simulation:","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using GLMakie\n\nf = Figure()\nax = Axis(f[1, 1], xlabel=\"Snapshot\", ylabel=\"Fraction\")\n\nlines!([l[1] for l in values(sys.loggers.SIR)], label=\"Susceptible\")\nlines!([l[2] for l in values(sys.loggers.SIR)], label=\"Infected\")\nlines!([l[3] for l in values(sys.loggers.SIR)], label=\"Recovered\")\naxislegend()","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Fraction SIR)","category":"page"},{"location":"examples/#Polymer-melt","page":"Examples","title":"Polymer melt","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"Here we use FENEBond, CosineAngle and LennardJones to simulate interacting polymers. We also analyse the end-to-end polymer distances and chain angles across the trajectory.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing GLMakie\nusing Colors\nusing LinearAlgebra\n\n# Simulate 10 polymers each consisting of 6 monomers\nn_polymers = 10\nn_monomers = 6\nn_atoms = n_monomers * n_polymers\nn_bonds_mon = n_monomers - 1\nn_bonds_tot = n_bonds_mon * n_polymers\nn_angles_mon = n_monomers - 2\nn_angles_tot = n_angles_mon * n_polymers\n\nstarting_length = 1.1u\"nm\"\nboundary = CubicBoundary(20.0u\"nm\")\n\n# Random placement of polymer centers at the start\nstart_coords = place_atoms(n_polymers, boundary; min_dist=6.0u\"nm\")\n\n# Polymers start almost completely extended\ncoords = []\nfor pol_i in 1:n_polymers\n for mon_i in 1:n_monomers\n push!(coords, start_coords[pol_i] .+ SVector(\n starting_length * (mon_i - 1 - n_monomers / 2),\n rand() * 0.1u\"nm\",\n rand() * 0.1u\"nm\",\n ))\n end\nend\ncoords = [coords...] # Ensure the array is concretely typed\n\n# Create FENEBonds between adjacent monomers\nbond_is, bond_js = Int[], Int[]\nfor pol_i in 1:n_polymers\n for bi in 1:n_bonds_mon\n push!(bond_is, (pol_i - 1) * n_monomers + bi )\n push!(bond_js, (pol_i - 1) * n_monomers + bi + 1)\n end\nend\n\nfene_k = 250.0u\"kJ * mol^-1 * nm^-2\"\nfene_r0 = 1.6u\"nm\"\nbonds = InteractionList2Atoms(\n bond_is,\n bond_js,\n [FENEBond(k=fene_k, r0=fene_r0, σ=1.0u\"nm\", ϵ=2.5u\"kJ * mol^-1\") for _ in 1:n_bonds_tot],\n)\n\n# Create CosineAngles between adjacent monomers\nangle_is, angle_js, angle_ks = Int[], Int[], Int[]\nfor pol_i in 1:n_polymers\n for bi in 1:n_angles_mon\n push!(angle_is, (pol_i - 1) * n_monomers + bi )\n push!(angle_js, (pol_i - 1) * n_monomers + bi + 1)\n push!(angle_ks, (pol_i - 1) * n_monomers + bi + 2)\n end\nend\n\nangles = InteractionList3Atoms(\n angle_is,\n angle_js,\n angle_ks,\n [CosineAngle(k=2.0u\"kJ * mol^-1\", θ0=0.0) for _ in 1:n_angles_tot],\n)\n\natoms = [Atom(mass=10.0u\"g/mol\", σ=1.0u\"nm\", ϵ=0.5u\"kJ * mol^-1\") for _ in 1:n_atoms]\n\n# Since we are using a generic pairwise Lennard-Jones potential too we need to\n# exclude adjacent monomers from the neighbor list\neligible = trues(n_atoms, n_atoms)\nfor pol_i in 1:n_polymers\n for mon_i in 1:n_bonds_mon\n i = (pol_i - 1) * n_monomers + mon_i\n j = (pol_i - 1) * n_monomers + mon_i + 1\n eligible[i, j] = false\n eligible[j, i] = false\n end\nend\n\nlj = LennardJones(\n cutoff=DistanceCutoff(5.0u\"nm\"),\n use_neighbors=true,\n)\nneighbor_finder = DistanceNeighborFinder(\n eligible=eligible,\n n_steps=10,\n dist_cutoff=5.5u\"nm\",\n)\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n pairwise_inters=(lj,),\n specific_inter_lists=(bonds, angles),\n neighbor_finder=neighbor_finder,\n loggers=(coords=CoordinatesLogger(200),),\n)\n\nsim = Langevin(dt=0.002u\"ps\", temperature=300.0u\"K\", friction=1.0u\"ps^-1\")\n\nsimulate!(sys, sim, 100_000)\n\ncolors = distinguishable_colors(n_polymers, [RGB(1, 1, 1), RGB(0, 0, 0)]; dropseed=true)\n\nvisualize(\n sys.loggers.coords,\n boundary,\n \"sim_polymer.gif\";\n connections=zip(bond_is, bond_js),\n color=repeat(colors; inner=n_monomers),\n connection_color=repeat(colors; inner=n_bonds_mon),\n)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Polymer simulation)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"logged_coords = values(sys.loggers.coords)\nn_frames = length(logged_coords)\n\n# Calculate end-to-end polymer distances for second half of trajectory\nend_to_end_dists = Float64[]\nfor traj_coords in logged_coords[(n_frames ÷ 2):end]\n for pol_i in 1:n_polymers\n start_i = (pol_i - 1) * n_monomers + 1\n end_i = pol_i * n_monomers\n dist = norm(vector(traj_coords[start_i], traj_coords[end_i], boundary))\n push!(end_to_end_dists, ustrip(dist))\n end\nend\n\nf = Figure(resolution=(600, 400))\nax = Axis(\n f[1, 1],\n xlabel=\"End-to-end distance / nm\",\n ylabel=\"Density\",\n title=\"End-to-end polymer distance over the trajectory\",\n)\nhist!(ax, end_to_end_dists, normalization=:pdf)\nxlims!(ax, low=0)\nylims!(ax, low=0)\nsave(\"polymer_dist.png\", f)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Polymer distances)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"# Calculate angles to adjacent monomers for second half of trajectory\nchain_angles = Float64[]\nfor traj_coords in logged_coords[(n_frames ÷ 2):end]\n for pol_i in 1:n_polymers\n for mon_i in 2:(n_monomers - 1)\n ang = bond_angle(\n traj_coords[(pol_i - 1) * n_monomers + mon_i - 1],\n traj_coords[(pol_i - 1) * n_monomers + mon_i ],\n traj_coords[(pol_i - 1) * n_monomers + mon_i + 1],\n boundary,\n )\n push!(chain_angles, rad2deg(ang))\n end\n end\nend\n\nf = Figure(resolution=(600, 400))\nax = Axis(\n f[1, 1],\n xlabel=\"Angle with adjacent monomers / degrees\",\n ylabel=\"Density\",\n title=\"Chain angles over the trajectory\",\n)\nhist!(ax, chain_angles, normalization=:pdf)\nxlims!(ax, 0, 180)\nylims!(ax, low=0)\nsave(\"polymer_angle.png\", f)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Polymer angles)","category":"page"},{"location":"examples/#Machine-learning-potentials","page":"Examples","title":"Machine learning potentials","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"There is an example of using ACE potentials in Molly.","category":"page"},{"location":"examples/#Python-ASE-calculator","page":"Examples","title":"Python ASE calculator","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"ASECalculator can be used along with PythonCall.jl to use a Python ASE calculator with Molly. Here we simulate a dipeptide molecule in a vacuum with MACE-OFF23:","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing PythonCall # Python packages ase and mace need to be installed beforehand\nusing Downloads\n\nDownloads.download(\n \"https://raw.githubusercontent.com/noeblassel/SINEQSummerSchool2023/main/notebooks/dipeptide_nowater.pdb\",\n \"dipeptide_nowater.pdb\",\n)\n\ndata_dir = joinpath(dirname(pathof(Molly)), \"..\", \"data\")\nff = MolecularForceField(joinpath(data_dir, \"force_fields\", \"ff99SBildn.xml\"))\nsys = System(\"dipeptide_nowater.pdb\", ff; rename_terminal_res=false)\n\nmc = pyimport(\"mace.calculators\")\nase_calc = mc.mace_off(model=\"medium\", device=\"cuda\")\n\ncalc = ASECalculator(\n ase_calc=ase_calc,\n atoms=sys.atoms,\n coords=sys.coords,\n boundary=sys.boundary,\n atoms_data=sys.atoms_data,\n)\n\nsys = System(\n sys;\n general_inters=(calc,),\n loggers=(StructureWriter(20, \"mace_dipeptide.pdb\"),) # Every 10 fs\n)\npotential_energy(sys)\n\nminimizer = SteepestDescentMinimizer(log_stream=stdout)\nsimulate!(sys, minimizer)\n\ntemp = 298.0u\"K\"\nrandom_velocities!(sys, temp)\nsimulator = Langevin(\n dt=0.0005u\"ps\", # 0.5 fs\n temperature=temp,\n friction=1.0u\"ps^-1\",\n)\n\nsimulate!(deepcopy(sys), simulator, 5; run_loggers=false)\n@time simulate!(sys, simulator, 2000)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Another example using psi4 to get the potential energy of a water molecule:","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing PythonCall # Python packages ase and psi4 need to be installed beforehand\n\nbuild = pyimport(\"ase.build\")\npsi4 = pyimport(\"ase.calculators.psi4\")\n\npy_atoms = build.molecule(\"H2O\")\nase_calc = psi4.Psi4(\n atoms=py_atoms,\n method=\"b3lyp\",\n basis=\"6-311g_d_p_\",\n)\n\natoms = [Atom(mass=16.0u\"u\"), Atom(mass=1.0u\"u\"), Atom(mass=1.0u\"u\")]\ncoords = SVector{3, Float64}.(eachrow(pyconvert(Matrix, py_atoms.get_positions()))) * u\"Å\"\nboundary = CubicBoundary(100.0u\"Å\")\n\ncalc = ASECalculator(\n ase_calc=ase_calc,\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n elements=[\"O\", \"H\", \"H\"],\n)\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n general_inters=(calc,),\n energy_units=u\"eV\",\n force_units=u\"eV/Å\",\n)\n\npotential_energy(sys) # -2080.2391023908813 eV","category":"page"},{"location":"examples/#Density-functional-theory","page":"Examples","title":"Density functional theory","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"DFTK.jl can be used to calculate forces using density functional theory (DFT), allowing the simulation of quantum systems in Molly. This example uses the DFTK.jl tutorial to simulate two silicon atoms with atomic units. A general interaction is used since the whole force calculation is offloaded to DFTK.jl.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing DFTK\nimport AtomsCalculators\n\nstruct DFTKInteraction{L, A}\n lattice::L\n atoms::A\nend\n\n# Define lattice and atomic positions\na = 5.431u\"Å\" # Silicon lattice constant\nlattice = a / 2 * [[0 1 1.]; # Silicon lattice vectors\n [1 0 1.]; # specified column by column\n [1 1 0.]];\n\n# Load HGH pseudopotential for Silicon\nSi = ElementPsp(:Si, psp=load_psp(\"hgh/lda/Si-q4\"))\n\n# Specify type of atoms\natoms_dftk = [Si, Si]\n\ndftk_interaction = DFTKInteraction(lattice, atoms_dftk)\n\nfunction AtomsCalculators.forces(sys, inter::DFTKInteraction; kwargs...)\n # Select model and basis\n model = model_LDA(inter.lattice, inter.atoms, sys.coords)\n kgrid = [4, 4, 4] # k-point grid (Regular Monkhorst-Pack grid)\n Ecut = 7 # kinetic energy cutoff\n basis = PlaneWaveBasis(model; Ecut=Ecut, kgrid=kgrid)\n\n # Run the SCF procedure to obtain the ground state\n scfres = self_consistent_field(basis; tol=1e-5)\n\n return compute_forces_cart(scfres)\nend\n\natoms = fill(Atom(mass=28.0), 2)\ncoords = [SVector(1/8, 1/8, 1/8), SVector(-1/8, -1/8, -1/8)]\nvelocities = [randn(SVector{3, Float64}) * 0.1 for _ in 1:2]\nboundary = CubicBoundary(Inf)\nloggers = (coords=CoordinatesLogger(Float64, 1),)\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n general_inters=(dftk_interaction,),\n loggers=loggers,\n force_units=NoUnits,\n energy_units=NoUnits,\n)\n\nsimulator = Verlet(dt=0.0005, remove_CM_motion=false)\n\nsimulate!(sys, simulator, 100)\n\nvalues(sys.loggers.coords)[end]\n# 2-element Vector{SVector{3, Float64}}:\n# [0.12060853912863925, 0.12292128337998731, 0.13100409788691614]\n# [-0.13352575661477334, -0.11473039463130282, -0.13189544838731393]","category":"page"},{"location":"examples/#Making-and-breaking-bonds","page":"Examples","title":"Making and breaking bonds","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"There is an example of mutable atom properties in the main documentation, but what if you want to make and break bonds during the simulation? In this case you can use a pairwise interaction to make, break and apply the bonds. The partners of the atom can be stored in the atom type. We make a logger to record when the bonds are present, allowing us to visualize them with the connection_frames keyword argument to visualize (this can take a while to plot).","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing GLMakie\nusing LinearAlgebra\n\nstruct BondableAtom\n i::Int\n mass::Float64\n σ::Float64\n ϵ::Float64\n partners::Set{Int}\nend\n\nstruct BondableInteraction\n prob_formation::Float64\n prob_break::Float64\n dist_formation::Float64\n k::Float64\n r0::Float64\nend\n\nMolly.use_neighbors(::BondableInteraction) = true\n\nfunction Molly.force(inter::BondableInteraction,\n dr,\n atom_i,\n atom_j,\n args...)\n # Break bonds randomly\n if atom_j.i in atom_i.partners && rand() < inter.prob_break\n delete!(atom_i.partners, atom_j.i)\n delete!(atom_j.partners, atom_i.i)\n end\n # Make bonds between close atoms randomly\n r2 = sum(abs2, dr)\n if r2 < inter.r0 * inter.dist_formation && rand() < inter.prob_formation\n push!(atom_i.partners, atom_j.i)\n push!(atom_j.partners, atom_i.i)\n end\n # Apply the force of a harmonic bond\n if atom_j.i in atom_i.partners\n c = inter.k * (norm(dr) - inter.r0)\n fdr = -c * normalize(dr)\n return fdr\n else\n return zero(dr)\n end\nend\n\nfunction bonds(sys::System, args...; kwargs...)\n bonds = BitVector()\n for i in 1:length(sys)\n for j in 1:(i - 1)\n push!(bonds, j in sys.atoms[i].partners)\n end\n end\n return bonds\nend\n\nBondLogger(n_steps) = GeneralObservableLogger(bonds, BitVector, n_steps)\n\nn_atoms = 200\nboundary = RectangularBoundary(10.0)\nn_steps = 2_000\ntemp = 1.0\n\natoms = [BondableAtom(i, 1.0, 0.1, 0.02, Set([])) for i in 1:n_atoms]\ncoords = place_atoms(n_atoms, boundary; min_dist=0.1)\nvelocities = [random_velocity(1.0, temp; dims=2) for i in 1:n_atoms]\npairwise_inters = (\n SoftSphere(cutoff=DistanceCutoff(2.0), use_neighbors=true),\n BondableInteraction(0.1, 0.1, 1.1, 2.0, 0.1),\n)\nneighbor_finder = DistanceNeighborFinder(\n eligible=trues(n_atoms, n_atoms),\n n_steps=10,\n dist_cutoff=2.2,\n)\nsimulator = VelocityVerlet(\n dt=0.02,\n coupling=AndersenThermostat(temp, 5.0),\n)\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=pairwise_inters,\n neighbor_finder=neighbor_finder,\n loggers=(\n coords=CoordinatesLogger(Float64, 20; dims=2),\n bonds=BondLogger(20),\n ),\n force_units=NoUnits,\n energy_units=NoUnits,\n)\n\nsimulate!(sys, simulator, n_steps)\n\nconnections = Tuple{Int, Int}[]\nfor i in 1:length(sys)\n for j in 1:(i - 1)\n push!(connections, (i, j))\n end\nend\n\nvisualize(\n sys.loggers.coords,\n boundary,\n \"sim_mutbond.mp4\";\n connections=connections,\n connection_frames=values(sys.loggers.bonds),\n markersize=0.1,\n)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Mutable bond simulation)","category":"page"},{"location":"examples/#Comparing-forces-to-AD","page":"Examples","title":"Comparing forces to AD","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"The force is the negative derivative of the potential energy with respect to position. MD packages, including Molly, implement the force functions directly for performance. However it is also possible to compute the forces using AD. Here we compare the two approaches for the Lennard-Jones potential and see that they give the same result.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing Zygote\nusing GLMakie\n\ninter = LennardJones()\nboundary = CubicBoundary(5.0)\na1, a2 = Atom(σ=0.3, ϵ=0.5), Atom(σ=0.3, ϵ=0.5)\n\nfunction force_direct(dist)\n c1 = SVector(1.0, 1.0, 1.0)\n c2 = SVector(dist + 1.0, 1.0, 1.0)\n vec = vector(c1, c2, boundary)\n F = force(inter, vec, a1, a2, NoUnits)\n return F[1]\nend\n\nfunction force_grad(dist)\n grad = gradient(dist) do dist\n c1 = SVector(1.0, 1.0, 1.0)\n c2 = SVector(dist + 1.0, 1.0, 1.0)\n vec = vector(c1, c2, boundary)\n potential_energy(inter, vec, a1, a2, NoUnits)\n end\n return -grad[1]\nend\n\ndists = collect(0.2:0.01:1.2)\nforces_direct = force_direct.(dists)\nforces_grad = force_grad.(dists)\n\nf = Figure(resolution=(600, 400))\nax = Axis(\n f[1, 1],\n xlabel=\"Distance / nm\",\n ylabel=\"Force / kJ * mol^-1 * nm^-1\",\n title=\"Comparing gradients from direct calculation and AD\",\n)\nscatter!(ax, dists, forces_direct, label=\"Direct\", markersize=8)\nscatter!(ax, dists, forces_grad , label=\"AD\" , markersize=8, marker='x')\nxlims!(ax, low=0)\nylims!(ax, -6.0, 10.0)\naxislegend()\nsave(\"force_comparison.png\", f)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Force comparison)","category":"page"},{"location":"examples/#AtomsCalculators.jl-compatibility","page":"Examples","title":"AtomsCalculators.jl compatibility","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"The AtomsCalculators.jl package provides a consistent interface that allows forces, energies etc. to be calculated with different packages. Calculators can be used with a Molly System by giving them as general_inters during system setup. It is also possible to use a MollyCalculator to calculate properties on AtomsBase.jl systems:","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nimport AtomsBase\nusing AtomsBaseTesting\nusing AtomsCalculators\n\nab_sys = AtomsBase.AbstractSystem(\n make_test_system().system; \n bounding_box = [[1.54732, 0.0 , 0.0 ],\n [0.0 , 1.4654985, 0.0 ],\n [0.0 , 0.0 , 1.7928950]]u\"Å\",\n)\n\ncoul = Coulomb(coulomb_const=2.307e-21u\"kJ*Å\")\ncalc = MollyCalculator(pairwise_inters=(coul,), force_units=u\"kJ/Å\", energy_units=u\"kJ\")\n\nAtomsCalculators.potential_energy(ab_sys, calc)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"9.112207692184968e-21 kJ","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"AtomsCalculators.forces(ab_sys, calc)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"5-element Vector{SVector{3, Quantity{Float64, 𝐋 𝐌 𝐓^-2, Unitful.FreeUnits{(Å^-1, kJ), 𝐋 𝐌 𝐓^-2, nothing}}}}:\n [5.052086904272771e-21 kJ Å^-1, 1.0837307191961731e-20 kJ Å^-1, -5.366866699852613e-21 kJ Å^-1]\n [5.252901001053284e-22 kJ Å^-1, -2.3267009382813732e-21 kJ Å^-1, 9.276115314848821e-21 kJ Å^-1]\n [-8.613462805775053e-21 kJ Å^-1, 5.726650141840073e-21 kJ Å^-1, -2.072868074170469e-20 kJ Å^-1]\n [3.0360858013969523e-21 kJ Å^-1, -1.423725639552043e-20 kJ Å^-1, 1.681943212670848e-20 kJ Å^-1]\n [0.0 kJ Å^-1, 0.0 kJ Å^-1, 0.0 kJ Å^-1]","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"We can also convert the AtomsBase.jl system to a Molly System:","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"System(ab_sys; force_units=u\"kJ/Å\", energy_units=u\"kJ\")","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"System with 5 atoms, boundary CubicBoundary{Quantity{Float64, 𝐋, Unitful.FreeUnits{(Å,), 𝐋, nothing}}}(Quantity{Float64, 𝐋, Unitful.FreeUnits{(Å,), 𝐋, nothing}}[1.54732 Å, 1.4654985 Å, 1.792895 Å])","category":"page"},{"location":"examples/#Variations-of-the-Morse-potential","page":"Examples","title":"Variations of the Morse potential","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"The Morse potential for bonds has a parameter a that determines the width of the potential. It can also be compared to the harmonic bond potential.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing GLMakie\n\nboundary = CubicBoundary(5.0)\ndists = collect(0.12:0.005:2.0)\n\nfunction energies(inter)\n return map(dists) do dist\n c1 = SVector(1.0, 1.0, 1.0)\n c2 = SVector(dist + 1.0, 1.0, 1.0)\n potential_energy(inter, c1, c2, boundary)\n end\nend\n\nf = Figure(resolution=(600, 400))\nax = Axis(\n f[1, 1],\n xlabel=\"Distance / nm\",\n ylabel=\"Potential energy / kJ * mol^-1\",\n title=\"Variations of the Morse potential\",\n)\nlines!(\n ax,\n dists,\n energies(HarmonicBond(k=20_000.0, r0=0.2)),\n label=\"Harmonic\",\n)\nfor a in [2.5, 5.0, 10.0]\n lines!(\n ax,\n dists,\n energies(MorseBond(D=100.0, a=a, r0=0.2)),\n label=\"Morse a=$a nm^-1\",\n )\nend\nylims!(ax, 0.0, 120.0)\naxislegend(position=:rb)\nsave(\"morse.png\", f)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Morse)","category":"page"},{"location":"examples/#Variations-of-the-Mie-potential","page":"Examples","title":"Variations of the Mie potential","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"The Mie potential is parameterised by m describing the attraction and n describing the repulsion. When m=6 and n=12 this is equivalent to the Lennard-Jones potential.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing GLMakie\n\nboundary = CubicBoundary(5.0)\na1, a2 = Atom(σ=0.3, ϵ=0.5), Atom(σ=0.3, ϵ=0.5)\ndists = collect(0.2:0.005:0.8)\n\nfunction energies(m, n)\n inter = Mie(m=m, n=n)\n return map(dists) do dist\n c1 = SVector(1.0, 1.0, 1.0)\n c2 = SVector(dist + 1.0, 1.0, 1.0)\n vec = vector(c1, c2, boundary)\n potential_energy(inter, vec, a1, a2, NoUnits)\n end\nend\n\nf = Figure(resolution=(600, 400))\nax = Axis(\n f[1, 1],\n xlabel=\"Distance / nm\",\n ylabel=\"Potential energy / kJ * mol^-1\",\n title=\"Variations of the Mie potential\",\n)\nfor m in [4, 6]\n for n in [10, 12]\n lines!(\n ax,\n dists,\n energies(Float64(m), Float64(n)),\n label=\"m=$m, n=$n\",\n )\n end\nend\nxlims!(ax, low=0.2)\nylims!(ax, -0.6, 0.3)\naxislegend(position=:rb)\nsave(\"mie.png\", f)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Mie)","category":"page"},{"location":"examples/#Variations-of-the-soft-core-LJ-potential","page":"Examples","title":"Variations of the soft-core LJ potential","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"The soft-core Lennard-Jones potential is parameterised by alpha, lambda and p in addition to the standard Lennard-Jones parameters. These parameters shift the value of r_ij to left(r_ij^6 + sigma_ij alpha lambda^p right)^frac16. This gives a soft core, i.e. the potential does not diverge for r_ij rightarrow 0.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing GLMakie\n\nboundary = CubicBoundary(5.0)\na1, a2 = Atom(σ=0.3, ϵ=0.5), Atom(σ=0.3, ϵ=0.5)\ndists = collect(0.05:0.005:0.8)\n\nfunction energies(α, λ, p)\n inter = LennardJonesSoftCore(α=α, λ=λ, p=p)\n return map(dists) do dist\n c1 = SVector(1.0, 1.0, 1.0)\n c2 = SVector(dist + 1.0, 1.0, 1.0)\n vec = vector(c1, c2, boundary)\n potential_energy(inter, vec, a1, a2, NoUnits)\n end\nend\n\nf = Figure(resolution=(600, 400))\nax = Axis(\n f[1, 1],\n xlabel=\"Distance / nm\",\n ylabel=\"Potential energy / kJ * mol^-1\",\n title=\"Variations of the soft-core Lennard-Jones potential\",\n)\nfor λ in [0.8, 0.9]\n for α in [0.2, 0.4]\n for p in [2]\n lines!(\n ax,\n dists,\n energies(α, λ, p),\n label=\"α=$α, λ=$λ, p=$p\",\n )\n end\n end\nend\n\nlines!(ax, dists, energies(0, 1, 2), label=\"standard LJ potential\")\n\nylims!(-5, 25)\naxislegend(position=:rt)\nsave(\"lennard_jones_sc.png\", f)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"(Image: Lennard-Jones Softcore)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"The form of the potential is approximately the same as standard Lennard-Jones for r_ij sigma_ij if some fractional values are used for lambda and alpha.","category":"page"},{"location":"examples/#Crystal-structures","page":"Examples","title":"Crystal structures","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"Molly can make use of SimpleCrystals.jl to generate crystal structures for simulation. All 3D Bravais lattices and most 2D Bravais lattices are supported as well as user-defined crystals through the SimpleCrystals API. The only unsupported crystal types are those with a triclinic 2D simulation domain or crystals with lattice angles larger than 90°.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Molly provides a constructor for System that takes in a Crystal struct:","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nimport SimpleCrystals\n\na = 0.52468u\"nm\" # Lattice parameter for FCC Argon at 10 K\natom_mass = 39.948u\"g/mol\"\ntemp = 10.0u\"K\"\nfcc_crystal = SimpleCrystals.FCC(a, atom_mass, SVector(4, 4, 4))\n\nn_atoms = length(fcc_crystal)\nvelocities = [random_velocity(atom_mass, temp) for i in 1:n_atoms]\n\nr_cut = 0.85u\"nm\"\nsys = System(\n fcc_crystal;\n velocities=velocities,\n pairwise_inters=(LennardJones(cutoff=ShiftedForceCutoff(r_cut)),),\n loggers=(\n kinetic_eng=KineticEnergyLogger(100),\n pot_eng=PotentialEnergyLogger(100),\n ),\n energy_units=u\"kJ * mol^-1\",\n force_units=u\"kJ * mol^-1 * nm^-1\",\n)","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Certain potentials such as LennardJones and Buckingham require extra atomic paramaters (e.g. σ) that are not implemented by the SimpleCrystals API. These paramaters must be added to the System manually by making use of the copy constructor:","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"σ = 0.34u\"nm\"\nϵ = (4.184 * 0.24037)u\"kJ * mol^-1\"\nupdated_atoms = []\n\nfor i in eachindex(sys)\n push!(updated_atoms, Atom(index=sys.atoms[i].index, atom_type=sys.atoms[i].atom_type,\n mass=sys.atoms[i].mass, charge=sys.atoms[i].charge,\n σ=σ, ϵ=ϵ))\nend\n\nsys = System(sys; atoms=[updated_atoms...])","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"Now the system can be simulated using any of the available simulators:","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"simulator = Langevin(\n dt=2.0u\"fs\",\n temperature=temp,\n friction=1.0u\"ps^-1\",\n)\nsimulate!(sys, simulator, 200_000)","category":"page"},{"location":"examples/#Constrained-dynamics","page":"Examples","title":"Constrained dynamics","text":"","category":"section"},{"location":"examples/","page":"Examples","title":"Examples","text":"Molly supports the SHAKE and RATTLE constraint algorithms. The code below shows an example where molecules of hydrogen are randomly placed in a box and constrained during a simulation.","category":"page"},{"location":"examples/","page":"Examples","title":"Examples","text":"using Molly\nusing Test\n\nr_cut = 8.5u\"Å\"\ntemp = 300.0u\"K\"\natom_mass = 1.00794u\"g/mol\"\n\nn_atoms_half = 200\natoms = [Atom(index=i, mass=atom_mass, σ=2.8279u\"Å\", ϵ=0.074u\"kcal* mol^-1\") for i in 1:n_atoms_half]\nmax_coord = 200.0u\"Å\"\ncoords = [max_coord .* rand(SVector{3}) for i in 1:n_atoms_half]\nboundary = CubicBoundary(200.0u\"Å\")\nlj = LennardJones(cutoff=ShiftedPotentialCutoff(r_cut), use_neighbors=true)\n\n# Add bonded atoms\nbond_length = 0.74u\"Å\" # Hydrogen bond length\nconstraints = []\nfor j in 1:n_atoms_half\n push!(atoms, Atom(index=(j + n_atoms_half), mass=atom_mass, σ=2.8279u\"Å\", ϵ=0.074u\"kcal* mol^-1\"))\n push!(coords, coords[j] .+ SVector(bond_length, 0.0u\"Å\", 0.0u\"Å\"))\n push!(constraints, DistanceConstraint(j, j + n_atoms_half, bond_length))\nend\n\nshake = SHAKE_RATTLE([constraints...], length(atoms), 1e-8u\"Å\", 1e-8u\"Å^2 * ps^-1\")\n\nneighbor_finder = DistanceNeighborFinder(\n eligible=trues(length(atoms), length(atoms)),\n dist_cutoff=1.5*r_cut,\n)\ndisable_constrained_interactions!(neighbor_finder, shake.clusters)\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n pairwise_inters=(lj,),\n neighbor_finder=neighbor_finder,\n constraints=(shake,),\n energy_units=u\"kcal * mol^-1\",\n force_units=u\"kcal * mol^-1 * Å^-1\",\n)\n\nrandom_velocities!(sys, temp)\n\nsimulator = VelocityVerlet(dt=0.001u\"ps\")\n\nsimulate!(sys, simulator, 10_000)\n\n# Check that the constraints are satisfied at the end of the simulation\n@test check_position_constraints(sys, shake)\n@test check_velocity_constraints(sys, shake)","category":"page"},{"location":"differentiable/#Differentiable-simulation-with-Molly","page":"Differentiable simulation","title":"Differentiable simulation with Molly","text":"","category":"section"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"note: Note\nThere are still many rough edges when taking gradients through simulations. Please open an issue if you run into an error and remember the golden rule of AD: check your gradients against finite differencing if you want to make sure they are correct.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"In the last few years, the deep learning revolution has broadened to include the paradigm of differentiable programming. The concept of using automatic differentiation (AD) to obtain exact gradients through physical simulations has many interesting applications, including parameterising force fields and training neural networks to describe atomic potentials.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"There are some projects that explore differentiable molecular simulations - see Related software. However Julia provides a strong suite of AD tools, with Enzyme.jl allowing source-to-source transformations for much of the language. With Molly you can use the power of Enzyme to obtain gradients through molecular simulations, even in the presence of complex interactions such as implicit solvation and stochasticity such as Langevin dynamics or the Andersen thermostat. Reverse mode AD can be used on the CPU with multithreading and on the GPU; performance is typically within an order of magnitude of the primal run. Forward mode AD can also be used on the CPU. Pairwise, specific and general interactions work, along with neighbor lists, and the same abstractions for running simulations are used as in the main package.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Differentiable simulation does not currently work with units and some components of the package. This is mentioned in the relevant docstrings. It is memory intensive on the GPU so using gradient checkpointing will likely be required for larger simulations.","category":"page"},{"location":"differentiable/#Pairwise-interactions","page":"Differentiable simulation","title":"Pairwise interactions","text":"","category":"section"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"First, we show how taking gradients through a simulation can be used to optimise an atom property in a Lennard-Jones fluid. In this type of simulation each atom has a σ value that determines how close it likes to get to other atoms. We are going to find the σ value that results in a desired distance of each atom to its closest neighbor. First we need a function to obtain the mean distance of each atom to its closest neighbor:","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"using Molly\n\nfunction mean_min_separation(final_coords, boundary)\n n_atoms = length(final_coords)\n sum_dists = 0.0\n for i in 1:n_atoms\n min_dist = 100.0\n for j in 1:n_atoms\n i == j && continue\n dist = sqrt(sum(abs2, vector(final_coords[i], final_coords[j], boundary)))\n min_dist = min(dist, min_dist)\n end\n sum_dists += min_dist\n end\n return sum_dists / n_atoms\nend","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Now we can set up and run the simulation in a similar way to that described in the Molly documentation. The difference is that we wrap the simulation in a loss function. This returns a single value that we want to obtain gradients with respect to, in this case the difference between the value of the above function at the end of the simulation and a target distance.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"using Zygote\nusing Format\n\ndist_true = 0.5\nscale_σ_to_dist = 2 ^ (1 / 6)\nσtrue = dist_true / scale_σ_to_dist\n\nn_atoms = 50\nn_steps = 500\natom_mass = 10.0\nboundary = CubicBoundary(3.0)\ntemp = 1.0\nneighbor_finder = DistanceNeighborFinder(\n eligible=trues(n_atoms, n_atoms),\n n_steps=10,\n dist_cutoff=1.8,\n)\nlj = LennardJones(\n cutoff=DistanceCutoff(1.5),\n use_neighbors=true,\n)\npairwise_inters = (lj,)\ncoords = place_atoms(n_atoms, boundary; min_dist=0.6)\nvelocities = [random_velocity(atom_mass, temp) for i in 1:n_atoms]\nsimulator = VelocityVerlet(\n dt=0.02,\n coupling=RescaleThermostat(temp),\n)\n\nfunction loss(σ, coords, velocities)\n atoms = [Atom(0, 0, atom_mass, 0.0, σ, 0.2) for i in 1:n_atoms]\n loggers = (coords=CoordinatesLogger(Float64, 10),)\n\n sys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=pairwise_inters,\n neighbor_finder=neighbor_finder,\n loggers=loggers,\n force_units=NoUnits,\n energy_units=NoUnits,\n )\n\n mms_start = mean_min_separation(Array(sys.coords), boundary)\n simulate!(sys, simulator, n_steps)\n mms_end = mean_min_separation(Array(sys.coords), boundary)\n loss_val = abs(mms_end - dist_true)\n\n printfmt(\"σ {:6.3f} | Mean min sep expected {:6.3f} | Mean min sep end {:6.3f} | Loss {:6.3f} | \",\n σ, σ * (2 ^ (1 / 6)), mms_end, loss_val)\n\n return loss_val\nend","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"We can obtain the gradient of loss with respect to the atom property σ.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"grad = gradient(loss, σtrue, coords, velocities)[1]","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"This gradient can be used in a training loop to optimise σ, starting from an arbitrary value.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"function train()\n σlearn = 0.60 / scale_σ_to_dist\n n_epochs = 15\n\n for epoch_n in 1:n_epochs\n printfmt(\"Epoch {:>2} | \", epoch_n)\n coords = place_atoms(n_atoms, boundary; min_dist=0.6)\n velocities = [random_velocity(atom_mass, temp) for i in 1:n_atoms]\n grad = gradient(loss, σlearn, coords, velocities)[1]\n printfmt(\"Grad {:6.3f}\\n\", grad)\n σlearn -= grad * 1e-2\n end\nend\n\ntrain()","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Epoch 1 | σ 0.535 | Mean min sep expected 0.600 | Mean min sep end 0.587 | Loss 0.087 | Grad 0.793\nEpoch 2 | σ 0.527 | Mean min sep expected 0.591 | Mean min sep end 0.581 | Loss 0.081 | Grad 1.202\nEpoch 3 | σ 0.515 | Mean min sep expected 0.578 | Mean min sep end 0.568 | Loss 0.068 | Grad 1.558\nEpoch 4 | σ 0.499 | Mean min sep expected 0.560 | Mean min sep end 0.551 | Loss 0.051 | Grad 0.766\nEpoch 5 | σ 0.491 | Mean min sep expected 0.552 | Mean min sep end 0.543 | Loss 0.043 | Grad 1.068\nEpoch 6 | σ 0.481 | Mean min sep expected 0.540 | Mean min sep end 0.531 | Loss 0.031 | Grad 0.757\nEpoch 7 | σ 0.473 | Mean min sep expected 0.531 | Mean min sep end 0.526 | Loss 0.026 | Grad 0.781\nEpoch 8 | σ 0.465 | Mean min sep expected 0.522 | Mean min sep end 0.518 | Loss 0.018 | Grad 1.549\nEpoch 9 | σ 0.450 | Mean min sep expected 0.505 | Mean min sep end 0.504 | Loss 0.004 | Grad 0.030\nEpoch 10 | σ 0.450 | Mean min sep expected 0.505 | Mean min sep end 0.504 | Loss 0.004 | Grad 0.066\nEpoch 11 | σ 0.449 | Mean min sep expected 0.504 | Mean min sep end 0.503 | Loss 0.003 | Grad 0.313\nEpoch 12 | σ 0.446 | Mean min sep expected 0.500 | Mean min sep end 0.501 | Loss 0.001 | Grad 0.636\nEpoch 13 | σ 0.439 | Mean min sep expected 0.493 | Mean min sep end 0.497 | Loss 0.003 | Grad -0.181\nEpoch 14 | σ 0.441 | Mean min sep expected 0.495 | Mean min sep end 0.498 | Loss 0.002 | Grad -0.758\nEpoch 15 | σ 0.449 | Mean min sep expected 0.504 | Mean min sep end 0.503 | Loss 0.003 | Grad 0.281","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"The final value we get is 0.449, close to the theoretical value of 0.445 if all atoms have a neighbor at the minimum pairwise energy distance. The RDF looks as follows, with the purple line corresponding to the desired distance to the closest neighbor. (Image: LJ RDF)","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"To make this run on the GPU the appropriate objects should be transferred to the GPU with CuArray: coords, velocities, atoms and the eligible matrix for the neighbor finder. If using custom interactions or some built-in interactions you may need to define methods of zero and + for your interaction type.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"It is common to require a loss function formed from values throughout a simulation. In this case it is recommended to split up the simulation into a set of short simulations in the loss function, each starting from the previous final coordinates and velocities. This runs an identical simulation but makes the intermediate coordinates and velocities available for use in calculating the final loss. For example, the RMSD could be calculated from the coordinates every 100 steps and added to a variable that is then divided by the number of chunks to get a loss value corresponding to the mean RMSD over the simulation. Loggers are ignored for gradient calculation and should not be used in the loss function.","category":"page"},{"location":"differentiable/#Specific-interactions","page":"Differentiable simulation","title":"Specific interactions","text":"","category":"section"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Next we look at obtaining gradients through simulations with specific interactions, e.g. bonds or angles between specified atoms. We will simulate two triatomic molecules and search for a minimum energy bond angle that gives a desired distance between the atoms at the end of the simulation.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"using Molly\nusing Zygote\nusing Format\nusing LinearAlgebra\n\ndist_true = 1.0\n\nn_steps = 150\natom_mass = 10.0\nboundary = CubicBoundary(3.0)\ntemp = 0.05\ncoords = [\n SVector(0.8, 0.75, 1.5), SVector(1.5, 0.70, 1.5), SVector(2.3, 0.75, 1.5),\n SVector(0.8, 2.25, 1.5), SVector(1.5, 2.20, 1.5), SVector(2.3, 2.25, 1.5),\n]\nn_atoms = length(coords)\nvelocities = zero(coords)\nsimulator = VelocityVerlet(\n dt=0.05,\n coupling=BerendsenThermostat(temp, 0.5),\n)\n\nfunction loss(θ)\n atoms = [Atom(0, 0, atom_mass, 0.0, 0.0, 0.0) for i in 1:n_atoms]\n loggers = (coords=CoordinatesLogger(Float64, 2),)\n specific_inter_lists = (\n InteractionList2Atoms(\n [1, 2, 4, 5],\n [2, 3, 5, 6],\n [HarmonicBond(100.0, 0.7) for _ in 1:4],\n ),\n InteractionList3Atoms(\n [1, 4],\n [2, 5],\n [3, 6],\n [HarmonicAngle(10.0, θ), HarmonicAngle(10.0, θ)],\n ),\n )\n\n sys = System(\n atoms=atoms,\n coords=copy(coords),\n boundary=boundary,\n velocities=copy(velocities),\n specific_inter_lists=specific_inter_lists,\n loggers=loggers,\n force_units=NoUnits,\n energy_units=NoUnits,\n )\n\n simulate!(sys, simulator, n_steps)\n\n d1 = norm(vector(sys.coords[1], sys.coords[3], boundary))\n d2 = norm(vector(sys.coords[4], sys.coords[6], boundary))\n dist_end = 0.5 * (d1 + d2)\n loss_val = abs(dist_end - dist_true)\n\n printfmt(\"θ {:5.1f}° | Final dist {:4.2f} | Loss {:5.3f} | \",\n rad2deg(θ), dist_end, loss_val)\n\n return loss_val\nend\n\nfunction train()\n θlearn = deg2rad(110.0)\n n_epochs = 20\n\n for epoch_n in 1:n_epochs\n printfmt(\"Epoch {:>2} | \", epoch_n)\n grad = gradient(loss, θlearn)[1]\n printfmt(\"Grad {:6.3f}\\n\", round(grad; digits=2))\n θlearn -= grad * 0.1\n end\nend\n\ntrain()","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Epoch 1 | θ 110.0° | Final dist 1.16 | Loss 0.155 | Grad 0.410\nEpoch 2 | θ 107.7° | Final dist 1.14 | Loss 0.138 | Grad 0.430\nEpoch 3 | θ 105.2° | Final dist 1.12 | Loss 0.119 | Grad 0.450\nEpoch 4 | θ 102.6° | Final dist 1.10 | Loss 0.099 | Grad 0.470\nEpoch 5 | θ 100.0° | Final dist 1.08 | Loss 0.077 | Grad 0.490\nEpoch 6 | θ 97.2° | Final dist 1.05 | Loss 0.049 | Grad 0.710\nEpoch 7 | θ 93.1° | Final dist 1.01 | Loss 0.012 | Grad 0.520\nEpoch 8 | θ 90.1° | Final dist 0.98 | Loss 0.015 | Grad -0.540\nEpoch 9 | θ 93.2° | Final dist 1.01 | Loss 0.013 | Grad 0.520\nEpoch 10 | θ 90.2° | Final dist 0.99 | Loss 0.015 | Grad -0.540\nEpoch 11 | θ 93.3° | Final dist 1.01 | Loss 0.014 | Grad 0.520\nEpoch 12 | θ 90.3° | Final dist 0.99 | Loss 0.014 | Grad -0.540\nEpoch 13 | θ 93.4° | Final dist 1.01 | Loss 0.015 | Grad 0.520\nEpoch 14 | θ 90.4° | Final dist 0.99 | Loss 0.013 | Grad -0.540\nEpoch 15 | θ 93.5° | Final dist 1.02 | Loss 0.016 | Grad 0.520\nEpoch 16 | θ 90.5° | Final dist 0.99 | Loss 0.012 | Grad -0.540\nEpoch 17 | θ 93.6° | Final dist 1.02 | Loss 0.016 | Grad 0.520\nEpoch 18 | θ 90.6° | Final dist 0.99 | Loss 0.011 | Grad -0.530\nEpoch 19 | θ 93.7° | Final dist 1.02 | Loss 0.017 | Grad 0.520\nEpoch 20 | θ 90.7° | Final dist 0.99 | Loss 0.010 | Grad -0.530","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"The final value we get is 90.7°, close to the theoretical value of 91.2° which can be calculated with trigonometry. The final simulation looks like this: (Image: Angle simulation) In the presence of other forces this value would not be so trivially obtainable. We can record the gradients for different values of θ:","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"θs = collect(0:3:180)[2:end]\ngrads = [gradient(loss, deg2rad(θ))[1] for θ in θs]","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"The plot of these shows that the gradient has the expected sign either side of the correct value: (Image: Angle gradient)","category":"page"},{"location":"differentiable/#Neural-network-potentials","page":"Differentiable simulation","title":"Neural network potentials","text":"","category":"section"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Flux models can also be incorporated into simulations. Here we show a neural network in the force function, though they can also be used in other parts of the simulation. This example also shows how gradients for multiple parameters can be obtained, in this case the parameters of the neural network. The jump from single to multiple parameters is important because single parameters can be optimised using finite differencing, whereas differentiable simulation is well-placed to optimise many parameters simultaneously.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"We set up three pseudo-atoms and train a network to imitate the Julia logo by moving the bottom two atoms:","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"using Molly\nusing GLMakie\nusing Zygote\nusing Flux\nimport AtomsCalculators\nusing Format\nusing LinearAlgebra\n\ndist_true = 1.0f0\n\nmodel = Chain(\n Dense(1, 5, relu),\n Dense(5, 1, tanh),\n)\nps = Flux.params(model)\n\nstruct NNBonds end\n\nfunction AtomsCalculators.forces(sys, inter::NNBonds; kwargs...)\n vec_ij = vector(sys.coords[1], sys.coords[3], sys.boundary)\n dist = norm(vec_ij)\n f = model([dist])[1] * normalize(vec_ij)\n fs = [f, zero(f), -f]\n return fs\nend\n\nn_steps = 400\nmass = 10.0f0\nboundary = CubicBoundary(5.0f0)\ntemp = 0.01f0\ncoords = [\n SVector(2.3f0, 2.07f0, 0.0f0),\n SVector(2.5f0, 2.93f0, 0.0f0),\n SVector(2.7f0, 2.07f0, 0.0f0),\n]\nn_atoms = length(coords)\nvelocities = zero(coords)\nsimulator = VelocityVerlet(\n dt=0.02f0,\n coupling=BerendsenThermostat(temp, 0.5f0),\n)\n\nfunction loss()\n atoms = [Atom(0, 0, mass, 0.0f0, 0.0f0, 0.0f0) for i in 1:n_atoms]\n loggers = (coords=CoordinatesLogger(Float32, 10),)\n general_inters = (NNBonds(),)\n\n sys = System(\n atoms=atoms,\n coords=copy(coords),\n boundary=boundary,\n velocities=copy(velocities),\n general_inters=general_inters,\n loggers=loggers,\n force_units=NoUnits,\n energy_units=NoUnits,\n )\n\n simulate!(sys, simulator, n_steps)\n\n dist_end = (norm(vector(sys.coords[1], sys.coords[2], boundary)) +\n norm(vector(sys.coords[2], sys.coords[3], boundary)) +\n norm(vector(sys.coords[3], sys.coords[1], boundary))) / 3\n loss_val = abs(dist_end - dist_true)\n\n printfmt(\"Dist end {:6.3f} | Loss {:6.3f}\\n\", dist_end, loss_val)\n visualize(sys.loggers.coords, boundary, \"sim.mp4\"; show_boundary=false)\n\n return loss_val\nend","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Before training the result looks like this: (Image: Logo before)","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"function train()\n n_epochs = 20\n opt = ADAM(0.02, (0.9, 0.999))\n\n for epoch_n in 1:n_epochs\n printfmt(\"Epoch {:>2} | \", epoch_n)\n Flux.train!(loss, ps, ((),), opt)\n end\nend\n\ntrain()","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Epoch 1 | Dist end 0.757 | Loss 0.243\nEpoch 2 | Dist end 0.773 | Loss 0.227\nEpoch 3 | Dist end 0.794 | Loss 0.206\nEpoch 4 | Dist end 0.817 | Loss 0.183\nEpoch 5 | Dist end 0.843 | Loss 0.157\nEpoch 6 | Dist end 0.870 | Loss 0.130\nEpoch 7 | Dist end 0.898 | Loss 0.102\nEpoch 8 | Dist end 0.927 | Loss 0.073\nEpoch 9 | Dist end 0.957 | Loss 0.043\nEpoch 10 | Dist end 0.988 | Loss 0.012\nEpoch 11 | Dist end 1.018 | Loss 0.018\nEpoch 12 | Dist end 1.038 | Loss 0.038\nEpoch 13 | Dist end 1.050 | Loss 0.050\nEpoch 14 | Dist end 1.055 | Loss 0.055\nEpoch 15 | Dist end 1.054 | Loss 0.054\nEpoch 16 | Dist end 1.049 | Loss 0.049\nEpoch 17 | Dist end 1.041 | Loss 0.041\nEpoch 18 | Dist end 1.030 | Loss 0.030\nEpoch 19 | Dist end 1.017 | Loss 0.017\nEpoch 20 | Dist end 1.003 | Loss 0.003","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"After training it looks much better: (Image: Logo after) You could replace the simple network here with a much more complicated model and it would theoretically be able to train, even if it might prove practically difficult (see discussion below).","category":"page"},{"location":"differentiable/#Biomolecular-force-fields","page":"Differentiable simulation","title":"Biomolecular force fields","text":"","category":"section"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Molly was used to train the GB99dms force field for implicit solvent molecular dynamics of proteins. This involved doing differentiable simulations of one million steps with a loss function based on the residue-residue distance match to explicit solvent simulations. The code is available.","category":"page"},{"location":"differentiable/#Molecular-loss-functions","page":"Differentiable simulation","title":"Molecular loss functions","text":"","category":"section"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Ultimately, you need some objective function in order to calculate the gradient for each parameter. Here are some ideas for loss functions suitable for differentiable molecular simulations:","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"The distance between atoms at the end of the simulation compared to some reference state. This loss is used in the examples given here, is physically reasonable, and has obvious bounds.\nThe distance between atoms throughout the simulation.\nThe radial distribution function of atoms.\nRMSD between atoms and a reference state - this would be suitable for macromolecules.\ndRMSD, the distance between a distance map and a reference distance map.\nThe radius of gyration of a molecule.\nThe flexibility of a set of atoms over the simulation.\nSupramolecular geometry, for example assembly of molecules into straight fibres.\nThe correlation of different velocities over the simulation.\nThe energy of the system.\nThe temperature of the system.\nSome measure of phase change or a critical point.\nA combination of the above, for example to obtain a force field relevant to both ordered and disordered proteins.","category":"page"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"Some of these are currently not possible in Molly as the loggers are ignored for gradient purposes, but this will hopefully change in future.","category":"page"},{"location":"differentiable/#Tips-and-tricks","page":"Differentiable simulation","title":"Tips and tricks","text":"","category":"section"},{"location":"differentiable/","page":"Differentiable simulation","title":"Differentiable simulation","text":"The magnitude of gradients may be less important than the sign. Consider sampling gradients across different sources of stochasticity, such as starting velocities and conformations.\nExploding gradients prove a problem when using the velocity Verlet integrator in the NVE ensemble. This is why the velocity rescaling and Berendsen thermostats were used in the above examples. Langevin dynamics also seems to work. It is likely that the development of suitable simulation strategies and thermostats will be necessary to unlock the potential of differentiable simulation.\nDo you really need a neural network to describe your potential? Think about learning a smaller number of physically-meaningful parameters before you put in a large neural network and expect it to learn. Whilst it is true that neural networks are universal function approximators, it does not follow that you will be able to train one by differentiating through a long simulation. A 1,000-step simulation with a 10-layer network at each step is analogous to training a 10,000 layer network (with shared weights).\nForward mode AD holds much promise for differentiable simulation, provided that the number of parameters is small, because the memory requirement is constant in the number of simulation steps. However, if the code runs slower than non-differentiable alternatives then the best approach is likely to use finite differencing with the simulation as a black box. Adjoint sensitivity is another approach to getting gradients which is not yet available in Molly.jl.","category":"page"},{"location":"","page":"Home","title":"Home","text":"\"Molly","category":"page"},{"location":"","page":"Home","title":"Home","text":"(Image: Build status) (Image: Coverage status) (Image: Latest release) (Image: License) (Image: Documentation stable) (Image: Documentation dev)","category":"page"},{"location":"","page":"Home","title":"Home","text":"Much of science can be explained by the movement and interaction of molecules. Molecular dynamics (MD) is a computational technique used to explore these phenomena, from noble gases to biological macromolecules. Molly.jl is a pure Julia package for MD, and for the simulation of physical systems more broadly.","category":"page"},{"location":"","page":"Home","title":"Home","text":"The package is described in a talk at JuliaCon 2024 and an earlier talk at Enzyme Conference 2023. Slides are also available for a tutorial in September 2023.","category":"page"},{"location":"","page":"Home","title":"Home","text":"Implemented features include:","category":"page"},{"location":"","page":"Home","title":"Home","text":"Non-bonded interactions - Lennard-Jones Van der Waals/repulsion force, electrostatic Coulomb potential and reaction field, gravitational potential, soft sphere potential, Mie potential, Buckingham potential, soft core variants.\nBonded interactions - harmonic and Morse bonds, bond angles, torsion angles, harmonic position restraints, FENE bonds.\nInterface to allow definition of new interactions, simulators, thermostats, neighbor finders, loggers etc.\nRead in OpenMM force field files and coordinate files supported by Chemfiles.jl. There is also experimental support for Gromacs files.\nVerlet, velocity Verlet, Störmer-Verlet, flexible Langevin and Nosé-Hoover integrators.\nAndersen, Berendsen and velocity rescaling thermostats.\nMonte Carlo barostat and variants.\nSteepest descent energy minimization.\nReplica exchange molecular dynamics.\nMonte Carlo simulation.\nPeriodic, triclinic and infinite boundary conditions.\nConstraints with SHAKE and RATTLE\nFlexible loggers to track arbitrary properties throughout simulations.\nCutoff algorithms for non-bonded interactions.\nVarious neighbor list implementations to speed up the calculation of non-bonded forces, including the use of CellListMap.jl.\nImplicit solvent GBSA methods.\nUnitful.jl compatibility so numbers have physical meaning.\nSet up crystal systems using SimpleCrystals.jl.\nAutomatic multithreading.\nGPU acceleration on CUDA-enabled devices.\nRun with Float64, Float32 or other float types.\nSome analysis functions, e.g. RDF.\nVisualise simulations as animations with Makie.jl.\nCompatibility with AtomsBase.jl and AtomsCalculators.jl.\nInterface to use Python ASE calculators.\nDifferentiable molecular simulation. This is a unique feature of the package and the focus of its current development.","category":"page"},{"location":"","page":"Home","title":"Home","text":"Features not yet implemented include:","category":"page"},{"location":"","page":"Home","title":"Home","text":"High GPU performance.\nEwald or particle mesh Ewald summation.\nFull support for constrained bonds and angles.\nProtein preparation - solvent box, add hydrogens etc.\nSimulators such as metadynamics.\nQuantum mechanical modelling.\nDomain decomposition algorithms.\nAlchemical free energy calculations.\nHigh test coverage.\nAPI stability.","category":"page"},{"location":"#Installation","page":"Home","title":"Installation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Julia is required, with Julia v1.9 or later required to get the latest version of Molly. It is recommended to run on the current stable Julia release for the best performance. Install Molly from the Julia REPL. Enter the package mode by pressing ] and run add Molly.","category":"page"},{"location":"#Usage","page":"Home","title":"Usage","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Some examples are given here, see the documentation for more on how to use the package.","category":"page"},{"location":"","page":"Home","title":"Home","text":"Simulation of a Lennard-Jones fluid:","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Molly\n\nn_atoms = 100\nboundary = CubicBoundary(2.0u\"nm\")\ntemp = 298.0u\"K\"\natom_mass = 10.0u\"g/mol\"\n\natoms = [Atom(mass=atom_mass, σ=0.3u\"nm\", ϵ=0.2u\"kJ * mol^-1\") for i in 1:n_atoms]\ncoords = place_atoms(n_atoms, boundary; min_dist=0.3u\"nm\")\nvelocities = [random_velocity(atom_mass, temp) for i in 1:n_atoms]\npairwise_inters = (LennardJones(),)\nsimulator = VelocityVerlet(\n dt=0.002u\"ps\",\n coupling=AndersenThermostat(temp, 1.0u\"ps\"),\n)\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=pairwise_inters,\n loggers=(temp=TemperatureLogger(100),),\n)\n\nsimulate!(sys, simulator, 10_000)","category":"page"},{"location":"","page":"Home","title":"Home","text":"Simulation of a protein:","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Molly\n\nsys = System(\n joinpath(dirname(pathof(Molly)), \"..\", \"data\", \"5XER\", \"gmx_coords.gro\"),\n joinpath(dirname(pathof(Molly)), \"..\", \"data\", \"5XER\", \"gmx_top_ff.top\");\n loggers=(\n temp=TemperatureLogger(10),\n writer=StructureWriter(10, \"traj_5XER_1ps.pdb\"),\n ),\n)\n\ntemp = 298.0u\"K\"\nrandom_velocities!(sys, temp)\nsimulator = VelocityVerlet(\n dt=0.0002u\"ps\",\n coupling=AndersenThermostat(temp, 1.0u\"ps\"),\n)\n\nsimulate!(sys, simulator, 5_000)","category":"page"},{"location":"","page":"Home","title":"Home","text":"The above 1 ps simulation looks something like this when you view it in VMD: (Image: MD simulation)","category":"page"},{"location":"#Contributing","page":"Home","title":"Contributing","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Contributions are very welcome including reporting bugs, fixing bugs, adding new features, improving performance, adding tests and improving documentation. Feel free to open an issue or use the channels below to discuss your contribution. New features will generally require tests to be added as well. See the roadmap issue for some discussion of recent progress and future plans.","category":"page"},{"location":"","page":"Home","title":"Home","text":"Join the #juliamolsim channel on the Julia Slack, the #molly channel on the JuliaMolSim Zulip or post on the Julia Discourse to discuss the usage and development of Molly.jl.","category":"page"},{"location":"","page":"Home","title":"Home","text":"Molly.jl follows the Contributor Covenant code of conduct.","category":"page"},{"location":"#Citation","page":"Home","title":"Citation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"If you use Molly, please cite the following paper (bib entry here):","category":"page"},{"location":"","page":"Home","title":"Home","text":"Greener JG. Differentiable simulation to develop molecular dynamics force fields for disordered proteins, Chemical Science 15, 4897-4909 (2024)","category":"page"},{"location":"","page":"Home","title":"Home","text":"A paper involving more contributors with further details on the software will be written at some point.","category":"page"},{"location":"#Interested?","page":"Home","title":"Interested?","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"There is the possibility of a postdoc position involving the development and use of Molly. Contact Joe Greener with informal enquiries.","category":"page"},{"location":"documentation/#Molly-documentation","page":"Documentation","title":"Molly documentation","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"This documentation will first introduce the main features of the package with some examples, then will give details on each component of a simulation. There are further examples in the Molly examples section. For more information on specific types or functions, see the Molly API section or call ?function_name in Julia. The Differentiable simulation with Molly section describes taking gradients through simulations.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The package takes a modular approach to molecular simulation. To run a simulation you create a System object and call simulate! on it. The different components of the system and simulation can be used as defined by the package, or you can define your own versions. An important principle of the package is that your custom components, particularly force and potential energy functions, should be easy to define and just as performant as the built-in versions.","category":"page"},{"location":"documentation/#Simulation-basics","page":"Documentation","title":"Simulation basics","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Let's look at the simulation of a fluid acting under the Lennard-Jones potential to start with. First, we'll need some atoms with the relevant parameters defined.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"using Molly\n\nn_atoms = 100\natom_mass = 10.0u\"g/mol\"\natoms = [Atom(mass=atom_mass, σ=0.3u\"nm\", ϵ=0.2u\"kJ * mol^-1\") for i in 1:n_atoms]","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"See the Unitful.jl documentation for more information on the unit annotations. Molly re-exports Unitful.jl and StaticArrays.jl since they are often required to run simulations. You can use your own atom types in Molly, provided that the mass function is defined and any fields required by the interactions are present. Next, we'll need some starting coordinates and velocities.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"boundary = CubicBoundary(2.0u\"nm\") # Periodic boundary conditions with a 2 nm cube\ncoords = place_atoms(n_atoms, boundary; min_dist=0.3u\"nm\") # Random placement without clashing\n\ntemp = 100.0u\"K\"\nvelocities = [random_velocity(atom_mass, temp) for i in 1:n_atoms]","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"We store the coordinates and velocities as static arrays for performance. They can be of 2 or 3 dimensions and of any number type, e.g. Float64 or Float32. Setting individual dimensions in a CubicBoundary to Inf * u\"nm\" makes the simulation have no boundary in that dimension. You can also use a TriclinicBoundary. Simulations in 2 dimensions should use a RectangularBoundary.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The vector function calculates the vector from one coordinate to another accounting for periodic boundary conditions by using the minimum image convention:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"vector(coords[1], coords[2], boundary)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"3-element SVector{3, Quantity{Float64, 𝐋, Unitful.FreeUnits{(nm,), 𝐋, nothing}}} with indices SOneTo(3):\n -0.8619698558762985 nm\n -0.9475020064484192 nm\n 0.8359421827141784 nm","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Now we can define our pairwise interactions, i.e. those between most or all atom pairs. Because we have defined the relevant parameters for the atoms, we can use the built-in LennardJones type.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"pairwise_inters = (LennardJones(),) # Don't forget the trailing comma!","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Finally, we can define the system and run the simulation. We use an AndersenThermostat to keep a constant temperature, and we log the temperature and coordinates every 10 steps. Periodic boundary conditions are automatically used with the cubic box we defined earlier.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"sys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=pairwise_inters,\n loggers=(\n temp=TemperatureLogger(10),\n coords=CoordinatesLogger(10),\n ),\n)\n\nsimulator = VelocityVerlet(\n dt=0.002u\"ps\",\n coupling=AndersenThermostat(temp, 1.0u\"ps\"),\n)\n\nsimulate!(sys, simulator, 1_000)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"atoms, coords and boundary are the minimum required properties to define a System, though you would generally want to add interactions to a System to do something useful with it. The data keyword argument can give arbitrary data to the System that can be accessed with sys.data, for example metadata or properties to be used with a custom simulator. System implements the AbstractSystem interface from AtomsBase.jl. Various functions can be used on a System:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"potential_energy(sys) # -40.91077282719628 kJ mol^-1\nkinetic_energy(sys) # 119.47758705080862 kJ mol^-1\ntotal_energy(sys) # 78.56681422361234 kJ mol^-1\n\nforces(sys)\naccelerations(sys)\n\nmasses(sys)\ndensity(sys) # 207.56738339673083 kg m^-3\ntemperature(sys) # 96.76667184796673 K\nrandom_velocities(sys, 300.0u\"K\")\n\nfloat_type(sys) # Float64\nis_on_gpu(sys) # false\n\n# Access properties\nsys.atoms\nsys.coords\nsys.boundary\nsys.velocities\nsys.topology\nsys.pairwise_inters\nsys.constraints\nsys.neighbor_finder\nsys.loggers\n\n# For certain systems\nvirial(sys)\npressure(sys)\ndipole_moment(sys)\n\n# AtomsBase.jl interface\nimport AtomsBase\nAtomsBase.mass(sys, 5) # 10.0 u\nAtomsBase.position(sys, 10)\n\n# Define a new system with certain properties changed\nSystem(sys; coords=(sys.coords .* 0.5))","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"By default the simulation is run in parallel on the number of threads available to Julia, but this behaviour can be changed by giving the keyword argument n_threads to simulate!. For example, n_threads=1 uses no parallelization.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The values stored by the loggers can be accessed using values, e.g. values(sys.loggers.coords). An animation of the stored coordinates can be saved by using visualize, which is available when GLMakie.jl is imported.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"using GLMakie\nvisualize(sys.loggers.coords, boundary, \"sim_lj.mp4\")","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"(Image: LJ simulation)","category":"page"},{"location":"documentation/#GPU-acceleration","page":"Documentation","title":"GPU acceleration","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To run simulations on the GPU you will need to have a CUDA-compatible device. CUDA.jl is used to run on the device. Simulation setup is similar to above, but with the coordinates, velocities and atoms moved to the GPU. This example also shows setting up a simulation to run with Float32, which gives better performance on GPUs. Of course, you will need to determine whether this level of numerical accuracy is appropriate in your case.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"using Molly\nusing CUDA\n\nn_atoms = 100\natom_mass = 10.0f0u\"g/mol\"\nboundary = CubicBoundary(2.0f0u\"nm\")\ntemp = 100.0f0u\"K\"\natoms = CuArray([Atom(mass=atom_mass, σ=0.3f0u\"nm\", ϵ=0.2f0u\"kJ * mol^-1\") for i in 1:n_atoms])\ncoords = CuArray(place_atoms(n_atoms, boundary; min_dist=0.3u\"nm\"))\nvelocities = CuArray([random_velocity(atom_mass, temp) for i in 1:n_atoms])\nsimulator = VelocityVerlet(dt=0.002f0u\"ps\")\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=(LennardJones(),),\n loggers=(\n temp=TemperatureLogger(typeof(1.0f0u\"K\"), 10),\n coords=CoordinatesLogger(typeof(1.0f0u\"nm\"), 10),\n ),\n)\n\nsimulate!(deepcopy(sys), simulator, 20) # Compile function\nsimulate!(sys, simulator, 1_000)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The device to run on can be changed with device!, e.g. device!(1). The GPU code path is currently designed to be compatible with differentiable simulation and runs slower than related software, but this is an active area of development. Nonetheless, GPU performance is significantly better than CPU performance and is good enough for many applications.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The number of GPU threads used for the GPU kernels can be tuned with the environmental variables MOLLY_GPUNTHREADS_PAIRWISE, MOLLY_GPUNTHREADS_SPECIFIC, MOLLY_GPUNTHREADS_DISTANCENF and MOLLY_GPUNTHREADS_IMPLICIT. In general these should only be changed if GPU memory errors occur on smaller GPUs.","category":"page"},{"location":"documentation/#Simulating-diatomic-molecules","page":"Documentation","title":"Simulating diatomic molecules","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"If we want to define specific interactions between atoms, for example bonds, we can do this as well. Using the same definitions as the first example, let's set up the coordinates so that paired atoms are 1 Å apart.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"atoms = [Atom(mass=atom_mass, σ=0.3u\"nm\", ϵ=0.2u\"kJ * mol^-1\") for i in 1:n_atoms]\n\ncoords = place_atoms(n_atoms ÷ 2, boundary; min_dist=0.3u\"nm\")\nfor i in 1:length(coords)\n push!(coords, coords[i] .+ [0.1, 0.0, 0.0]u\"nm\")\nend\n\nvelocities = [random_velocity(atom_mass, temp) for i in 1:n_atoms]","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"We could have used place_diatomics instead here. Now we can use the built-in interaction list and bond types to place harmonic bonds between paired atoms.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"bonds = InteractionList2Atoms(\n collect(1:(n_atoms ÷ 2)), # First atom indices\n collect((1 + n_atoms ÷ 2):n_atoms), # Second atom indices\n [HarmonicBond(k=300_000.0u\"kJ * mol^-1 * nm^-2\", r0=0.1u\"nm\") for i in 1:(n_atoms ÷ 2)],\n)\n\nspecific_inter_lists = (bonds,)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"This time we are also going to use a neighbor list to speed up the Lennard-Jones calculation since we don't care about interactions beyond a certain distance. We can use the built-in DistanceNeighborFinder. The arguments are a 2D array of eligible interacting pairs, the number of steps between each update and the distance cutoff to be classed as a neighbor. Since the neighbor finder is run every 10 steps we should also use a distance cutoff for the neighbor list that is larger than the cutoff for the interaction.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"# All pairs apart from bonded pairs are eligible for non-bonded interactions\neligible = trues(n_atoms, n_atoms)\nfor i in 1:(n_atoms ÷ 2)\n eligible[i, i + (n_atoms ÷ 2)] = false\n eligible[i + (n_atoms ÷ 2), i] = false\nend\n\nneighbor_finder = DistanceNeighborFinder(\n eligible=eligible,\n n_steps=10,\n dist_cutoff=1.5u\"nm\",\n)\n\ncutoff = DistanceCutoff(1.2u\"nm\")\npairwise_inters = (LennardJones(use_neighbors=true, cutoff=cutoff),)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Now we can simulate as before.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"sys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=pairwise_inters,\n specific_inter_lists=specific_inter_lists,\n neighbor_finder=neighbor_finder,\n loggers=(\n temp=TemperatureLogger(10),\n coords=CoordinatesLogger(10),\n ),\n)\n\nsimulator = VelocityVerlet(\n dt=0.002u\"ps\",\n coupling=AndersenThermostat(temp, 1.0u\"ps\"),\n)\nsimulate!(sys, simulator, 1_000)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"This time when we view the trajectory we can add lines to show the bonds.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"visualize(\n sys.loggers.coords,\n boundary,\n \"sim_diatomic.mp4\";\n connections=[(i, i + (n_atoms ÷ 2)) for i in 1:(n_atoms ÷ 2)],\n)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"(Image: Diatomic simulation) The neighbors can be found using find_neighbors(sys), which returns a NeighborList. When using a neighbor finder, functions such as forces and potential_energy require the neighbors to be passed as a second argument, e.g. forces(sys, find_neighbors(sys)).","category":"page"},{"location":"documentation/#Simulating-gravity","page":"Documentation","title":"Simulating gravity","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly is geared primarily to molecular simulation, but can also be used to simulate other physical systems. Let's set up a gravitational simulation. This example also shows a 2D simulation and no specified units.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"atoms = [Atom(mass=1.0f0), Atom(mass=1.0f0)]\ncoords = [SVector(0.3f0, 0.5f0), SVector(0.7f0, 0.5f0)]\nvelocities = [SVector(0.0f0, 1.0f0), SVector(0.0f0, -1.0f0)]\npairwise_inters = (Gravity(use_neighbors=false, G=1.5f0),)\nsimulator = VelocityVerlet(dt=0.002f0)\nboundary = RectangularBoundary(1.0f0)\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=pairwise_inters,\n loggers=(coords=CoordinatesLogger(Float32, 10; dims=2),),\n force_units=NoUnits,\n energy_units=NoUnits,\n)\n\nsimulate!(sys, simulator, 2_000)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"When we view the simulation we can use some extra options:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"visualize(\n sys.loggers.coords,\n boundary,\n \"sim_gravity.mp4\";\n trails=4,\n framerate=15,\n color=[:orange, :lightgreen],\n)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"(Image: Gravity simulation)","category":"page"},{"location":"documentation/#Simulating-a-protein","page":"Documentation","title":"Simulating a protein","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The recommended way to run a macromolecular simulation is to read in a force field in OpenMM XML format to a MolecularForceField and then read in a coordinate file in a format supported by Chemfiles.jl. Files for common force fields can be found at OpenMM and OpenMM force fields. This sets up a system in the same data structures as above and that is simulated in the same way. Here we carry out an energy minimization, simulate with a Langevin integrator in the NPT ensemble and use a StructureWriter to write the trajectory as a PDB file.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"data_dir = joinpath(dirname(pathof(Molly)), \"..\", \"data\")\nff = MolecularForceField(\n joinpath(data_dir, \"force_fields\", \"ff99SBildn.xml\"),\n joinpath(data_dir, \"force_fields\", \"tip3p_standard.xml\"),\n joinpath(data_dir, \"force_fields\", \"his.xml\"),\n)\n\nsys = System(\n joinpath(data_dir, \"6mrr_equil.pdb\"),\n ff;\n loggers=(\n energy=TotalEnergyLogger(10),\n writer=StructureWriter(10, \"traj_6mrr_1ps.pdb\", [\"HOH\"]),\n ),\n gpu=false,\n)\n\nminimizer = SteepestDescentMinimizer()\nsimulate!(sys, minimizer)\n\ntemp = 298.0u\"K\"\nrandom_velocities!(sys, temp)\nsimulator = Langevin(\n dt=0.001u\"ps\",\n temperature=temp,\n friction=1.0u\"ps^-1\",\n coupling=MonteCarloBarostat(1.0u\"bar\", temp, sys.boundary),\n)\n\nsimulate!(sys, simulator, 5_000)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The OpenMM setup procedure is tested against OpenMM in terms of matching forces and energies. However it is not thoroughly tested with respect to ligands or special residues and requires that atom names exactly match residue templates. By default, terminal residues are renamed to match the appropriate templates. For example, the first (N-terminal) residue could be changed from \"MET\" to \"NMET\". This can be turned off by giving rename_terminal_res=false to System if the residue names in the input file are appropriate. Currently atom classes are not supported, only atom types. Residue patches, virtual sites, file includes and any force types other than HarmonicBondForce/HarmonicAngleForce/PeriodicTorsionForce/NonbondedForce are currently ignored.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"tip: Obtaining compatible structure files\nFuture work will increase the features and robustness when reading in structure files. In the mean time, the following tips may help you to read in a file correctly and without errors:Make sure there are no missing residues or heavy atoms. Tools such as MODELLER and SCWRL4 can fix these issues.\nRemove the hydrogen atoms and add them back using OpenMM, which will ensure they have atom names compatible with the OpenMM force field files.\nMake sure that all residue names match the corresponding residue template name and that all atom names match the appropriate atom in the residue template.\nNon-standard residues also require CONECT records for Chemfiles to assign bonds correctly, see for example a compatible alanine dipeptide file.Some PDB files that read in fine can be found here.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To run on the GPU, set gpu=true. You can use an implicit solvent method by giving the implicit_solvent keyword argument to System. The options are \"obc1\", \"obc2\" and \"gbn2\", corresponding to the Onufriev-Bashford-Case GBSA model with parameter set I or II and the GB-Neck2 model. Other options include overriding the boundary dimensions in the file (boundary) and modifying the non-bonded interaction and neighbor list cutoff distances (dist_cutoff and dist_neighbors).","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly also has a rudimentary parser of Gromacs topology and coordinate files. For example:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"sys = System(\n joinpath(dirname(pathof(Molly)), \"..\", \"data\", \"5XER\", \"gmx_coords.gro\"),\n joinpath(dirname(pathof(Molly)), \"..\", \"data\", \"5XER\", \"gmx_top_ff.top\");\n loggers=(\n temp=TemperatureLogger(10),\n writer=StructureWriter(10, \"traj_5XER_1ps.pdb\"),\n ),\n)\n\ntemp = 298.0u\"K\"\nrandom_velocities!(sys, temp)\nsimulator = Verlet(\n dt=0.0002u\"ps\",\n coupling=BerendsenThermostat(temp, 1.0u\"ps\"),\n)\n\nsimulate!(sys, simulator, 5_000)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Harmonic position restraints can be added to a System for equilibration using add_position_restraints:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"sys_res = add_position_restraints(\n sys,\n 100_000.0u\"kJ * mol^-1 * nm^-2\";\n atom_selector=is_heavy_atom,\n)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The Gromacs setup procedure should be considered experimental. Currently Ewald summation methods, constraint algorithms and high GPU performance are missing from the package, so Molly is not suitable for production simulations of biomolecules. Stay tuned for developments in this area.","category":"page"},{"location":"documentation/#Enhanced-sampling","page":"Documentation","title":"Enhanced sampling","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly has the ReplicaSystem struct and simulators such as TemperatureREMD to carry out replica exchange molecular dynamics (REMD). On CPU these are run in parallel by dividing up the number of available threads. For example, to run temperature REMD on a protein with 4 replicas and attempt exchanges every 1 ps:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"using Statistics\n\ndata_dir = joinpath(dirname(pathof(Molly)), \"..\", \"data\")\nff = MolecularForceField(\n joinpath(data_dir, \"force_fields\", \"ff99SBildn.xml\"),\n joinpath(data_dir, \"force_fields\", \"tip3p_standard.xml\"),\n joinpath(data_dir, \"force_fields\", \"his.xml\"),\n)\n\nsys = System(joinpath(data_dir, \"6mrr_equil.pdb\"), ff)\n\nminimizer = SteepestDescentMinimizer()\nsimulate!(sys, minimizer)\n\nn_replicas = 4\n\nrep_sys = ReplicaSystem(\n atoms=sys.atoms,\n replica_coords=[copy(sys.coords) for _ in 1:n_replicas],\n boundary=sys.boundary,\n n_replicas=n_replicas,\n atoms_data=sys.atoms_data,\n pairwise_inters=sys.pairwise_inters,\n specific_inter_lists=sys.specific_inter_lists,\n general_inters=sys.general_inters,\n neighbor_finder=sys.neighbor_finder,\n replica_loggers=[(temp=TemperatureLogger(10),) for _ in 1:n_replicas],\n)\n\ntemps = [240.0u\"K\", 280.0u\"K\", 320.0u\"K\", 360.0u\"K\"]\ndt = 0.0005u\"ps\"\nsimulators = [Langevin(dt=dt, temperature=temp, friction=1.0u\"ps^-1\") for temp in temps]\n\nsim = TemperatureREMD(\n dt=dt,\n temperatures=temps,\n simulators=simulators,\n exchange_time=1.0u\"ps\",\n)\n\nsimulate!(rep_sys, sim, 40_000; assign_velocities=true)\n\nprintln(rep_sys.exchange_logger.n_attempts)\n# 30\n\nfor i in 1:n_replicas\n final_temps = values(rep_sys.replicas[i].loggers.temp)[(end - 10):end]\n println(mean(final_temps))\nend\n# 240.1691457033836 K\n# 281.3783250460198 K\n# 320.44985840482974 K\n# 357.710520769689 K","category":"page"},{"location":"documentation/#Monte-Carlo-sampling","page":"Documentation","title":"Monte Carlo sampling","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly has the MetropolisMonteCarlo simulator to carry out Monte Carlo sampling with Metropolis selection rates. For example, to perform simulated annealing on charged particles to form a crystal lattice:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"n_atoms = 100\natoms = [Atom(mass=10.0u\"g/mol\", charge=1.0) for i in 1:n_atoms]\nboundary = RectangularBoundary(4.0u\"nm\")\n\ncoords = place_atoms(n_atoms, boundary; min_dist=0.2u\"nm\")\npairwise_inters = (Coulomb(),)\n\ntemperatures = [1198.0, 798.0, 398.0, 198.0, 98.0, 8.0]u\"K\"\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n pairwise_inters=pairwise_inters,\n loggers=(\n coords=CoordinatesLogger(n_atoms, dims=2),\n montecarlo=MonteCarloLogger(),\n ),\n)\n\ntrial_args = Dict(:shift_size => 0.1u\"nm\")\nfor t in temperatures\n sim = MetropolisMonteCarlo(; \n temperature=t,\n trial_moves=random_uniform_translation!,\n trial_args=trial_args,\n )\n\n simulate!(sys, sim, 10_000)\nend\n\nprintln(sys.loggers.montecarlo.n_accept)\n# 15234\n\nvisualize(sys.loggers.coords, boundary, \"sim_montecarlo.gif\")","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"(Image: Monte Carlo simulation)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"trial_moves should be a function that takes a System as its argument and optional keyword arguments trial_args. It should modify the coordinates as appropriate, accounting for any boundary conditions. random_uniform_translation! and random_normal_translation! are provided as common trial move functions. MonteCarloLogger records various properties throughout the simulation.","category":"page"},{"location":"documentation/#Units","page":"Documentation","title":"Units","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly is fairly opinionated about using Unitful.jl units as shown above: you don't have to use them, but it is better if you do. Any consistent unit scheme can be used, or no units at all. Molly is most strict about the mixture of molar and non-molar types. For example, if your energy and force units are molar then your atom masses should be g/mol or similar. If you are not using units then no quantities can have Unitful annotations and you are responsible for ensuring a consistent unit system. Whilst you occasionally may run into friction with dimension mismatches, using units has the major advantages of catching whole classes of errors and letting you physically interpret the numbers in your system. The performance overhead of using units is minimal. Units are not currently compatible with differentiable simulations.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"All your interaction types need to return the same units of force and energy or the simulation will not run. By default these are kJ * mol^-1 * nm^-1 for force and kJ * mol^-1 for energy, but this can be changed using the force_units and energy_units arguments to System and some interactions. These arguments should be NoUnits if you are not using units. If you need to strip units for downstream analysis, use the ustrip or ustrip_vec functions. It should be noted that charges are stored as dimensionless, i.e. 1.0 represents an atomic charge of +1.","category":"page"},{"location":"documentation/#Atom-types","page":"Documentation","title":"Atom types","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly has a built-in Atom type with a few properties commonly used in molecular simulation defined. The mass and charge functions can be used on an Atom. Custom atom types can be used just as effectively provided that either the mass function is defined on the type or the type has a mass field (the fallback for the mass function). The type should also have all fields required by any interactions. The list of atoms passed to the System constructor should be concretely typed.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Custom atom types should generally be bits types, i.e. isbitstype(MyAtom) should be true, to work on the GPU. Additional non-bits type data for the atoms that is not directly used when calculating the interactions can be passed to the System constructor with the atoms_data keyword argument. For example the built-in AtomData type contains fields such as the atom name that are useful when writing trajectories.","category":"page"},{"location":"documentation/#Forces-and-energies","page":"Documentation","title":"Forces and energies","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Interactions define how different parts of the system interact. The force on each particle in the system is derived from the potential corresponding to the interaction.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"vecF_i = -sum_j fracdV_ij(r_ij)dr_ijfracvecr_ijr_ij","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"In Molly there are three types of interactions:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Pairwise interactions are present between all or most atom pairs, and account for example for non-bonded terms in molecular mechanics force fields.\nSpecific interactions are present between specific atoms, and account for example for bonded terms in molecular mechanics force fields.\nGeneral interactions are a free-form interaction type that can access the whole system and outputs forces for all atoms. This is useful for neural network potentials, implicit solvent models and other cases that require maximum flexibility. General interactions should be compatible with the AtomsCalculators.jl interface.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The available pairwise interactions are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"LennardJones\nLennardJonesSoftCore\nSoftSphere\nMie\nBuckingham\nCoulomb\nCoulombSoftCore\nCoulombReactionField\nGravity","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The available specific interactions are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"HarmonicPositionRestraint - 1 atom\nHarmonicBond - 2 atoms\nMorseBond - 2 atoms\nFENEBond - 2 atoms\nHarmonicAngle - 3 atoms\nCosineAngle - 3 atoms\nPeriodicTorsion - 4 atoms\nRBTorsion - 4 atoms","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The available general interactions are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"ImplicitSolventOBC\nImplicitSolventGBN2\nMullerBrown\nASECalculator","category":"page"},{"location":"documentation/#Pairwise-interactions","page":"Documentation","title":"Pairwise interactions","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To define your own pairwise interaction, first define the struct:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"struct MyPairwiseInter\n # Any properties, e.g. constants for the interaction or cutoff parameters\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"You can also define a use_neighbors method, which determines whether the neighbor list is used to omit distant atoms (true) or whether all atom pairs are always considered (false):","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly.use_neighbors(inter::MyPairwiseInter) = true","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"This is false by default. If it is true, you must specify a neighbor finder when setting up the System. For built-in interactions this function accesses the use_neighbors field of the struct. To work on the GPU the struct should be a bits type, i.e. isbitstype(MyPairwiseInter) should be true.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Next, you need to define a method for the force function acting between a pair of atoms. This has a set series of arguments:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function Molly.force(inter::MyPairwiseInter,\n vec_ij,\n atom_i,\n atom_j,\n force_units,\n special,\n coord_i,\n coord_j,\n boundary,\n velocity_i,\n velocity_j,\n step_n)\n # Replace this with your force calculation\n # A positive force causes the atoms to move apart\n f = 0.0\n\n # Obtain a vector for the force\n fdr = f * normalize(vec_ij)\n return fdr\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Most of the arguments will generally not be used but are passed to allow maximum flexibility. You can use args... to indicate unused further arguments, e.g. Molly.force(inter::MyPairwiseInter, vec_ij, args...). vec_ij is the vector between the closest images of atoms i and j accounting for the periodic boundary conditions. Atom properties can be accessed, e.g. atom_i.σ. force_units can be useful for returning a zero force under certain conditions. step_n is the step number in the simulator, allowing time-dependent interactions. Beware that this step counter starts from 1 every time simulate! is called. It also doesn't work with simulate_remd!. Typically the force function is where most computation time is spent during the simulation, so consider optimising this function if you want high performance. One nice feature of Molly is that this function will work on both the CPU and the GPU.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The argument special is a Bool determining whether the atom pair interaction should be treated as special. This is specified during neighbor finder construction. When simulating molecules, for example, non-bonded interactions for atoms in a 1-4 bonding arrangement (i-x-x-j) are often weighted by a factor such as 0.5. For interactions where this is relevant, special can be used to apply this weighting in the interaction. It can have a variety of uses depending on the context, for example if you have multiple interactions and want to exclude certain atom pairs from one of the interactions only.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To use your custom interaction in a simulation, add it to the list of pairwise interactions:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"pairwise_inters = (MyPairwiseInter(),)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Then create a System and simulate as above. Note that you can also use a named tuple instead of a tuple if you want to access interactions by name:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"pairwise_inters = (MyPairwiseInter=MyPairwiseInter(),)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"For performance reasons it is best to avoid containers with abstract type parameters, such as Vector{Any}.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"If you wish to calculate potential energies or log the energy throughout a simulation, you will need to define a method for the potential_energy function. This has the same arguments as force, except the fifth argument is the energy units not the force units, and should return a single value corresponding to the potential energy:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function Molly.potential_energy(inter::MyPairwiseInter,\n vec_ij,\n atom_i,\n atom_j,\n energy_units,\n special,\n coord_i,\n coord_j,\n boundary,\n velocity_i,\n velocity_j,\n step_n)\n # Example Lennard-Jones interaction\n σ = (atom_i.σ + atom_j.σ) / 2\n ϵ = sqrt(atom_i.ϵ * atom_j.ϵ)\n r = norm(vec_ij)\n E = 4ϵ * ((σ/r)^6 - (σ/r)^12)\n return E\nend","category":"page"},{"location":"documentation/#Specific-interactions","page":"Documentation","title":"Specific interactions","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To define your own specific interaction, first define the struct:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"struct MySpecificInter\n # Properties, e.g. a bond distance corresponding to the energy minimum\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Next, you need to define a method for the force function. The form of this will depend on whether the interaction involves 1, 2, 3 or 4 atoms. For example in the 2 atom case:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function Molly.force(inter::MySpecificInter,\n coord_i,\n coord_j,\n boundary,\n atom_i,\n atom_j,\n force_units,\n velocity_i,\n velocity_j)\n dr = vector(coords_i, coords_j, boundary)\n\n # Replace this with your force calculation\n # A positive force causes the atoms to move apart\n f = 0.0\n\n fdr = f * normalize(dr)\n return SpecificForce2Atoms(-fdr, fdr)\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Again, most of these arguments are rarely used and can be replaced with args.... The 3 atom case would define Molly.force(inter::MySpecificInter, coord_i, coord_j, coord_k, boundary, atom_i, atom_j, atom_k, force_units, velocity_i, velocity_j, velocity_k) and return SpecificForce3Atoms(f1, f2, f3). To use your custom interaction, add it to the specific interaction lists along with the atom indices:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"specific_inter_lists = (\n InteractionList2Atoms(\n [1, 3],\n [2, 4],\n [MySpecificInter(), MySpecificInter()],\n ),\n)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"For 3 atom interactions use InteractionList3Atoms and pass 3 sets of indices. If using the GPU, the inner list of indices and interactions should be moved to the GPU with CuArray. The number in the interaction list and the return type from force must match, e.g. InteractionList3Atoms must always return SpecificForce3Atoms from the corresponding force function. If some atoms are required in the interaction for force calculation but have no force applied to them by the interaction, give a zero force vector for those atoms. Again a method for potential_energy with the same arguments, except the seventh argument is the energy units not the force units, can be defined:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function Molly.potential_energy(inter::MySpecificInter,\n coord_i,\n coord_j,\n boundary,\n atom_i,\n atom_j,\n energy_units,\n velocity_i,\n velocity_j)\n # Example harmonic bond interaction\n dr = vector(coord_i, coord_j, boundary)\n r = norm(dr)\n return (inter.k / 2) * (r - inter.r0) ^ 2\nend","category":"page"},{"location":"documentation/#General-interactions","page":"Documentation","title":"General interactions","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To define your own general interaction, first define the struct:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"struct MyGeneralInter\n # Properties, e.g. a neural network model\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Next, you need to define a method for the AtomsCalculators.forces function (note this is different to the force function above).","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"import AtomsCalculators\n\nfunction AtomsCalculators.forces(sys,\n inter::MyGeneralInter;\n neighbors=nothing,\n step_n=0,\n n_threads=Threads.nthreads(),\n kwargs...)\n # kwargs... is required, neighbors/step_n/n_threads can be omitted if not used\n\n # Calculate the forces on all atoms using the interaction and the system\n # The output should have the same shape as the coordinates\n # For example, a neural network might do something like this\n return inter.model(sys.coords, sys.atoms)\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The neighbors calculated from the neighbor list are available in this function, but may or may not be used depending on context. You could carry out your own neighbor finding in this function if required. Note that this function calculates forces not accelerations; if you have a neural network that calculates accelerations you should multiply these by masses(sys) to get the forces according to F=ma.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"A method for the AtomsCalculators.potential_energy function that takes the same arguments and returns a single value can also be defined. A method for the virial function can also be defined, allowing virial and pressure calculation when using custom general interactions. To use your custom interaction in a simulation, add it to the list of general interactions:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"general_inters = (MyGeneralInter(),)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"general_inters=general_inters can be given as a keyword argument when setting up the System.","category":"page"},{"location":"documentation/#Cutoffs","page":"Documentation","title":"Cutoffs","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The total potential energy of a system is given as a sum of the individual inter-particle potentials","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"V(vecr_1 dotsc vecr_N) = sum_ijV_ij(r_ij)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The forces acting on the particles are given by","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"vecF_i = -sum_j fracdV_ij(r_ij)dr_ijfracvecr_ijr_ij","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"In the case of the Lennard-Jones potential, the inter-particle potential is given by","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"V_ij(r_ij) = 4varepsilon_ij leftleft(fracsigma_ijr_ijright)^12 - left(fracsigma_ijr_ijright)^6right","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"and the forces are given by","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"beginaligned\nvecF_i = 24varepsilon_ij left(2fracsigma_ij^12r_ij^13 - fracsigma_ij^6r_ij^7right) fracvecr_ijr_ij \n= frac24varepsilon_ijr_ij^2 left2left(fracsigma_ij^6r_ij^6right)^2 -left(fracsigma_ijr_ijright)^6right vecr_ij\nendaligned","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"As the potential, and thus also the force decreases rapidly with the distance, in almost every implementation of the Lennard-Jones force calculation there is a cutoff radius beyond which the force is set to 0.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"While this is often a sensible approach, it introduces a discontinuity in the force function and it requires us to also modify the potential, as beyond the cutoff radius the force would be 0, but the derivative of the unmodified potential is not. One way to truncate the potential is to shift the potential by its cutoff value.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"beginaligned\nvecF_SP(vecr) = begincases\nvecF(vecr) r r_c \n0 r r_c\nendcases \nV_SP(r) = begincases\nV(r) - V(r_c) r le r_c \n0 r r_c\nendcases\nendaligned","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"This way the potential function is continuous and the relation between forces and potentials is satisfied. This truncation method is called shifted potential cutoff.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Another option is to shift the force in order to make it continuous","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"beginaligned\nF_SF(r) = begincases\nF(r) - F(r_c) r le r_c \n0 r r_c\nendcases \nV_SF(r) = begincases\nV(r) - (r-r_c) V(r_c) - V(r_c) r le r_c \n0 r r_c\nendcases\nendaligned","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"This requires a more complicated change in the potential in order to satisfy the relation between them. This method is called the shifted force cutoff. The continuity of the force is desirable as it may give better energy conservation properties as shown in Toxvaerd 2011.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"There are also more complicated truncation methods that interpolate between the original potential and 0, but we will consider those two for the moment. The truncation approximations that we use can significantly alter the qualitative features of the simulation as shown in many articles in the molecular dynamics literature (Fitzner 2017, van der Spoel 2006 and others).","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Since the truncation algorithm is independent of the interaction for which is used, each compatible interaction is defined without including cutoffs. The corresponding interaction constructor has a cutoff field (default NoCutoff) which is then used via dispatch to apply the chosen cutoff, e.g. SoftSphere(cutoff=ShiftedPotentialCutoff(1.2u\"nm\")). The available cutoffs are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"NoCutoff\nDistanceCutoff\nShiftedPotentialCutoff\nShiftedForceCutoff\nCubicSplineCutoff","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The following interactions can use a cutoff:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"LennardJones\nLennardJonesSoftCore\nSoftSphere\nMie\nBuckingham\nCoulomb\nCoulombSoftCore","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"In addition, CoulombReactionField and the implicit solvent models have a dist_cutoff argument for a cutoff distance.","category":"page"},{"location":"documentation/#Boundaries","page":"Documentation","title":"Boundaries","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly allows the use of various periodic and infinite boundary conditions. The available 3D boundaries are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"CubicBoundary\nTriclinicBoundary","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The available 2D boundaries are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"RectangularBoundary","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Some examples of using boundaries:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"CubicBoundary(2.0u\"nm\" ) # Periodic cube with 2 nm sides\nCubicBoundary(2.0u\"nm\" , 2.0u\"nm\" , 2.0u\"nm\" ) # Periodic cube with 2 nm sides\nCubicBoundary(4.0u\"nm\" , 5.0u\"nm\" , 6.0u\"nm\" ) # Periodic cuboid\nCubicBoundary(2.0u\"nm\" , 2.0u\"nm\" , Inf * u\"nm\") # Infinite boundary in z direction\nCubicBoundary(Inf * u\"nm\", Inf * u\"nm\", Inf * u\"nm\") # Infinite boundary, no periodicity\nCubicBoundary(Inf * u\"nm\" ) # Infinite boundary, no periodicity\n\nRectangularBoundary(2.0u\"nm\" ) # Periodic square\nRectangularBoundary(4.0u\"nm\", 5.0u\"nm\" ) # Periodic rectangle\nRectangularBoundary(2.0u\"nm\", Inf * u\"nm\") # Infinite boundary in y direction\n\n# Periodic triclinic from basis vectors\nTriclinicBoundary(SVector(\n SVector(2.2 , 0.0 , 0.0 )u\"nm\",\n SVector(1.0 , 1.7320508, 0.0 )u\"nm\",\n SVector(1.37888 , 0.5399122, 1.0233204)u\"nm\",\n))\n\n# Periodic triclinic from basis vector lengths and angles α/β/γ\nb = TriclinicBoundary(\n SVector(2.2, 2.0, 1.8)u\"nm\",\n deg2rad.(SVector(50.0, 40.0, 60.0)),\n)\n\n# Volume of bounding box\nvolume(b) # 3.8993746318188633 nm^3\n\n# Random coordinate uniformly distributed within boundary\nrandom_coord(b) # SVector(2.651062310435411, 2.1702306804433973, 0.9518105403051831)u\"nm\"\n\n# Wrap a coordinate back into the boundary if it is outside\nwrap_coords(SVector(1.0, 1.0, 1.0)u\"nm\", b) # SVector(3.2, 1.0, 1.0)u\"nm\"","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The box_center, AtomsBase.n_dimensions, float_type, place_atoms and place_diatomics functions are also available for boundaries.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The appropriate boundary to use will depend on your simulation. For example, having different lengths in each dimension would usually only make sense in a situation where forces or restraints depended on the dimension.","category":"page"},{"location":"documentation/#Simulators","page":"Documentation","title":"Simulators","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Simulators define what type of simulation is run. This could be anything from a simple energy minimization to complicated replica exchange MD. The available simulators are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"SteepestDescentMinimizer\nVelocityVerlet\nVerlet\nStormerVerlet\nLangevin\nLangevinSplitting\nOverdampedLangevin\nNoseHoover\nTemperatureREMD\nHamiltonianREMD\nMetropolisMonteCarlo","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Many of these require a time step dt as an argument. Many also remove the center of mass motion every time step, which can be tuned with the remove_CM_motion argument.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The LangevinSplitting simulator can be used to define a variety of integrators such as velocity Verlet (splitting \"BAB\"), the Langevin implementation in Langevin (\"BAOA\"), and symplectic Euler integrators (\"AB\" and \"BA\").","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To define your own simulator, first define a struct:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"struct MySimulator\n # Any properties, e.g. the time step or coupling methods\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Then, define a method for simulate! that carries out the simulation. This example shows some helper functions that you can use:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function Molly.simulate!(sys,\n sim::MySimulator,\n n_steps::Integer;\n n_threads::Integer=Threads.nthreads(),\n run_loggers=true)\n # Find neighbors like this\n neighbors = find_neighbors(sys, sys.neighbor_finder; n_threads=n_threads)\n\n for step_n in 1:n_steps\n # Calculate accelerations like this\n accels_t = accelerations(sys, neighbors, step_n; n_threads=n_threads)\n\n # Ensure coordinates stay within the simulation box like this\n sys.coords = wrap_coords.(sys.coords, (sys.boundary,))\n\n # Example velocity update\n # Includes appropriate unit conversion for when the force units are per mol\n sys.velocities += (accels_t .+ accels_t_dt) .* sim.dt / 2\n\n # Apply coupling like this\n recompute_forces = apply_coupling!(sys, sim.coupling, sim, neighbors, step_n;\n n_threads=n_threads)\n\n # Remove center of mass motion like this\n remove_CM_motion!(sys)\n\n # Apply the loggers like this\n # Computed quantities can also be given as keyword arguments to apply_loggers!\n apply_loggers!(sys, neighbors, step_n, run_loggers; n_threads=n_threads)\n\n # Find new neighbors like this\n neighbors = find_neighbors(sys, sys.neighbor_finder, neighbors, step_n, recompute_forces;\n n_threads=n_threads)\n end\n\n return sys\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"See more in the source code, for example how to apply constraints to coordinates and velocities. To use your custom simulator, give it as the second argument when calling simulate!.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To define your own replica exchange simulator, first define a struct:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"struct MyREMDSimulator\n dt # Time step\n exchange_time # Time between exchanges\n simulators # A list of simulators to use for each replica e.g. Langevin\n # Other properties of the simulation\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Then define the function that carries out the exchange, remd_exchange!:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function Molly.remd_exchange!(sys::ReplicaSystem,\n sim::MyREMDSimulator,\n n::Integer,\n m::Integer;\n n_threads::Integer=Threads.nthreads(),\n rng=Random.GLOBAL_RNG)\n # Attempt to exchange the replicas with index n and m\n # First define Δ for the REMD scheme\n make_exchange = Δ <= 0 || rand(rng) < exp(-Δ) # Example of Metropolis acceptance rate\n if make_exchange\n # Exchange coordinates and velocities of replicas\n # Also scale the velocities to match the temperature if required\n end\n\n return Δ, make_exchange\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The above function returns Δ, the argument of the acceptance rate that is logged by ReplicaExchangeLogger, and a boolean indicating whether the exchange was successful.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Then, define a method for the simulate! function to perform the parallel simulation. This does any initial setup such as assigning velocities then uses simulate_remd! to run the simulation:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function Molly.simulate!(sys::ReplicaSystem,\n sim::MyREMDSimulator,\n n_steps::Integer;\n rng=Random.GLOBAL_RNG,\n n_threads::Integer=Threads.nthreads())\n # Do any initial setup if necessary\n simulate_remd!(sys, sim, n_steps; rng=rng, n_threads=n_threads)\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Under the hood there are two implementations for the forces function, used by accelerations, and for potential_energy: a version geared towards CPUs and parallelism, and a version geared towards GPUs. You can define different versions of a simulator for CPU and GPU systems by dispatching on System{D, false} or System{D, true} respectively. This also applies to coupling methods, neighbor finders and analysis functions. You do not have to define two versions though: you may only intend to use the simulator one way, or one version may be performant in all cases.","category":"page"},{"location":"documentation/#Coupling","page":"Documentation","title":"Coupling","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Some simulators can be modified by adding coupling methods to allow properties like temperature and pressure to be controlled during a simulation. The available couplers are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"AndersenThermostat\nRescaleThermostat\nBerendsenThermostat\nMonteCarloBarostat\nMonteCarloAnisotropicBarostat\nMonteCarloMembraneBarostat","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Currently the VelocityVerlet, Verlet, StormerVerlet, Langevin and NoseHoover simulators support coupling methods, with the default being NoCoupling. Couplers are given to the coupling keyword argument during simulator construction:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"temp = 300.0u\"K\"\npress = 1.0u\"bar\"\nthermostat = AndersenThermostat(temp, 1.0u\"ps\")\nbarostat = MonteCarloBarostat(press, temp, sys.boundary)\n\n# Velocity Verlet with Andersen thermostat\nVelocityVerlet(dt=0.001u\"ps\", coupling=thermostat)\n\n# Velocity Verlet with Andersen thermostat and Monte Carlo barostat\nVelocityVerlet(dt=0.001u\"ps\", coupling=(thermostat, barostat))","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The appropriate coupling to use will depend on the situation. For example, the MonteCarloBarostat for controlling pressure assumes a constant temperature but does not actively control the temperature. It should be used alongside a temperature coupling method such as the Langevin simulator or AndersenThermostat coupling.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To define your own coupling method, first define the struct:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"struct MyCoupler\n # Any properties, e.g. a target temperature or coupling constant\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Then, define the function that implements the coupling every time step:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function Molly.apply_coupling!(sys, coupling::MyCoupler, sim, neighbors, step_n;\n n_threads=Threads.nthreads())\n # Do something to the simulation, e.g. scale the velocities\n # Return whether the coupling has invalidated the currently stored forces,\n # for example by changing the coordinates\n recompute_forces = false\n return recompute_forces\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The functions random_velocity, maxwell_boltzmann and temperature may be useful here. To use your custom coupler, give it as the coupling argument to the simulator as above.","category":"page"},{"location":"documentation/#Loggers","page":"Documentation","title":"Loggers","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Loggers record properties of the simulation to allow monitoring and analysis. The available loggers are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"GeneralObservableLogger\nTemperatureLogger\nCoordinatesLogger\nVelocitiesLogger\nTotalEnergyLogger\nKineticEnergyLogger\nPotentialEnergyLogger\nForcesLogger\nVolumeLogger\nDensityLogger\nVirialLogger\nPressureLogger\nStructureWriter\nTimeCorrelationLogger\nAutoCorrelationLogger\nAverageObservableLogger\nReplicaExchangeLogger\nMonteCarloLogger","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Many of the loggers can be initialised with just the number of steps between recorded values, e.g. CoordinatesLogger(10). An optional first argument is the type of the recorded value; the above is equivalent to CoordinatesLogger(typeof(1.0u\"nm\"), 10) but if the simulation did not use units then CoordinatesLogger(Float64, 10) would be required. If the simulation is in 2D, giving dims=2 as a keyword argument is required for some loggers. A logger's history can be accessed with values(my_logger).","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To define your own logger, first define the struct and a method for values to access the stored values:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"struct MyLogger\n n_steps::Int\n history::Vector{Float64}\n # Any other properties\nend\n\nBase.values(logger::MyLogger) = logger.history","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Then, define the logging function that is called every step by the simulator:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function Molly.log_property!(logger::MyLogger,\n sys,\n neighbors,\n step_n;\n n_threads=Threads.nthreads(),\n kwargs...)\n if step_n % logger.n_steps == 0\n # Record some property or carry out some action\n end\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The use of n_steps is optional and is an example of how to record a property periodically throughout the simulation. To use your custom logger, add it to the named tuple of loggers given when creating the System:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"loggers = (mylogger=MyLogger(10, []),) # Don't forget the trailing comma!","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"In addition to being run at the end of each step, loggers are run before the first step, i.e. at step 0. This means that a logger that records a value every step for a simulation with 100 steps will end up with 101 values. Running loggers before the first step can be disabled by giving run_loggers=:skipzero as a keyword argument to simulate!, which can be useful when splitting up simulations into multiple simulate! calls. For example, this runs the loggers 301 times:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"simulate!(sys, simulator, 100) # Default run_loggers=true\nsimulate!(sys, simulator, 100; run_loggers=:skipzero)\nsimulate!(sys, simulator, 100; run_loggers=:skipzero)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Running loggers can be disabled entirely with run_loggers=false, which is the default for SteepestDescentMinimizer. Loggers are currently ignored for the purposes of taking gradients, so if a logger is used in the gradient calculation the gradients will appear to be nothing.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Many times, a logger will just record an observation to an Array containing a record of past observations. For this purpose, you can use the GeneralObservableLogger without defining a custom logging function. Define your observation function as","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function my_observable(sys::System, neighbors, step_n; n_threads::Integer, kwargs...)\n # Probe the system for some desired property\n return observation\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Keyword arguments current_forces and current_potential_energy can also be used here to avoid recomputing values that are passed from the simulator:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function my_pe_observable(sys::System, neighbors; n_threads::Integer,\n current_potential_energy=nothing, kwargs...)\n if isnothing(current_potential_energy)\n # Compute potential energy\n return potential_energy(sys, neighbors; n_threads=n_threads)\n else\n # Potential energy was passed from simulator, reuse\n return current_potential_energy\n end\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"These keyword arguments are also available in log_property!. Which values are passed depends on the simulator being used, for example SteepestDescentMinimizer passes current_potential_energy because it uses it for minimization. Note that loggers are called after apply_coupling!, so the coordinates may have changed since the potential energy or forces were computed.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"A logger which records the property every n_steps can be constructed through ","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"my_logger = GeneralObservableLogger(my_observable, T, n_steps)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"where T = typeof(observation) is the type of the return value for my_observable. AverageObservableLogger is similar but records a running average rather than storing observations.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"The TimeCorrelationLogger logger can be used to compute correlation functions of the form","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"C(t) = fraclangle A_t cdot B_0 ranglesqrtlangle A^2 rangle langle B^2 rangle","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"where A and B are scalar or vector centered observables and the brackets are ensemble averages. This includes the computations of autocorrelation functions, which can be used to gather insight into the dynamical properties of the system, for instance using Green-Kubo formulas, or the statistical properties of a sampling method.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Let's look at a simple example, computing the velocity autocorrelation function for a simple system consisting of diatomic molecules defined by HarmonicBond potentials between pairs of atoms, and an additional SoftSphere potential between all pairs of atoms. Let's start by defining the system.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"n_atoms = 400\natom_mass = 10.0u\"g/mol\"\natoms = [Atom(mass=atom_mass, σ=0.2u\"nm\", ϵ=0.2u\"kJ * mol^-1\") for i in 1:n_atoms]\n\n# Initialization\nboundary = CubicBoundary(6.0u\"nm\")\ncoords = place_diatomics(n_atoms ÷ 2, boundary, 0.2u\"nm\"; min_dist=0.2u\"nm\")\n\ntemp = 50.0u\"K\"\nvelocities = [random_velocity(atom_mass, temp) .* 0.01 for i in 1:n_atoms]\n\n# Interaction potentials\npairwise_inters = (SoftSphere(use_neighbors=true, cutoff=DistanceCutoff(0.6u\"nm\")),)\n\nbonds = [HarmonicBond(k=10000u\"kJ * mol^-1 * nm^-2\", r0=0.2u\"nm\") for i in 1:(n_atoms ÷ 2)]\nspecific_inter_lists = (InteractionList2Atoms(\n collect(1:2:n_atoms),\n collect(2:2:n_atoms),\n bonds,\n),)\n\n# Define system\nnf = DistanceNeighborFinder(eligible=trues(n_atoms, n_atoms), dist_cutoff=0.6u\"nm\")\n\nsys = System(\n atoms=atoms,\n coords=coords,\n boundary=boundary,\n velocities=velocities,\n pairwise_inters=pairwise_inters,\n specific_inter_lists=specific_inter_lists,\n neighbor_finder=nf,\n)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"We leave the loggers empty until we thermalize the system using Langevin dynamics.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"dt = 0.002u\"ps\"\nsimulator = LangevinSplitting(\n dt=dt,\n temperature=temp,\n friction=10.0u\"g * mol^-1 * ps^-1\",\n splitting=\"BAOAB\",\n)\nsimulate!(sys, simulator, 10_000)\n@show temperature(sys)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"temperature(sys) = 48.76795299825687 K","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Good. Next we define our correlation logger, add it to the system's loggers and run a longer simulation. Note that we need to redeclare the system when adding a logger.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"# Velocity observable\n# args and kwargs because more complex observables may require neighbors and parallelism\nV(s::System, args...; kwargs...) = s.velocities\nV_Type = eltype(sys.velocities)\nlogger = TimeCorrelationLogger(V, V, V_Type, V_Type, n_atoms, 1_000)\n\nsys = System(\n atoms=atoms,\n coords=sys.coords,\n boundary=boundary,\n velocities=sys.velocities,\n pairwise_inters=pairwise_inters,\n specific_inter_lists=specific_inter_lists,\n neighbor_finder=nf,\n loggers=(velocity_autocorrelation=logger,)\n)\nsimulate!(sys, simulator, 100_000)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Check the output:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"show(sys.loggers)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"(velocity_autocorrelation = AutoCorrelationLogger with n_correlation 1000 and 100001 samples collected for observable V,)","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Note we also could have used the convenience function AutoCorrelationLogger to define our logger since the two observables we are correlating are the same.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"using GLMakie\nf = Figure()\nax = Axis(f[1, 1], xlabel=\"Time / ps\", ylabel=\"Correlation\")\nlines!((1:1000) .* ustrip(dt), values(sys.loggers.velocity_autocorrelation))","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"(Image: Velocity Autocorrelations)\nAs expected, the velocities are highly correlated at small time offsets and the correlation decays rapidly. The oscillatory behavior is due to the harmonic bond interactions.","category":"page"},{"location":"documentation/#Constraints","page":"Documentation","title":"Constraints","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly implements SHAKE and its extension, RATTLE, to perform constrained molecular dynamics (see SHAKE_RATTLE). These methods are useful for maintaining bond lengths and angles during a simulation, often allowing the use of longer time steps and therefore more efficient use of computing resources. The constraints satisfied by SHAKE are solely on the atomic coordinates:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"beginaligned\nvecr_ij cdot vecr_ij = d^2_ij\nendaligned","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"whereas RATTLE also constrains the velocities:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"beginaligned\nvecr_ij cdot vecv_ij = 0\nendaligned","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Here vecr_ij is the vector between atoms i and j in a constraint, d_ij is the bond length to be maintained and vecv_ij is the difference in the velocity vectors for atoms i and j. SHAKE was originally derived for the Verlet integration scheme (Ryckaert et al. 1977) with RATTLE extending SHAKE to work for velocity Verlet where the velocities are also integrated (Andersen 1983).","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Currently, constraints are supported by the following simulators:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"SteepestDescentMinimizer\nVelocityVerlet\nVerlet\nStormerVerlet\nLangevin","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Simulators incompatible with constraints will print a warning and continue when used with systems containing constraints. Constraints are not currently compatible with GPU simulation.","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"In Molly, the SHAKE constraints for diatomic molecules are solved analytically while all larger constraints are solved iteratively. The velocity constraints imposed by RATTLE form a linear system of equations which could be solved exactly; however, this operation is expensive for clusters of more than 4 constraints. Therefore, RATTLE constraints can be solved by direct matrix inversion for small clusters (4 or fewer constraints) and iteratively otherwise (currently only solved iteratively). The number of constraints here does not refer to the total number of constraints in the system, rather to the total number of constraints in an independent cluster/molecule. For example, a water molecule can be constrained by 2 distance constraints and 1 angle constraint which is only 3 constraints. However, a C-C backbone of an organic molecule like octane would need 7 constraints to maintain all the C-C bond lengths. Constraining large clusters will result in a performance penalty.","category":"page"},{"location":"documentation/#Neighbor-finders","page":"Documentation","title":"Neighbor finders","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Neighbor finders find close atoms periodically throughout the simulation, saving on computation time by allowing the force calculation between distant atoms to be omitted. When using a neighbor finder you should in general also use a cutoff (see Cutoffs) with a cutoff distance less than the neighbor finder distance. The available neighbor finders are:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"NoNeighborFinder\nCellListMapNeighborFinder\nTreeNeighborFinder\nDistanceNeighborFinder","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To define your own neighbor finder, first define the struct:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"struct MyNeighborFinder\n eligible::BitArray{2}\n special::BitArray{2}\n n_steps::Int\n # Any other properties, e.g. a distance cutoff\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Examples of three useful properties are given here: a matrix indicating atom pairs eligible for pairwise interactions, a matrix indicating atoms in a special arrangement such as 1-4 bonding, and a value determining how many time steps occur between each evaluation of the neighbor finder. Then, define the neighbor finding function that is called every step by the simulator:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"function Molly.find_neighbors(sys,\n nf::MyNeighborFinder,\n current_neighbors=nothing,\n step_n::Integer=0,\n force_recompute::Bool=false;\n n_threads::Integer=Threads.nthreads())\n if force_recompute || step_n % nf.n_steps == 0\n if isnothing(current_neighbors)\n neighbors = NeighborList()\n else\n neighbors = current_neighbors\n end\n empty!(neighbors)\n # Add to neighbors, for example\n push!(neighbors, (1, 2, false)) # atom i, atom j and whether they are in a special interaction\n return neighbors\n else\n return current_neighbors\n end\nend","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"To use your custom neighbor finder, give it as the neighbor_finder argument when creating the System.","category":"page"},{"location":"documentation/#Analysis","page":"Documentation","title":"Analysis","text":"","category":"section"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Molly contains some tools for analysing the results of simulations. Functions that may be useful for analysis include:","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"visualize\nrdf\ndistances\ndisplacements\nrmsd\nradius_gyration\nhydrodynamic_radius\nbond_angle\ntorsion_angle","category":"page"},{"location":"documentation/","page":"Documentation","title":"Documentation","text":"Julia is a language well-suited to implementing all kinds of analysis for molecular simulations.","category":"page"},{"location":"exercises/#Molly-exercises","page":"Exercises","title":"Molly exercises","text":"","category":"section"},{"location":"exercises/","page":"Exercises","title":"Exercises","text":"A notebook with exercises covering various parts of Molly is available here, with answers available here.","category":"page"},{"location":"exercises/","page":"Exercises","title":"Exercises","text":"Another notebook with exercises on computing transport coefficients with Molly is available here.","category":"page"}] }