NQCModels.jl
To perform nonadiabatic molecular dynamics simulations, it is necessary to define the system Hamiltonian. For simple models, this often comes in the form of small matrix in the diabatic representation but equally the electronic Hamiltonian could be obtained directly from ab initio electronic structure theory.
NQCModels.jl is a package that aims to provide a common interface for defining these models that is flexible enough to allow for a wide range of specifications and requirements. NQCDynamics.jl uses this interface to obtain the potentials and couplings necessary to perform the dynamics simulations. Along with the minimal interface, NQCModels.jl also provides a library of popular models often used in the field of nonadiabatic dynamics.
Taking advantages of Julia's seamless modularity, NQCModels.jl is designed as a separate package so that it can also be used independently from the main package.
Depending on the quantities provided by the Model, we use Julia's abstract type system to group models that provide the same quantities. Currently, there are two top-level abstract types: ClassicalModel and QuantumModel. The ClassicalModel is used for adiabatic dynamics, providing only a classical potential in the form of an analytical function and its derivative, the force used in classical mechanics. The QuantumModel is used for nonadiabatic dynamics, where the potential is no longer an analytical function instead a Hermitian matrix.
Using Models
In the Getting started section we briefly touched on how the ClassicalModel works and introduced one of the included models. Here let's take a look at a QuantumModel, which is more appropriate for nonadiabatic dynamics.
The DoubleWell is a two state, 1 dimensional model where each state is harmonic with linear coupling to the single degree of freedom.
using NQCModels
model = DoubleWell()DoubleWell{Int64, Int64, Int64, Int64}
mass: Int64 1
ω: Int64 1
γ: Int64 1
Δ: Int64 1
Our DoubleWell implements the functions potential and derivative, their in-place versions, nstates and ndofs that return the potential, the derivative of the potential, the number of states, and the number of degrees of freedom, respectively.
julia> p = potential(model, hcat(0.2))2×2 LinearAlgebra.Hermitian{Float64, Matrix{Float64}}: 0.302843 0.5 0.5 -0.262843julia> d = derivative(model, hcat(0.2))1×1 Matrix{LinearAlgebra.Hermitian{Float64, Matrix{Float64}}}: [1.614213562373095 0.0; 0.0 -1.2142135623730952]julia> potential!(model, p, hcat(0.5))2×2 Matrix{Float64}: 0.832107 0.5 0.5 -0.582107julia> derivative!(model, d, hcat(0.5))julia> nstates(model)2julia> ndofs(model)1
In general the position argument that appears in the derivative and the potential will need to be provided as an AbstractMatrix with dimensions dofsxnatoms. In the case of 1D models of a single atom, potential and derivative accept a position of type Real, but for the most consistent results it's recommended that the user wraps real valued positions in 1x1 matrices using the hcat function.
To understand how this can extend to another dimension, we can take a quick look at the GatesHollowayElbow model which is another two state diabatic model, but this one uses two dimensions to model a diatomic molecule interacting with a surface. The two degrees of freedom are the molecular bond length and the distance from the surface respectively and so the potential and derivative functions accept a position argument with two values, one for each degree of freedom.
julia> model = GatesHollowayElbow()GatesHollowayElbow λ₁: Float64 3.5 λ₂: Float64 3.5 z₀: Float64 1.4 x₀: Float64 0.6 α: Float64 1.028 d: Float64 0.17161933455967182 z12: Float64 0.5 c: Float64 0.018374661087759297 γ: Float64 1.0julia> p = potential(model, [1.0 1.0])2×2 LinearAlgebra.Hermitian{Float64, Matrix{Float64}}: 0.0710215 0.0111448 0.0111448 0.0744945julia> d = derivative(model, [1.0 1.0])1×2 Matrix{LinearAlgebra.Hermitian{Float64, Matrix{Float64}}}: [0.0810696 0.0; 0.0 -0.0129425] … [-0.000787036 -0.0111448; -0.0111448 0.0810696]julia> potential!(model, p, [1.2 0.5])2×2 Matrix{Float64}: 0.0875049 0.0183747 0.0183747 0.0295571julia> derivative!(model, d, [1.2 0.5])1×2 Matrix{LinearAlgebra.Hermitian{Float64, Matrix{Float64}}}: [0.0728352 0.0; 0.0 -0.00642707] … [-0.00452908 -0.0183747; -0.0183747 0.0848168]julia> nstates(model)2julia> ndofs(model)2
Here we see how the derivative now becomes a Matrix with size matching our input, but each entry is a Hermitian containing the elementwise derivative of the potential with respect to each degree of freedom. In this case, the Matrix has size = (1, 2), and in general the dimensions of the Matrix that wraps the Hermitian derivatives should match the dimensions of the position Matrix a given model takes as input.
Included models
NQCModels.jl includes both analytical models as well as interfaces to connect to full-dimensional potential energy surfaces. An overview of the implemented analytical models can be seen in the Analytic model library and many shall return later when we investigate the dynamics methods. An overview of interfaces to full-dimensional potential energy surfaces can be seen in the Full dimensional model library.
To learn more about NQCModels.jl and learn how to implement new models, visit the developer documentation.