API Reference
Verlet.Core.kinetic_energy
Verlet.Core.minimum_image
Verlet.Core.potential_energy
Verlet.Core.velocity_verlet!
Verlet.Core.wrap_positions!
Verlet.Core.CubicBox
Verlet.Core.ParticleSystem
Verlet.Neighbors.build_cellgrid
Verlet.Neighbors.build_neighborlist
Verlet.Neighbors.build_neighborlist_cells
Verlet.Neighbors.lj_forces
Verlet.Neighbors.maybe_rebuild!
Verlet.Neighbors.rebin!
Verlet.Neighbors.CellGrid
Verlet.Neighbors.NeighborList
Verlet.Constraints.apply_rattle!
Verlet.Constraints.apply_shake!
Verlet.Constraints.constraint_residuals
Verlet.Constraints.remove_com_motion!
Verlet.Constraints.velocity_verlet_shake_rattle!
Verlet.Constraints.DistanceConstraints
Verlet.Thermostats.degrees_of_freedom
Verlet.Thermostats.degrees_of_freedom
Verlet.Thermostats.instantaneous_temperature
Verlet.Thermostats.instantaneous_temperature
Verlet.Thermostats.langevin_baoab!
Verlet.Thermostats.langevin_baoab!
Verlet.Thermostats.langevin_baoab_constrained!
Verlet.Thermostats.velocity_rescale!
Verlet.Thermostats.velocity_rescale!
Verlet.Core
— ModuleCore module: fundamental types & utilities.
Data representation (current): positions :: Vector{SVector{D,T}} velocities :: Vector{SVector{D,T}} forces(R) :: Vector{SVector{D,T}} (or (F,U) with return_potential=true)
Matrix-based NxD layouts are deprecated.
Verlet.Core.CubicBox
— Typestruct CubicBox{T<:Real}
Simple cubic periodic box with side length L
.
Verlet.Core.ParticleSystem
— TypeParticleSystem{Dims,T_float}
A lightweight container for particle states.
Fields
positions::Vector{SVector{Dims,T_float}}
: length-N vector of positions (each an SVector)velocities::Vector{SVector{Dims,T_float}}
: length-N vector of velocitiesmasses::Vector{T_float}
: length-N vector of particle masses
Verlet.Core.kinetic_energy
— Methodkinetic_energy(system::ParticleSystem) -> T_float
Total kinetic energy: ∑ ½ mᵢ ‖vᵢ‖²
.
Verlet.Core.minimum_image
— Methodminimum_image(Δ, box::CubicBox)
minimum_image(Δ, L)
Apply the minimum-image convention to the displacement vector Δ
for the periodic box
(wraps components to (-L/2, L/2]) and return the new vector.
Verlet.Core.potential_energy
— Methodpotential_energy(system::ParticleSystem{Dims,T_float}, forces::Function) -> T_float
Try to obtain total potential energy using forces(...; return_potential=true)
convention.
Verlet.Core.velocity_verlet!
— Methodvelocity_verlet!(system::ParticleSystem{Dims,T_float}, forces::Function, dt::T_float)
Advance system
by one timestep dt
using the velocity Verlet integrator.
Verlet.Core.wrap_positions!
— Methodwrap_positions!(R, box::CubicBox)
Wrap particle positions R
into the primary periodic image of box
in-place. This is useful before building neighbor lists or measuring displacements.
The resulting positions will be in the range (-L/2, L/2].
Verlet.Neighbors.CellGrid
— TypeCellGrid{IT<:Integer, T<:Real}
Linked-list cell grid for cubic periodic boxes. The domain is split into a uniform dims = (nx,ny,nz)
grid of cubic cells with an intrusive linked list (heads
, next
) storing particle indices in each cell. This enables O(N) rebinning at fixed density and powers the O(N) neighbor build.
Fields
L::T
: cubic box length.cell_size::T
: effective uniform cell width actually used for binning.dims::NTuple{3,IT}
: number of cells along each axis (each ≥ 1).heads::Vector{IT}
: lengthnx*ny*nz
; head index per cell (0
sentinel = empty).next::Vector{IT}
: lengthN
; linked list “next” pointer per particle (0
= end).
Verlet.Neighbors.NeighborList
— TypeNeighborList{IT<:Integer,T<:Real}
Compressed-sparse-row (CSR) neighbor list with snapshot of reference positions.
Fields
cutoff::T
: physical cutoff distance (rcut
).skin::T
: extra buffer distance beyond cutoff.pairs::Vector{IT}
: concatenated neighbor indices (CSR-style).offsets::Vector{IT}
: lengthN+1
; neighbors of particlei
are stored inpairs[offsets[i]:(offsets[i+1]-1)]
.ref_positions::Matrix{T}
: snapshot of positions at last rebuild, used for displacement checks.
Use build_neighborlist
to construct, and maybe_rebuild!
to keep it updated.
Verlet.Neighbors.build_cellgrid
— Methodbuild_cellgrid(R, box; cell_size)
Create a new CellGrid
sized for cell_size
and bin positions R
(N×3). Returns a populated grid with linked lists set for particle indices 1..N.
Verlet.Neighbors.build_neighborlist
— Methodbuild_neighborlist(R, box; cutoff, skin=0.3) -> NeighborList
Construct a neighbor list for positions R
inside a CubicBox
. Pairs are included if they are within cutoff + skin
under minimum-image distance.
This is an O(N²) operation but performed infrequently. The returned NeighborList
uses a CSR (compressed sparse row) layout for compact storage and fast iteration.
Verlet.Neighbors.build_neighborlist_cells
— Methodbuild_neighborlist_cells(R, box; cutoff, skin=0.3, grid=nothing) -> NeighborList-like
Construct a half neighbor list (each pair appears exactly once with j>i) using an O(N) cell-linked grid build. Returns an object with .offsets
and .pairs
fields compatible with typical CSR traversal.
Verlet.Neighbors.lj_forces
— Methodlj_forces(R, box, nlist; ϵ=1.0, σ=1.0, rcut=NaN, shift=false, return_potential=false)
Compute Lennard–Jones forces using a prebuilt NeighborList
. This is more efficient than the brute-force O(N²) kernel, scaling ~O(N) for fixed density.
Arguments
R::Vector{SVector{Dims,T}}
: positions.box::CubicBox
: periodic cubic box.nlist::NeighborList
: neighbor list built with at least the requested cutoff.
Keywords
ϵ
: LJ well depth (default 1.0).σ
: LJ length scale (default 1.0).rcut
: optional cutoff to use for the force calculation (defaults tonlist.cutoff
).shift::Bool
: if true, subtract potential at cutoff to make it continuous.return_potential::Bool
: if true, also return total potential energy.
Returns
F::Vector{SVector{Dims,T}}
: force on each particle (same shape asR
).U::Float64
(ifreturn_potential=true
).
Example
box = CubicBox(8.0)
R = randn(SVector{3, Float64}, 10)
wrap_positions!(R, box)
nlist = build_neighborlist(R, box; cutoff=2.5, skin=0.4)
F, U = lj_forces(R, box, nlist; rcut=2.5, return_potential=true)
Verlet.Neighbors.maybe_rebuild!
— Methodmaybe_rebuild!(nlist, R, box) -> Bool
Check whether the neighbor list nlist
is still valid given new positions R
. If the maximum displacement since the last build is greater than skin/2
, rebuild the list and return true
. Otherwise leave it unchanged and return false
.
Verlet.Neighbors.rebin!
— Methodrebin!(grid, R, box) -> grid
Reset the grid's heads
and next
and bin the positions R
into cells according to the current cell_size
and periodic cubic box box
.
Verlet.Constraints.DistanceConstraints
— Typestruct DistanceConstraints{T_int,T_float}
Immutable set of pairwise distance constraints for SHAKE/RATTLE.
Verlet.Constraints.apply_rattle!
— Methodapply_rattle!(ps, cons)
Correct velocities to satisfy velocity constraints.
Verlet.Constraints.apply_shake!
— Methodapply_shake!(ps, cons, dt)
Iteratively correct positions to satisfy constraints.
Verlet.Constraints.constraint_residuals
— Methodconstraint_residuals(ps::ParticleSystem, cons::DistanceConstraints) -> (; maxC, rmsC, maxCd, rmsCd)
Compute the constraint residuals for a system subject to pairwise distance constraints.
For each constraint l
with atoms (i, j)
and target distance r0_l
:
- Position residual:
C_l = ||r_i - r_j||^2 - r0_l^2
- Velocity residual:
Ċ_l = 2 (r_i - r_j) ⋅ (v_i - v_j)
Returns a named tuple with maxC
, rmsC
, maxCd
, rmsCd
.
Example
ps = ParticleSystem([SVector(0.0, 0, 0), SVector(1.0, 0, 0)], [SVector(0.0, 0, 0), SVector(0.0, 0, 0)], [1.0, 1.0])
cons = DistanceConstraints([(1,2)], [1.0])
constraint_residuals(ps, cons)
Verlet.Constraints.remove_com_motion!
— Methodremove_com_motion!(ps; which=:velocity)
Remove center-of-mass motion from velocities/positions.
Verlet.Constraints.velocity_verlet_shake_rattle!
— Methodvelocity_verlet_shake_rattle!(ps, forces, dt, cons)
Constrained velocity Verlet step with SHAKE/RATTLE.
Verlet.Thermostats.degrees_of_freedom
— Function- degreesoffreedom(ps) -> Int
Return the effective translational degrees of freedom of the system. Currently this is simply N * D
for N
particles in D
spatial dimensions.
This function is a hook for future extensions (e.g., constraints, rigid bodies, or COM removal) that reduce the effective DoF used in temperature calculations.
Verlet.Thermostats.degrees_of_freedom
— Methoddegrees_of_freedom(ps; constraints=nothing, remove_com=false) -> Int
Effective translational DoF, reduced by number of constraints and optionally COM removal. This is the canonical method. A no-keyword fallback is provided for backward compatibility.
Verlet.Thermostats.instantaneous_temperature
— Function- instantaneous_temperature(ps; kB=1.0) -> Float64
Compute the instantaneous scalar temperature via equipartition:
KE = 0.5 * Σᵢ mᵢ ||vᵢ||² T = 2 * KE / (kB * dof)
where dof = degrees_of_freedom(ps)
. For now, dof = N * D
(translational DoF only).
Keywords
kB::Real = 1.0
: Boltzmann constant in your unit system.
Returns
Float64
: instantaneous temperature.
Notes
- For small
N
, expect large fluctuations. Compare time averages to the targetT
when thermostatting.
Verlet.Thermostats.instantaneous_temperature
— Methodinstantaneous_temperature(ps; kB=1.0) -> Float64
Compute instantaneous temperature via equipartition: T = 2 * KE / (kB * dof).
Verlet.Thermostats.langevin_baoab!
— Function- langevinbaoab!(ps::ParticleSystem, forces, dt; γ, T, kB=1.0, rng=Random.defaultrng())
Advance the system by one NVT step using the Langevin BAOAB integrator.
This scheme integrates the Langevin SDE
m dv = [F(r) - γ m v] dt + √(2γ m kB T) dW, dr = v dt
via the B → A → O → A → B splitting with an exact Ornstein–Uhlenbeck (O) step. It is widely used for robust thermostatting and near-optimal configurational sampling. When γ == 0
, the O-step is the identity and BAOAB reduces to velocity–Verlet (up to roundoff).
Arguments
ps::ParticleSystem
: System withpositions::Vector{SVector{Dims,T}}
,velocities::Vector{SVector{Dims,T}}
, andmasses::Vector{T}
.forces
: A callableF(R)::Vector{SVector{Dims,T}}
returning forces evaluated at positionsR
.dt::Real
: Time step (same time units as used forγ
).
Keywords
γ::Real
: Friction coefficient [1/time].T::Real
: Target temperature. Must be consistent withkB
(so thatkB*T
has units of energy).kB::Real = 1.0
: Boltzmann constant in your unit system.rng::AbstractRNG = Random.default_rng()
: RNG used only in the stochastic O-step for reproducibility.
Details
The O-step uses the exact OU update: c = exp(-γ*dt) sᵢ = sqrt((1 - c^2) * kB*T / mᵢ) # per-particle, per-component scale vᵢ[k] ← c * vᵢ[k] + sᵢ * ξ, where ξ ~ 𝒩(0,1)
For numerical stability when γ*dt ≪ 1
, implementations should use the linearized forms c ≈ 1 - γ*dt
and 1 - c^2 ≈ 2γ*dt
to avoid catastrophic cancellation.
Performance tips
- Avoid allocations in the inner loop. Reuse force buffers if you add multi-step drivers.
- Use
@views
for in-place operations onps.positions
/ps.velocities
. - Pass an explicit
rng
to ensure reproducibility across runs and Julia sessions.
Pitfalls
- Units matter: ensure
γ
is in1/time
, andkB*T
is in energy, consistent withr
,v
, andm
. - Very large
γ*dt
yields overdamped dynamics (OK for sampling, not for kinetics). - With heterogeneous masses, compute
sᵢ
per particle; do not share a single scale across species. - For tiny systems, instantaneous temperature fluctuates strongly — average over time for diagnostics.
Example
```julia
Pseudocode showing typical usage (assumes your package exports ParticleSystem and BAOAB):
using Random N, D = 64, 3 ps = ParticleSystem(zeros(N, D), randn(N, D), ones(N)) dt, γ, T = 0.005, 1.0, 1.5 rng = MersenneTwister(123) forces(R) = -0.1 .* R # harmonic "bath"
for _ in 1:2_000
- langevin_baoab!(ps, forces, dt; γ=γ, T=T, kB=1.0, rng=rng) end ```
Verlet.Thermostats.langevin_baoab!
— Methodlangevin_baoab!(ps, forces, dt; γ, T, kB=1.0, rng=Random.default_rng())
Advance one step with the Langevin BAOAB integrator: B(half) → A(half) → O(OU) → A(half) → B(half). With γ=0, this reduces to velocity-Verlet (deterministic).
Arguments
ps
: particle system (positions N×D, velocities N×D, masses length N)forces
: callable F = forces(positions)::N×Ddt
: time step
Keywords
γ
: friction (1/time)T
: target temperaturekB
: Boltzmann constant (default 1.0)rng
: AbstractRNG for reproducibility
Verlet.Thermostats.langevin_baoab_constrained!
— Methodlangevin_baoab_constrained!(ps::ParticleSystem, forces, dt, cons::DistanceConstraints;
γ, T, kB=1.0, rng=Random.default_rng())
Advance one constrained NVT BAOAB step with SHAKE/RATTLE projections.
Splitting and projections:
- B (half kick) →
apply_rattle!
(velocities) - A (half drift) →
apply_shake!
(positions) - O (OU stochastic) →
apply_rattle!
(velocities) - A (half drift) →
apply_shake!
(positions) - Recompute forces
- B (half kick) →
apply_rattle!
(velocities)
Notes:
- Uses
exp(-γ*dt)
for the OU decay. Noise variance is(1 - c^2) * kB*T / m_i
per component. - With constraints, prefer this method over the unconstrained
langevin_baoab!
.
Verlet.Thermostats.velocity_rescale!
— Function- velocity_rescale!(ps, T; kB=1.0)
Deterministically rescale all velocities to match a target temperature T
. This is a one-shot utility often used for quick pre-equilibration or initializing an NVT run. It is not a thermostat and does not generate the correct kinetic energy distribution by itself.
Keywords
kB::Real = 1.0
: Boltzmann constant.
Notes
Let T₀ = instantaneous_temperature(ps; kB)
and λ = √(T / max(T₀, eps()))
. This function applies vᵢ ← λ * vᵢ
in-place.
Verlet.Thermostats.velocity_rescale!
— Methodvelocity_rescale!(ps, T; kB=1.0)
Deterministically rescale velocities to match target temperature T.