Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some refactoring #16

Merged
merged 1 commit into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/Manifest.toml
/docs/Manifest.toml
/docs/build/
*~
.*.swp
4 changes: 1 addition & 3 deletions src/AtomsBuilder.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ module AtomsBuilder
import AtomsBase
using StaticArrays: SMatrix, SVector

const Mat3{T} = SMatrix{3, 3, T}
const Mat3{T} = SMatrix{3, 3, T}
const Vec3{T} = SVector{3, T}

include("chemistry.jl")

include("utils.jl")

include("bulk.jl")

end
92 changes: 46 additions & 46 deletions src/bulk.jl
Original file line number Diff line number Diff line change
@@ -1,46 +1,45 @@
export bulk

using Unitful: unit
using Unitful: unit
using LinearAlgebra: I

const _unit_cells = Dict( # (positions, cell matrix, factor of a)
:fcc => ( [ [0 0 0], ],
[0 1 1; 1 0 1; 1 1 0], 0.5),
:fcc => ( [ [0 0 0], ], [0 1 1; 1 0 1; 1 1 0], 0.5),
:bcc => ( [ [0.0,0.0,0.0], ],
[-1 1 1; 1 -1 1; 1 1 -1], 0.5),
:diamond => ( [ [0.0, 0.0, 0.0], [0.5, 0.5, 0.5] ],
[0 1 1; 1 0 1; 1 1 0], 0.5)
)

const _cubic_cells = Dict( # (positions, factor of a)
:fcc => ( [ [0 0 0], [0 1 1], [1 0 1], [1 1 0] ], 0.5 ),
:bcc => ( [ [0 0 0], [1 1 1] ], 0.5 ),
:fcc => ( [ [0 0 0], [0 1 1], [1 0 1], [1 1 0] ], 0.5 ),
:bcc => ( [ [0 0 0], [1 1 1] ], 0.5 ),
:diamond => ( [ [0 0 0], [1 1 1], [0 2 2], [1 3 3], [2 0 2],
[3 1 3], [2 2 0], [3 3 1] ], 0.25 )
)

const _simple_structures = [:fcc, :bcc, :diamond]

function _simple_bulk(sym::Symbol, cubic::Bool; a=nothing, T = Float64)
function _simple_bulk(sym::Symbol, cubic::Bool; a=nothing, T=Float64)
if cubic
X, scal = _cubic_cells[Chemistry.symmetry(sym)]
C = Matrix(1.0I, 3, 3) / scal
else
X, C, scal = _unit_cells[Chemistry.symmetry(sym)]
end
a === nothing && (a = Chemistry.lattice_constant(sym))
a = @something a Chemistry.lattice_constant(sym)
TU = typeof( one(T) * unit(a) )
return [ Vec3{TU}(x * a * scal) for x in X ], Mat3{TU}(C * a * scal)
[ Vec3{TU}(x * a * scal) for x in X ], Mat3{TU}(C * a * scal)
end


function _bulk_hcp(sym::Symbol; a=nothing, c=nothing, T=Float64)
D = Chemistry.refstate(sym)
a === nothing && (a = D["a"])
c === nothing && (c = a * D["c/a"])
return [ a * Vec3{T}(0.0, 0.0, 0.0),
a * Vec3{T}(0.0, 1 / sqrt(3), c / a / 2) ],
a * Mat3{T}( [1.0, -1/2, 0.0, 0.0, sqrt(3)/2, 0.0, 0.0, 0.0, c/a] )
a = @something a D["a"]
c = @something c a*D["c/a"]
[a * Vec3{T}(0.0, 0.0, 0.0),
a * Vec3{T}(0.0, 1 / sqrt(3), c / a / 2) ],
a * Mat3{T}([1, -1/2, 0,
0, sqrt(3)/2, 0,
0, 0, c/a])
end


Expand All @@ -49,36 +48,37 @@ _convert_pbc(pbc::Bool) = (pbc, pbc, pbc)
_convert_pbc(pbc::AbstractVector) = tuple(pbc...)

