Handling Atoms
This package makes the choice to separate the atomic parameters from their positions and velocities for ease of use with the differential equations solvers. This contrasts somewhat with most other software packages where these would usually be joined together into a single object.
The atomic parameters here are contained within the Atoms
type introduced earlier in the Getting started section. As mentioned previously, there exist some basic constructors which use either elemental symbols or numbers to initialise the parameters:
julia> using NQCDynamics
julia> Atoms([:H, :H, :H])
Atoms{Float64}([:H, :H, :H], [1, 1, 1], [1837.4715941070515, 1837.4715941070515, 1837.4715941070515])
julia> Atoms([1, 2, 3])
Atoms{Float64}([:X, :X, :X], [0, 0, 0], [1.0, 2.0, 3.0])
If there are many atoms, you can use Julia's array manipulation utilities to create large vectors with many atom types. For example, if adding an adsorbate to a metal surface, it could be initialised as:
julia> au = fill(:Au, 40)
40-element Vector{Symbol}: :Au :Au :Au :Au :Au :Au :Au :Au :Au :Au ⋮ :Au :Au :Au :Au :Au :Au :Au :Au :Au
julia> no = [:N, :O]
2-element Vector{Symbol}: :N :O
julia> auno = [au; no]
42-element Vector{Symbol}: :Au :Au :Au :Au :Au :Au :Au :Au :Au :Au ⋮ :Au :Au :Au :Au :Au :Au :Au :N :O
julia> Atoms(auno)
Atoms{Float64}([:Au, :Au, :Au, :Au, :Au, :Au, :Au, :Au, :Au, :Au … :Au, :Au, :Au, :Au, :Au, :Au, :Au, :Au, :N, :O], [79, 79, 79, 79, 79, 79, 79, 79, 79, 79 … 79, 79, 79, 79, 79, 79, 79, 79, 7, 8], [359048.0926227164, 359048.0926227164, 359048.0926227164, 359048.0926227164, 359048.0926227164, 359048.0926227164, 359048.0926227164, 359048.0926227164, 359048.0926227164, 359048.0926227164 … 359048.0926227164, 359048.0926227164, 359048.0926227164, 359048.0926227164, 359048.0926227164, 359048.0926227164, 359048.0926227164, 359048.0926227164, 25533.199026445902, 29164.39289099079])
Manipulating atomic structures with AtomsBase.jl
AtomsBase provides a convenient format for representing atomic geometries, facilitating interoperability between a collection of different packages.
When working with NQCDynamics, the most useful packages are NQCBase for reading and writing structures and trajectories, and PythonCall for working with ASE from within Julia.
PythonCall.jl by default depends on CondaPkg.jl to install Python packages, but you can opt out and use your own python installation.
Using Python's ase
package from Julia
Julia provides multiple options to run Python-based code from Julia scripts. NQCDInterfASE provides compatibility with PythonCall.jl. PythonCall forces you as a user to think more about when data is copied in memory between Python and Julia, supporting the creation of more efficient code.
This example shows how ase.build
can be used to build a structure from within Julia, then convert from the ASE format into the required objects for atoms, positions and unit cell for an NQCD simulation:
using PythonCall
using NQCBase
ase_build = pyimport("ase.build")
# Make a silicon supercell using ASE
atoms_ase = ase_build.bulk("Si") * pytuple((4, 1, 1))
It is currently not possible to use an AtomsBase system directly with NQCDynamics, but can be quickly converted to the correct format:
using NQCDynamics
using NQCDInterfASE # Import python interface functionality
atoms_nqcd, positions_nqcd, cell_nqcd = convert_from_ase_atoms(atoms_ase)
println(atoms_nqcd)
println(positions_nqcd)
println(cell_nqcd)
Saving and loading with AtomsBase
After running a simulation it is often desirable to save the trajectory in a standard format for visualization. For this, convert the NQCDynamics output into the AtomsBase format, then use AtomsIO to write the file in your chosen format.
using AtomsIO
using NQCBase
v = rand(3, 8); #velocities
r = rand(3,8); #positions
cell = PeriodicCell([ 1 0 0;
0 1 0;
0 0 1;
]) # unit cell definition
#all definitions here are in atomic units, use Unitful.jl if you want to convert into Angstrom
system = System(atoms_nqcd, r, v, cell)
AtomsIO.save_system("Si.xyz", system) # Save a single image
trajectory = Trajectory(atoms_nqcd, [r, r, r, r], [v, v, v, v], cell)
AtomsIO.save_trajectory("Si.xyz", trajectory) # Save a trajectory
AtomsIO also provides load_system
and load_trajectory
which can be converted to the NQCDynamics format as above to initialise simulations. Refer to AtomsIO for more information.