Matter and Coordinates
Atoms
and Charges
are the building blocks of Crystal
s and molecules in Xtals.jl
. Each have coordinates in both Cart
esian and Frac
tional space (associated with unit cell information, i.e., a Box
).
Coordinates
We store coordinates as an abstract Coords
type that has two subtypes: Cart
and Frac
for Cartesian and Fractional, respectively. See the Wikipedia page on fractional coordinates, which are defined in the context of a periodic system, e.g. within a crystal.
Construct coordinates of n
particles by passing a n
by 3
array:
# construct cartesian coordinates of a particle
coord = Cart([1.0, 2.0, 5.0])
coord.x
# output
3×1 Matrix{Float64}:
1.0
2.0
5.0
# construct fractional coordinates of a particle
coord = Frac([0.1, 0.2, 0.5])
coord.xf
# output
3×1 Matrix{Float64}:
0.1
0.2
0.5
The coordinates of multiple particles are stored column-wise:
# five particles at uniform random coordinates
coords = Cart([
0.0 1.0 0.0 0.0 1.0
0.0 0.0 1.0 0.0 1.0
0.0 0.0 0.0 1.0 1.0
])
Many Array
operations work on Coords
, such as:
coords[2] # coordinate of 2nd particle
coords[2:3] # (slicing by index) coords of particles 2 and 3
coords[[1, 2, 5]] # (slicing by index) coords of particles 1, 2, and 5
coords[rand(Bool, 5)] # (boolean slicing) coords, selected at random
length(coords) # number of particles, (5)
Manipulating coordinates
Coords
are immutable:
coords.x = rand(3, 5)
# output
ERROR: setfield!: immutable struct of type Cart cannot be changed
But we can manipulate the values of Array{Float64, 2}
where coordinates (through coords.x
or coords.xf
) are stored:
coords.x[2, 3] = 100.0 # successful!
coords.x[:] = rand(3, 5) # successful! (achieves the above, but need the [:] to say "overwrite all of the elements"
Fractional coordinates can be wrapped to be inside the unit cell box:
coords = Frac([1.2, -0.3, 0.9])
wrap!(coords)
coords.xf
# output
3×1 Matrix{Float64}:
0.19999999999999996
0.7
0.9
We can translate coordinates by a vector dx
:
dx = Cart([1.0, 2.0, 3.0])
coords = Cart([1.0, 0.0, 0.0])
translate_by!(coords, dx)
coords.x
# output
3×1 Matrix{Float64}:
2.0
2.0
3.0
If dx::Frac
and coords::Cart
, translate_by!
requires a Box
to convert between Frac
tional and Cart
esian, as the last argument:
dx = Frac([0.1, 0.2, 0.3])
box = unit_cube()
coords = Cart([1.0, 0.0, 0.0])
translate_by!(coords, dx, box)
coords.x
# output
3×1 Matrix{Float64}:
1.1
0.20000000000000004
0.3
Atoms
An atom is specified by its coordinates and atomic species. We can construct a set of Atoms
(perhaps comprising a molecule or crystal) as follows:
species = [:O, :H, :H] # atomic species are represnted with Symbols
coords = Cart([
0.0 0.757 -0.757 # coordinates of each
0.0 0.586 0.586
0.0 0.0 0.0
])
atoms = Atoms(species, coords) # 3 atoms comprising water
atoms.n # number of atoms, 3
atoms.coords # coordinates; atoms.coords.x gives the array of coords
atoms.species # array of species
atoms::Atoms{Cart} # successful type assertion, as opposed to atoms::Atoms{Frac}
The last line illustrates the two subtypes of Atoms
, depending on whether the Coords
are stored as Frac
tional or Cart
esian.
We can slice Atoms
, such as:
atoms[1] # 1st atom
atoms[2:3] # 2nd and 3rd atom
And combine them:
atoms_combined = atoms[1] + atoms[2:3] # combine atoms 1, 2, and 3
isapprox(atoms, atoms_combined)
# output
true
Charges
Point Charges
work analogously to Atoms
, except instead of species
, the values of the point charges are stored in an array q
.
q = [-1.0, 0.5, 0.5] # values of point charges, units: electrons
coords = Cart([
0.0 0.757 -0.757 # coordinates of the point charges
0.0 0.586 0.586
0.0 0.0 0.0
])
charges = Charges(q, coords) # 3 point charges
charges.n # number of charges, 3
charges.coords # retreive coords
charges.q # retreive q
charges::Charges{Cart} # successful type assertion, as opposed to charges::Charges{Frac}
We can determine if the set of point charges comprise a charge-neutral system by:
net_charge(charges)
# output
0.0
neutral(charges)
# output
true
Detailed Docs
Xtals.Coords
— Typeabstract type for coordinates.
Xtals.Frac
— Typefractional coordinates, a subtype of Coords
.
construct by passing an Array{Float64, 2}
whose columns are the coordinates.
generally, fractional coordinates should be in [0, 1] and are implicitly associated with a Box
to represent a periodic coordinate system.
e.g.
f_coords = Frac(rand(3, 2)) # 2 particles
f_coords.xf # retreive fractional coords
Xtals.Cart
— Typecartesian coordinates, a subtype of Coords
.
construct by passing an Array{Float64, 2}
whose columns are the coordinates.
e.g.
c_coords = Cart(rand(3, 2)) # 2 particles
c_coords.x # retreive cartesian coords
Xtals.Atoms
— Typeused to represent a set of atoms in space (their atomic species and coordinates).
struct Atoms{T <: Coords} # enforce that the type specified is `Coords`
n::Int # how many atoms?
species::Array{Symbol, 1} # list of species
coords::T # coordinates
end
here, T
is Frac
or Cart
.
helper constructor (infers n
):
species = [:H, :H]
coords = Cart(rand(3, 2))
atoms = Atoms(species, coords)
Xtals.Charges
— Typeused to represent a set of partial point charges in space (their charges and coordinates).
struct Charges{T <: Coords} # enforce that the type specified is `Coords`
n::Int
q::Array{Float64, 1}
coords::T
end
here, T
is Frac
or Cart
.
helper constructor (infers n
):
q = [0.1, -0.1]
coords = Cart(rand(3, 2))
charges = Charges(q, coords)
Xtals.net_charge
— Functionnc = net_charge(charges)
nc = net_charge(crystal)
nc = net_charge(molecule)
find the sum of charges in charges::Charges
or charges in crystal::Crystal
or molecule::Molecule
. (if there are no charges, the net charge is zero.)
Xtals.neutral
— Functionneutral(charges, tol) # true or false. default tol = 1e-5
neutral(crystal, tol) # true or false. default tol = 1e-5
determine if a set of charges::Charges
(charges.q
) sum to an absolute value less than tol::Float64
. if crystal::Crystal
is passed, the function looks at the crystal.charges
. i.e. determine the absolute value of the net charge is less than tol
.
Xtals.translate_by!
— Functiontranslate_by!(coords, dx)
translate_by!(coords, dx, box)
translate_by!(molecule, dx)
translate_by!(molecule, dx, box)
translate coords
by the vector dx
. that is, add the vector dx
.
this works for any combination of Frac
and Cart
coords.
modifies coordinates in place.
box
is needed when mixing Frac
and Cart
coords.
note that periodic boundary conditions are not subsequently applied here.
if applied to a molecule::Molecule
, the coords of atoms, charges, and center of mass are all translated.