"""
`bulk(sym)` : generates a `FlexibleSystem` unit cell for a bulk
crystal structure. If `sym` is a chemical symbol then the phase and
lattice constant are taken from a database that is consistent with ASE.
If `sym` is one of `[:fcc, :bcc, :diamond, :hcp]` then one needs to
specify the kwargs `a` or `c` to determine the lattice constants.
`bulk(sym)` : generates a `FlexibleSystem` unit cell for a bulk
crystal structure. If `sym` is a chemical symbol then the phase and
lattice constant are taken from a database that is consistent with ASE.
Optional alternative values can be chosen via the kwargs
`a`, `b` or `c` to specify the lattice constants.
"""
function bulk(sym::Symbol; T=Float64, cubic = false, pbc = (true,true,true),
a=nothing, c=nothing) # , x=nothing, y=nothing, z=nothing)
symm = try
Chemistry.symmetry(sym)
catch e
if e isa KeyError
throw(ArgumentError("No symmetry information known for element $sym"))
else
rethrow()
end
end
if symm in _simple_structures
X, C = _simple_bulk(sym, cubic; a=a)
elseif symm == :hcp
X, C = _bulk_hcp(sym; a=a, c=c) # cubic parameter is irrelevant for hcp
else
throw(ArgumentError("Currently bulk not immplemented for symmetry $symm"))
end
m = Chemistry.atomic_mass(sym)
Z = Chemistry.atomic_number(sym)
nat = length(X)
at = _flexible_system( X, fill(Z, nat), C, _convert_pbc(pbc))
# I don't remember what this does so I'm commenting it out until I understand
# it again ...
# (x !== nothing || y !== nothing || z !== nothing) && rotate!(at, x=x, y=y, z=z)
return at
function bulk(sym::Symbol; cubic=false, pbc=(true, true, true),
a=nothing, c=nothing, b=nothing, T=Float64)
# , x=nothing, y=nothing, z=nothing)
symm = try
Chemistry.symmetry(sym)
catch e
if e isa KeyError
throw(ArgumentError("No symmetry information known for element $sym"))
else
rethrow()
end
end
if symm in (:fcc, :bcc, :diamond)
X, C = _simple_bulk(sym, cubic; a, T)
elseif symm == :hcp
cubic && throw(ArgumentError("cubic=true cannot be selected for $symm lattices"))
X, C = _bulk_hcp(sym; a, c, T)
else
throw(ArgumentError("Currently bulk not immplemented for symmetry $symm"))
end
Z = Chemistry.atomic_number(sym)
nat = length(X)
at = _flexible_system( X, fill(Z, nat), C, _convert_pbc(pbc))
# I don't remember what this does so I'm commenting it out until I understand
# it again ...
# (x !== nothing || y !== nothing || z !== nothing) && rotate!(at, x=x, y=y, z=z)
return at
end
20 changes: 10 additions & 10 deletions src/chemistry.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
module Chemistry

using JSON, Unitful
using JSON, Unitful

# --------------------------------------------------------------------
# Load data and prepare it a bit ...
# Load data and prepare it a bit ...

const ase_data_path = joinpath(@__DIR__(), "..", "data", "asedata.json")

Expand All @@ -13,7 +13,7 @@ const _rnn = [Float64(d) * u"Å" for d in ase_data["rnn"]]
const _masses = [Float64(m) * u"u" for m in ase_data["masses"]]
const _refstates = Dict{String, Any}[ d == nothing ? Dict{String, Any}() : d
for d in ase_data["refstates"]]
for D in _refstates
for D in _refstates
if haskey(D, "a")
D["a"] *= u"Å"
end
Expand All @@ -30,9 +30,9 @@ for (n, sym) in enumerate(Symbol.(ase_data["symbols"]))
end

