Custom Potentials
YASS allows you to implement custom potentials for molecular simulations. This guide demonstrates how to create a custom potential using a Lennard-Jones potential for gold (Au) as an example. To create a custom potential, you need three components:
PotVarsstruct - Holds potential parameters- Initializer function - Sets up the potential
- Evaluation functions - Calculate energies and forces
using YetAnotherSimulationSuite
struct AuLJPotVars{F<:Float64} <: PotVars
ε::F # Well depth (eV)
σ::F # Equilibrium distance (Å)
rc::F # Cutoff radius (Å)
rc2::F # Squared cutoff (Ų)
end
# Initialize potential with Au parameters
function AuLJPotential(x::Union{Vector{MyAtoms}, MyCell})
ε = 5.29e-3 # eV
σ = 2.951 # Å (minimum at 2^(1/6)σ ≈ 3.31Å)
rc = 8.0 # Å
rc2 = rc^2 # Precalculate squared cutoff
AuLJPotVars(ε, σ, rc, rc2)
end
# Energy-only evaluation
function AuLJEnergy(u, vars)
E = 0.0
potVars = vars.potVars
# Extract parameters
ε = potVars.ε
σ = potVars.σ
rc2 = potVars.rc2
# Calculate pair interactions
for i = 1:length(u)
for j = i+1:length(u)
# Get distance vector and magnitude squared
r = u[j] - u[i]
r2 = dot(r, r)
# Skip if beyond cutoff
r2 > rc2 && continue
# Calculate LJ terms
σ_r2 = (σ^2)/r2
σ_r6 = σ_r2^3
σ_r12 = σ_r6^2
# Add pair energy
E += 4ε*(σ_r12 - σ_r6)
end
end
E
end
# Force-only evaluation (in-place)
function AuLJForces!(F, u, vars)
potVars = vars.potVars
# Extract parameters
ε = potVars.ε
σ = potVars.σ
rc2 = potVars.rc2
# Calculate forces between pairs
for i = 1:length(u)
for j = i+1:length(u)
# Get distance vector and magnitude squared
r = u[j] - u[i]
r2 = dot(r, r)
# Skip if beyond cutoff
r2 > rc2 && continue
# Calculate LJ terms
σ_r2 = (σ^2)/r2
σ_r6 = σ_r2^3
σ_r12 = σ_r6^2
# Calculate force (negative gradient)
f = 24ε/r2 * (2σ_r12 - σ_r6) .* r
# Add forces
F[i] .-= f
F[j] .+= f
end
end
end
# Combined energy and forces evaluation
function AuLJEnergyForces!(F, u, vars)
E = 0.0
potVars = vars.potVars
# Extract parameters
ε = potVars.ε
σ = potVars.σ
rc2 = potVars.rc2
# Calculate interactions
for i = 1:length(u)
for j = i+1:length(u)
# Get distance vector and magnitude squared
r = u[j] - u[i]
r2 = dot(r, r)
# Skip if beyond cutoff
r2 > rc2 && continue
# Calculate LJ terms
σ_r2 = (σ^2)/r2
σ_r6 = σ_r2^3
σ_r12 = σ_r6^2
# Add pair energy
E += 4ε*(σ_r12 - σ_r6)
# Calculate and add forces
f = 24ε/r2 * (2σ_r12 - σ_r6) .* r
F[i] .-= f
F[j] .+= f
end
end
E
end
# Initializer function
function AuLJ(; constraints=nothing)
Calculator(
AuLJPotential;
E = AuLJEnergy,
F = AuLJForces!,
EF = AuLJEnergyForces!,
constraints=constraints
)
endUsage example
# Create gold cluster
atoms::Vector{MyAtoms} = [
Particle([0.0, 0.0, 0.0], zeros(3), 196.97, "Au"),
Particle([3.0, 0.0, 0.0], zeros(3), 196.97, "Au"),
Particle([0.0, 3.0, 0.0], zeros(3), 196.97, "Au")
]
# Create calculator
calc = AuLJ()
# Get energy
E = getPotEnergy(calc, atoms)
println("Potential energy: $E eV")
# Get forces
F = getForces(calc, atoms)
println("Forces on first atom: $(F[1]) eV/Å")
# Run MD simulation
traj = run(calc, atoms, (0.0, 10.0ps), 1.0fs, NVE())
println("Trajectory length: $(length(traj)) frames")Key Points About Custom Potentials
Parameter Storage
- Use
PotVarsto store constants and parameters - Precalculate frequently used values (like rc²)
- Include units in comments for clarity
- Use
Force Calculations
- Use in-place operations with
F[i] .-= fstyle updates
- Use in-place operations with
Optimization Tips
- Implement distance cutoffs for efficiency
- Precalculate squared terms where possible
- Use inplace operations and buffers to reduce memory allocations
Good Practices
- Include references for potential parameters
- Document units clearly
- Implement all three evaluation functions for flexibility
- Test energy conservation in MD simulations
For more examples, check the source code of built-in potentials in YASS.