# --------------------------------------------------------------------
# some convenient accessor and conversion functions
# these will not be exported and only accessed via Chemistry.atomic_number etc
# in order to avoid confusion with the AtomsBase interface
# some convenient accessor and conversion functions
# these will not be exported and only accessed via Chemistry.atomic_number etc
# in order to avoid confusion with the AtomsBase interface

atomic_number(sym::Symbol) = _numbers[sym]

Expand All @@ -51,11 +51,11 @@ lattice_constant(sym::Symbol) = lattice_constant(atomic_number(sym))
refstate(z::Integer) = _refstates[z+1]
refstate(sym::Symbol) = refstate(atomic_number(sym))

# The `rnn` could be exported though since it is quite useful for a lot of
# modelling scripts. I'll keep it here for now, unexported. Something to
# think about.
# The `rnn` could be exported though since it is quite useful for a lot of
# modelling scripts. I'll keep it here for now, unexported. Something to
# think about.

rnn(z::Integer) = _rnn[z+1]
rnn(sym::Symbol) = rnn(atomic_number(sym))

end
end
10 changes: 4 additions & 6 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,9 @@ function _flexible_system(positions, elements, cell, pbc)
Nat = length(positions)
syms = Chemistry.chemical_symbol.(elements)
atoms = [ Atom(syms[i], positions[i]) for i in 1:Nat ]
bc = pbc isa Bool ? (pbc, pbc, pbc) : tuple(pbc...)
bb = tuple([cell[i, :] for i = 1:3]...)
return FlexibleSystem(atoms;
cell_vectors = bb,
periodicity = bc)
return FlexibleSystem(atoms;
cell_vectors=tuple([cell[i, :] for i = 1:3]...),
periodicity=pbc)
end

_set_position(x::Atom, 𝐫) = Atom(atomic_number(x), 𝐫;
Expand Down Expand Up @@ -188,4 +186,4 @@ removed.
function Base.deleteat!(sys::FlexibleSystem, n)
deleteat!(sys.particles, n)
return sys
end
end
97 changes: 52 additions & 45 deletions test/test_bulk.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,56 +4,63 @@ using Test, AtomsBase, Unitful, Random, JSON

##

@testset "Testing `bulk` and `repeat` against JuLIP reference data" begin
test_systems = JSON.parsefile(joinpath(@__DIR__(), "..", "data", "test_systems.json"))

@info("Testing `bulk` and `repeat` against JuLIP reference data")
_ustripvecvec(X) = [ ustrip.(x) for x in X ]

test_systems = JSON.parsefile(joinpath(@__DIR__(), "..", "data", "test_systems.json"))
compare_system(sys_f, sys_j) = (
all( ustrip.( hcat(cell_vectors(sys_f)...) ) .≈ hcat(sys_j["cell"]...)' ) &&
all( AtomsBuilder._convert_pbc(sys_j["pbc"]) .== periodicity(sys_f) ) &&
all( atomic_number(sys_f, :) .== sys_j["Z"] ) &&
all( _ustripvecvec(position(sys_f, :)) .≈ sys_j["X"] ) )

_ustripvecvec(X) = [ ustrip.(x) for x in X ]
for D in test_systems
@testset "Testing $(D["sym"])" begin
if D["sym"] in ("Ti", )
cubic = false
else
cubic = D["cubic"]
end

compare_system(sys_f, sys_j) = (
all( ustrip.( hcat(cell_vectors(sys_f)...) ) .≈ hcat(sys_j["cell"]...)' ) &&
all( AtomsBuilder._convert_pbc(sys_j["pbc"]) .== periodicity(sys_f) ) &&
all( atomic_number(sys_f, :) .== sys_j["Z"] ) &&
all( _ustripvecvec(position(sys_f, :)) .≈ sys_j["X"] ) )
sys_f = bulk(Symbol(D["sym"]); cubic, pbc=D["pbc"])
nn = D["nn"] isa Integer ? D["nn"] : tuple(D["nn"]...)
if nn != 1
sys_f = sys_f * nn
end
@test compare_system(sys_f, D["sys"])
end
end
end

for D in test_systems
nn = D["nn"] isa Integer ? D["nn"] : tuple(D["nn"]...)
sys_f = bulk( Symbol(D["sym"]);
cubic = D["cubic"],
pbc = D["pbc"] )
if nn != 1
sys_f = sys_f * nn
end
@test compare_system(sys_f, D["sys"])
@testset "Test argument errors are raised" begin
@test_throws ArgumentError bulk(:Og) # Case where no bulk structure is known
@test_throws ArgumentError bulk(:Og) # Case where no bulk structure is known
@test_throws ArgumentError bulk(:Ti; cubic=true) # Can't make hcp crystals cubic
end

##
@testset "Execute code" begin
# not sure how to write a test for this, but at least it should execute
sys0 = rattle!(bulk(:C, cubic=true) * (2,3,4), 0.1u"Å")
sys1 = rattle!(bulk(:C, cubic=true) * (2,3,4), 0.1)
sys2 = rattle!(bulk(:C) * (2,3,4), 0.1)
rattle!(bulk(:C) * (2,3,4), 0.01u"nm")

X = position(sys1, :)
Xnew = [ x .+ 0.01u"Å" for x in X ]
sys3 = set_positions(sys1, Xnew)
@test position(sys3, :) == Xnew

# not sure how to write a test for this, but at least it should execute
sys0 = rattle!(bulk(:C, cubic=true) * (2,3,4), 0.1u"Å")
sys1 = rattle!(bulk(:C, cubic=true) * (2,3,4), 0.1)
sys2 = rattle!(bulk(:C) * (2,3,4), 0.1)
rattle!(bulk(:C) * (2,3,4), 0.01u"nm")

X = position(sys1, :)
Xnew = [ x .+ 0.01u"Å" for x in X ]
sys3 = set_positions(sys1, Xnew)
@test position(sys3, :) == Xnew

Z = atomic_number(sys1, :)
@test all(Z .== AtomsBuilder.Chemistry.atomic_number(:C))
zO = AtomsBuilder.Chemistry.atomic_number(:O)
Znew = copy(Z); Znew[3:5:end] .= zO
sys4 = set_elements(sys3, Znew)
@test all(atomic_number(sys4, :) .== Znew)

pold = deepcopy(sys4.particles)
deleteat!(sys4, 1:5)
@test position.(pold[6:end]) == position(sys4, :)
@test mass.(pold[6:end]) == mass(sys4, :)
@test atomic_number.(pold[6:end]) == atomic_number(sys4, :)


@test_throws ArgumentError bulk(:Og) # Case where no bulk structure is known
@test_throws ArgumentError bulk(:B) # Tetragonal, which is currently not implemented
Z = atomic_number(sys1, :)
@test all(Z .== AtomsBuilder.Chemistry.atomic_number(:C))
zO = AtomsBuilder.Chemistry.atomic_number(:O)
Znew = copy(Z); Znew[3:5:end] .= zO
sys4 = set_elements(sys3, Znew)
@test all(atomic_number(sys4, :) .== Znew)

pold = deepcopy(sys4.particles)
deleteat!(sys4, 1:5)
@test position.(pold[6:end]) == position(sys4, :)
@test mass.(pold[6:end]) == mass(sys4, :)
@test atomic_number.(pold[6:end]) == atomic_number(sys4, :)
end
2 changes: 1 addition & 1 deletion test/test_utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ using AtomsBuilder, Test, AtomsBase, Unitful, Random
##

Random.seed!(1234)
sys = bulk(:Ti, cubic=true) * 5
sys = bulk(:Ti, cubic=false) * 5
sys = randz!(sys, [ :Ti => 0.5, :O => 0.5 ])
Z = atomic_number(sys, :)
@test count( Z .== 8 ) / length(Z) > 0.4
Expand Down
Loading