From 3de5887623804fc725f94701a168261fa2da51d3 Mon Sep 17 00:00:00 2001 From: "Michael F. Herbst" Date: Thu, 15 Dec 2022 22:16:23 +0100 Subject: [PATCH 01/10] More detailed printing --- Project.toml | 1 + src/AtomsBase.jl | 2 +- src/atom.jl | 17 ++++--- src/atomview.jl | 5 +- src/fast_system.jl | 5 -- src/flexible_system.jl | 5 -- src/interface.jl | 20 +++++++- src/properties.jl | 18 ------- src/show.jl | 105 +++++++++++++++++++++++++++++++++++++++++ test/atom.jl | 1 + test/fast_system.jl | 4 +- test/interface.jl | 3 +- test/printing.jl | 12 +++-- 13 files changed, 150 insertions(+), 48 deletions(-) create mode 100644 src/show.jl diff --git a/Project.toml b/Project.toml index 6f2c812..d047c89 100644 --- a/Project.toml +++ b/Project.toml @@ -5,6 +5,7 @@ version = "0.2.4" [deps] PeriodicTable = "7b2266bf-644c-5ea3-82d8-af4bbd25a884" +Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" UnitfulAtomic = "a7773ee8-282e-5fa2-be4e-bd808c38a91a" diff --git a/src/AtomsBase.jl b/src/AtomsBase.jl index a8d5918..34a3308 100644 --- a/src/AtomsBase.jl +++ b/src/AtomsBase.jl @@ -1,11 +1,11 @@ module AtomsBase using Unitful using UnitfulAtomic -import PeriodicTable: elements using StaticArrays include("interface.jl") include("properties.jl") +include("show.jl") include("flexible_system.jl") include("atom.jl") include("atomview.jl") diff --git a/src/atom.jl b/src/atom.jl index 07dc24a..703c41f 100644 --- a/src/atom.jl +++ b/src/atom.jl @@ -19,8 +19,8 @@ position(atom::Atom) = atom.position atomic_mass(atom::Atom) = atom.atomic_mass atomic_symbol(atom::Atom) = atom.atomic_symbol atomic_number(atom::Atom) = atom.atomic_number -element(atom::Atom) = elements[atomic_symbol(atom)] -n_dimensions(atom::Atom{D}) where {D} = D +element(atom::Atom) = element(atomic_number(atom)) +n_dimensions(::Atom{D}) where {D} = D Base.hasproperty(at::Atom, x::Symbol) = hasfield(Atom, x) || haskey(at.data, x) Base.getproperty(at::Atom, x::Symbol) = hasfield(Atom, x) ? getfield(at, x) : getindex(at.data, x) @@ -35,9 +35,9 @@ end function Atom(identifier::AtomId, position::AbstractVector{L}, velocity::AbstractVector{V}=zeros(length(position))u"bohr/s"; - atomic_symbol=Symbol(elements[identifier].symbol), - atomic_number=elements[identifier].number, - atomic_mass::M=elements[identifier].atomic_mass, + atomic_symbol=Symbol(element(identifier).symbol), + atomic_number=element(identifier).number, + atomic_mass::M=element(identifier).atomic_mass, kwargs...) where {L <: Unitful.Length, V <: Unitful.Velocity, M <: Unitful.Mass} Atom{length(position), L, V, M}(position, velocity, atomic_symbol, atomic_number, atomic_mass, Dict(kwargs...)) @@ -61,10 +61,9 @@ function Base.convert(::Type{Atom}, id_pos::Pair{<:AtomId,<:AbstractVector{<:Uni Atom(id_pos...) end -function Base.show(io::IO, at::Atom{D, L}) where {D, L} - pos = ustrip.(at.position) - print(io, "Atom($(at.atomic_symbol), [", join(pos, ", "), "]u\"$(unit(L))\")") -end +Base.show(io::IO, at::Atom) = show_atom(io, at) +Base.show(io::IO, mime::MIME"text/plain", at::Atom) = show_atom(io, mime, at) + # # Special high-level functions to construct atomic systems diff --git a/src/atomview.jl b/src/atomview.jl index bd937bf..fdfa31b 100644 --- a/src/atomview.jl +++ b/src/atomview.jl @@ -11,5 +11,8 @@ position(v::AtomView) = position(v.system, v.index) atomic_mass(v::AtomView) = atomic_mass(v.system, v.index) atomic_symbol(v::AtomView) = atomic_symbol(v.system, v.index) atomic_number(v::AtomView) = atomic_number(v.system, v.index) -element(v::AtomView) = elements[atomic_symbol(v)] n_dimensions(v::AtomView) = n_dimensions(v.system) +element(atom::AtomView) = element(atomic_number(atom)) + +Base.show(io::IO, at::AtomView) = show_atom(io, at) +Base.show(io::IO, mime::MIME"text/plain", at::AtomView) = show_atom(io, mime, at) diff --git a/src/fast_system.jl b/src/fast_system.jl index 6120826..8cf91f2 100644 --- a/src/fast_system.jl +++ b/src/fast_system.jl @@ -41,11 +41,6 @@ function FastSystem(particles, box, boundary_conditions) atomic_number.(particles), atomic_mass.(particles)) end -function Base.show(io::IO, system::FastSystem) - print(io, "FastSystem") - show_system(io, system) -end - bounding_box(sys::FastSystem) = sys.box boundary_conditions(sys::FastSystem) = sys.boundary_conditions Base.length(sys::FastSystem) = length(sys.positions) diff --git a/src/flexible_system.jl b/src/flexible_system.jl index d80d355..fc523e8 100644 --- a/src/flexible_system.jl +++ b/src/flexible_system.jl @@ -40,11 +40,6 @@ function FlexibleSystem(system::AbstractSystem; end FlexibleSystem(;system::FlexibleSystem, kwargs...) = FlexibleSystem(system; kwargs...) -function Base.show(io::IO, system::FlexibleSystem) - print(io, "FlexibleSystem") - show_system(io, system) -end - bounding_box(sys::FlexibleSystem) = sys.box boundary_conditions(sys::FlexibleSystem) = sys.boundary_conditions species_type(sys::FlexibleSystem{D, S, L}) where {D, S, L} = S diff --git a/src/interface.jl b/src/interface.jl index cdf3c08..76f34f6 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -1,7 +1,8 @@ import Base.position +import PeriodicTable export AbstractSystem -export BoundaryCondition, DirichletZero, Periodic, infinite_box +export BoundaryCondition, DirichletZero, Periodic, infinite_box, isinfinite export bounding_box, boundary_conditions, periodicity, n_dimensions, species_type export position, velocity, element, atomic_mass, atomic_number, atomic_symbol @@ -52,6 +53,10 @@ function species_type end """Return vector indicating whether the system is periodic along a dimension.""" periodicity(sys::AbstractSystem) = [isa(bc, Periodic) for bc in boundary_conditions(sys)] +"""Returns true if the given system is infinite""" +isinfinite(sys::AbstractSystem{D}) where {D} = bounding_box(sys) == infinite_box(D) + + """ n_dimensions(::AbstractSystem) n_dimensions(atom) @@ -74,8 +79,9 @@ Base.iterate(sys::AbstractSystem, state = firstindex(sys)) = # # Species property accessors from systems and species # + """The element corresponding to a species/atom (or missing).""" -function element end +element(id::Union{Symbol,AbstractString,Integer}) = PeriodicTable.elements[id] """ @@ -126,6 +132,11 @@ atomic_mass(sys::AbstractSystem, index) = atomic_mass(sys[index]) Vector of atomic symbols in the system `sys` or the atomic symbol of a particular `species` / the `i`th species in `sys`. + +The intention is that [`atomic_number`](@ref) carries the meaning +of identifying the type of a `species` (e.g. the element for the case of an atom), whereas +[`atomic_symbol`](@ref) may return a more unique identifier. For example for a deuterium atom +this may be `:D` while `atomic_number` is still `1`. """ atomic_symbol(sys::AbstractSystem) = atomic_symbol.(sys) atomic_symbol(sys::AbstractSystem, index) = atomic_symbol(sys[index]) @@ -138,6 +149,11 @@ atomic_symbol(sys::AbstractSystem, index) = atomic_symbol(sys[index]) Vector of atomic numbers in the system `sys` or the atomic number of a particular `species` / the `i`th species in `sys`. + +The intention is that [`atomic_number`](@ref) carries the meaning +of identifying the type of a `species` (e.g. the element for the case of an atom), whereas +[`atomic_symbol`](@ref) may return a more unique identifier. For example for a deuterium atom +this may be `:D` while `atomic_number` is still `1`. """ atomic_number(sys::AbstractSystem) = atomic_number.(sys) atomic_number(sys::AbstractSystem, index) = atomic_number(sys[index]) diff --git a/src/properties.jl b/src/properties.jl index 5e859cd..d35d539 100644 --- a/src/properties.jl +++ b/src/properties.jl @@ -1,6 +1,5 @@ export chemical_formula - """ Returns the chemical formula of an AbstractSystem as a string. """ @@ -18,20 +17,3 @@ function chemical_formula(symbols::AbstractVector{Symbol}) join(sort(parts)) end chemical_formula(system) = chemical_formula(atomic_symbol(system)) - - -function show_system(io::IO, system::AbstractSystem{D}) where {D} - print(io, "($(chemical_formula(system)), ") - bc = boundary_conditions(system) - if all(isequal(bc[1]), bc) - print(io, typeof(bc[1]), ", ") - end - box = bounding_box(system) - if box != infinite_box(D) - box_str = ["[" * join(ustrip.(bvector), ", ") * "]" for bvector in box] - print(io, "box=[", join(box_str, ", "), "]u\"$(unit(box[1][1]))\"") - else - print(io, "box=infinite") - end - print(io, ")") -end diff --git a/src/show.jl b/src/show.jl new file mode 100644 index 0000000..f0ed98a --- /dev/null +++ b/src/show.jl @@ -0,0 +1,105 @@ +using Printf + +""" +Suggested function to print AbstractSystem objects to screen +""" +function show_system(io::IO, system::AbstractSystem{D}) where {D} + bc = boundary_conditions(system) + + print(io, typeof(system).name.name, "($(chemical_formula(system))") + if isinfinite(system) + print(io, ", infinite") + else + perstr = [p ? "T" : "F" for p in periodicity(system)] + print(io, ", periodic = ", join(perstr, "")) + end + + if !isinfinite(system) + box_str = ["[" * join(ustrip.(bvector), ", ") * "]" + for bvector in bounding_box(system)] + bunit = unit(eltype(first(bounding_box(system)))) + print(io, ", bounding_box = [", join(box_str, ", "), "]u\"$bunit\"") + end + print(io, ")") +end +function show_system(io::IO, ::MIME"text/plain", system::AbstractSystem{D}) where {D} + bc = boundary_conditions(system) + box = bounding_box(system) + print(io, typeof(system).name.name, "($(chemical_formula(system))") + if isinfinite(system) + print(io, ", infinite") + else + perstr = [p ? "T" : "F" for p in periodicity(system)] + print(io, ", periodic = ", join(perstr, "")) + end + println(io, "):") + + extra_line = false + if !isinfinite(system) + extra_line = true + box = bounding_box(system) + bunit = unit(eltype(first(bounding_box(system)))) + for (i, bvector) in enumerate(box) + if i == 1 + @printf io " %-17s : [" "bounding_box" + else + print(io, " "^25) + end + boxstr = [(@sprintf "%8.6g" ustrip(b)) for b in bvector] + print(io, join(boxstr, " ")) + println(io, i == D ? "]u\"$bunit\"" : ";") + end + end + + if system isa FlexibleSystem + for (k, v) in pairs(system.data) + extra_line = true + @printf io " %-17s : %s\n" string(k) string(v) + end + end + + # TODO Better would be some ascii-graphical representation of the structure + if length(system) < 20 + extra_line && println(io) + for atom in system + println(io, " ", atom) + end + end +end + +Base.show(io::IO, system::AbstractSystem) = show_system(io, system) +function Base.show(io::IO, mime::MIME"text/plain", system::AbstractSystem) + show_system(io, mime, system) +end + +function show_atom(io::IO, at) + pos = [(@sprintf "%8.6g" ustrip(p)) for p in position(at)] + posunit = unit(eltype(position(at))) + print(io, "Atom($(atomic_symbol(at)), [", join(pos, ", "), "]u\"$posunit\"") + if ismissing(velocity(at)) || iszero(velocity(at)) + print(io, ")") + else + vel = [(@sprintf "%8.6g" ustrip(p)) for p in velocity(at)] + velunit = unit(eltype(velocity(at))) + print(io, ", [", join(vel, ", "), "]u\"$velunit\")") + end +end + +function show_atom(io::IO, ::MIME"text/plain", at) + println(io, "Atom(", atomic_symbol(at), ", atomic_number = ", atomic_number(at), + ", atomic_mass = ", atomic_mass(at), "):") + + pos = [(@sprintf "%.8g" ustrip(p)) for p in position(at)] + posunit = unit(eltype(position(at))) + @printf io " %-17s : [%s]u\"%s\"\n" "position" join(pos, ",") string(posunit) + if !ismissing(velocity(at)) && !iszero(velocity(at)) + vel = [(@sprintf "%.8g" ustrip(p)) for p in velocity(at)] + velunit = unit(eltype(velocity(at))) + @printf io " %-17s : [%s]u\"%s\"\n" "velocity" join(vel, ",") string(velunit) + end + if at isa Atom + for (k, v) in pairs(at.data) + @printf io " %-17s : %s\n" string(k) string(v) + end + end +end diff --git a/test/atom.jl b/test/atom.jl index afb0804..eb0c8a1 100644 --- a/test/atom.jl +++ b/test/atom.jl @@ -13,6 +13,7 @@ using Test @test n_dimensions(at) == 3 @test position(at) == zeros(3) * u"m" @test velocity(at) == zeros(3) * u"bohr/s" + @test element(at) == element(:Si) @test at.atomic_symbol == :Si @test at.atomic_number == 14 @test hasproperty(at, :atomic_mass) diff --git a/test/fast_system.jl b/test/fast_system.jl index 9736d38..de93d5a 100644 --- a/test/fast_system.jl +++ b/test/fast_system.jl @@ -15,7 +15,8 @@ using PeriodicTable @test atomic_mass(system) == [12.011, 12.011]u"u" @test boundary_conditions(system) == bcs @test bounding_box(system) == box - + @test !isinfinite(system) + @test element(system[1]) == element(:C) # Test AtomView for method in (position, atomic_mass, atomic_symbol, atomic_number) @@ -24,5 +25,4 @@ using PeriodicTable end @test ismissing(velocity(system[1])) @test n_dimensions(system[1]) == n_dimensions(system) - @test element(system[1]) == elements[:C] end diff --git a/test/interface.jl b/test/interface.jl index 269a437..a450dff 100644 --- a/test/interface.jl +++ b/test/interface.jl @@ -14,10 +14,10 @@ using PeriodicTable @testset "Atoms" begin @test position(atoms[1]) == [0.25, 0.25, 0.25]u"m" @test velocity(atoms[1]) == [0.0, 0.0, 0.0]u"bohr/s" - @test element(atoms[1]) == PeriodicTable.elements[:C] @test atomic_symbol(atoms[1]) == :C @test atomic_number(atoms[1]) == 6 @test atomic_mass(atoms[1]) == 12.011u"u" + @test element(atoms[1]) == element(:C) end @testset "System" begin @@ -29,6 +29,7 @@ using PeriodicTable @test bounding_box(flexible) == [[1, 0, 0], [0, 1, 0], [0, 0, 1]]u"m" @test boundary_conditions(flexible) == [Periodic(), Periodic(), DirichletZero()] @test periodicity(flexible) == [1, 1, 0] + @test !isinfinite(flexible) @test n_dimensions(flexible) == 3 @test position(flexible) == [[0.25, 0.25, 0.25], [0.75, 0.75, 0.75]]u"m" @test position(flexible, 1) == [0.25, 0.25, 0.25]u"m" diff --git a/test/printing.jl b/test/printing.jl index 68b53c5..2fdfe32 100644 --- a/test/printing.jl +++ b/test/printing.jl @@ -4,17 +4,21 @@ using Test @testset "Printing atomic systems" begin at = Atom(:Si, zeros(3) * u"m", extradata=42) - @test repr(at) == "Atom(Si, [0.0, 0.0, 0.0]u\"m\")" - + @test repr(at) == "Atom(Si, [ 0, 0, 0]u\"m\")" + show(stdout, MIME("text/plain"), at) + atoms = [:Si => [0.0, -0.125, 0.0], :C => [0.125, 0.0, 0.0]] box = [[10, 0.0, 0.0], [0.0, 5, 0.0], [0.0, 0.0, 7]]u"Å" flexible_system = periodic_system(atoms, box; fractional=true) @test repr(flexible_system) == """ - FlexibleSystem(CSi, Periodic, box=[[10.0, 0.0, 0.0], [0.0, 5.0, 0.0], [0.0, 0.0, 7.0]]u"Å")""" + FlexibleSystem(CSi, periodic = TTT, bounding_box = [[10.0, 0.0, 0.0], [0.0, 5.0, 0.0], [0.0, 0.0, 7.0]]u"Å")""" + show(stdout, MIME("text/plain"), flexible_system) fast_system = FastSystem(flexible_system) @test repr(fast_system) == """ - FastSystem(CSi, Periodic, box=[[10.0, 0.0, 0.0], [0.0, 5.0, 0.0], [0.0, 0.0, 7.0]]u"Å")""" + FastSystem(CSi, periodic = TTT, bounding_box = [[10.0, 0.0, 0.0], [0.0, 5.0, 0.0], [0.0, 0.0, 7.0]]u"Å")""" + show(stdout, MIME("text/plain"), fast_system) + show(stdout, MIME("text/plain"), fast_system[1]) end From b750999414afafe909b242cd7f341173a7144e1b Mon Sep 17 00:00:00 2001 From: "Michael F. Herbst" Date: Fri, 16 Dec 2022 08:51:39 +0100 Subject: [PATCH 02/10] Update apireference.md --- docs/src/apireference.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/apireference.md b/docs/src/apireference.md index 120261c..5b5778f 100644 --- a/docs/src/apireference.md +++ b/docs/src/apireference.md @@ -22,6 +22,7 @@ chemical_formula boundary_conditions bounding_box element +isinfinite n_dimensions periodicity species_type From acaf9340cf153ec94dc025f093ac5ae6dc4e2a04 Mon Sep 17 00:00:00 2001 From: "Michael F. Herbst" Date: Sat, 17 Dec 2022 10:47:00 +0100 Subject: [PATCH 03/10] Cosmetic fixes --- src/atom.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/atom.jl b/src/atom.jl index 703c41f..c44e0d3 100644 --- a/src/atom.jl +++ b/src/atom.jl @@ -82,7 +82,7 @@ isolated_system(atoms::AbstractVector; kwargs...) = isolated_system(convert.(Ato function periodic_system(atoms::AbstractVector, box::AbstractVector{<:AbstractVector}; fractional=false, kwargs...) - boundary_conditions=fill(Periodic(), length(box)) + boundary_conditions = fill(Periodic(), length(box)) lattice = hcat(box...) !fractional && return atomic_system(atoms, box, boundary_conditions; kwargs...) From b621cdedc54dcbd95edca5574702bae570ecdbdb Mon Sep 17 00:00:00 2001 From: "Michael F. Herbst" Date: Sat, 17 Dec 2022 22:02:10 +0100 Subject: [PATCH 04/10] Update documentation --- README.md | 47 +++++----- docs/make.jl | 3 +- docs/src/apireference.md | 37 +++++--- docs/src/atomicsystems.md | 84 ------------------ docs/src/overview.md | 28 +++++- docs/src/tutorial.md | 177 ++++++++++++++++++++++++++++++++++++++ src/atom.jl | 90 ++++++++++++++++++- src/flexible_system.jl | 36 ++++++-- src/show.jl | 2 +- test/atom.jl | 3 +- 10 files changed, 375 insertions(+), 132 deletions(-) delete mode 100644 docs/src/atomicsystems.md create mode 100644 docs/src/tutorial.md diff --git a/README.md b/README.md index 1776704..bfe2ae6 100644 --- a/README.md +++ b/README.md @@ -7,44 +7,43 @@ [![Build Status](https://github.com/JuliaMolSim/AtomsBase.jl/workflows/CI/badge.svg)](https://github.com/JuliaMolSim/AtomsBase.jl/actions) [![Coverage](https://codecov.io/gh/JuliaMolSim/AtomsBase.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/JuliaMolSim/AtomsBase.jl) -**AtomsBase is currently in the relatively early stages of development and we very much -want developer/user input! If you think anything about it should be -added/removed/changed, _please_ [file an issue](https://github.com/JuliaMolSim/AtomsBase.jl/issues) or chime into the discussion on an -existing one! (Look particularly for issues with the [`question` label](https://github.com/JuliaMolSim/AtomsBase.jl/issues?q=is%3Aissue+is%3Aopen+label%3Aquestion))** - -AtomsBase is an abstract interface for representation of atomic geometries in Julia. It aims to be a lightweight means of facilitating interoperability between various tools including... -* chemical simulation engines (e.g. density functional theory, molecular dynamics, etc.) -* file I/O with standard formats (.cif, .xyz, ...) -* numerical tools: sampling, integration schemes, etc. +AtomsBase is an abstract interface for representation of atomic geometries in +Julia. It aims to be a lightweight means of facilitating interoperability +between various tools including ... + +* Chemical simulation engines: + - [DFTK.jl](https://github.com/JuliaMolSim/DFTK.jl) (density-functional theory) + - [Molly.jl](https://github.com/JuliaMolSim/Molly.jl) (molecular dynamics) +* Integration with third party-libraries: + - [ASEconvert.jl](https://github.com/mfherbst/ASEconvert.jl) (integration with the Atomistic Simulation Environment) +* I/O with standard file formats (.cif, .xyz, ...) + - E.g. [AtomIO.jl](https://github.com/mfherbst/AtomIO.jl) * automatic differentiation and machine learning systems + - [ChemistryFeaturization.jl](https://github.com/Chemellia/ChemistryFeaturization.jl) + (featurization of atomic systems) +* numerical tools: sampling, integration schemes, etc. * visualization (e.g. plot recipes) -Currently, the design philosophy is to be as lightweight as possible, with only -a small set of required function dispatches to make adopting the interface into -existing packages easy. We also provide a couple of +Currently, the design philosophy is to be as lightweight as possible with a small set +of required function dispatches to make adopting the interface easy. +We also provide a couple of [standard flexible implementations of the interface](https://juliamolsim.github.io/AtomsBase.jl/stable/atomicsystems/#atomic-systems) that we envision to be broadly applicable. If features beyond these are required we encourage developers to open PRs or provide their own implementations. -For more on how to use the package, see [the documentation](https://juliamolsim.github.io/AtomsBase.jl/stable). - -## Installation - -AtomsBase can be installed using the Julia package manager. -From the Julia REPL, type `]` to enter the Pkg REPL mode and run - -``` -pkg> add AtomsBase -``` +For more on how to use the package, +see [the documentation](https://juliamolsim.github.io/AtomsBase.jl/stable). -## Packages Using AtomsBase -The following (not all yet-registered) packages currently make use of this interface (please feel free to send a PR to add to this list!): +## Packages using AtomsBase +The following (not all yet-registered) packages currently make use of AtomsBase: * [ASEPotential](https://github.com/jrdegreeff/ASEPotential.jl) +* [ASEconvert](https://github.com/mfherbst/ASEconvert.jl) * [AtomIO](https://github.com/mfherbst/AtomIO.jl): I/O for atomic structures, also wraps some ASE functionality * [Atomistic](https://github.com/cesmix-mit/Atomistic.jl/tree/263ec97b5f380f1b2ba593bf8feaf36e7f7cff9a): integrated workflow for MD simulations, part of [CESMIX](https://computing.mit.edu/cesmix/) * [BFPIS](https://github.com/GDufenshuoo/BFPIS.jl) * [ChemistryFeaturization](https://github.com/Chemellia/ChemistryFeaturization.jl): Interface for featurization of atomic structures for input into machine learning models, part of [Chemellia](https://chemellia.org) * [DFTK](https://github.com/JuliaMolSim/DFTK.jl): density functional theory simulations +* [ExtXYZ](https://github.com/libAtoms/ExtXYZ.jl): Parser for extended XYZ files * [InteratomicPotentials](https://github.com/cesmix-mit/InteratomicPotentials.jl): implementations of a variety of interatomic potentials, also part of [CESMIX](https://computing.mit.edu/cesmix/) * [Molly](https://github.com/JuliaMolSim/Molly.jl): molecular dynamics simulations * [Xtals](https://github.com/SimonEnsemble/Xtals.jl): I/O and structure representation for crystals diff --git a/docs/make.jl b/docs/make.jl index e6b52fa..ecd2329 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -11,12 +11,13 @@ makedocs(; format=Documenter.HTML(; prettyurls=get(ENV, "CI", "false") == "true", canonical="https://juliamolsim.github.io/AtomsBase.jl", + edit_link="master", assets=String[], ), pages=[ "Home" => "index.md", + "tutorial.md", "overview.md", - "atomicsystems.md", "apireference.md" ], checkdocs=:exports, diff --git a/docs/src/apireference.md b/docs/src/apireference.md index 5b5778f..51ad32e 100644 --- a/docs/src/apireference.md +++ b/docs/src/apireference.md @@ -1,3 +1,7 @@ +```@meta +CurrentModule = AtomsBase +``` + # API reference ## Index @@ -6,26 +10,35 @@ Pages = ["apireference.md"] ``` -## Types - +## System properties ```@docs -AbstractSystem{D} +boundary_conditions +bounding_box +chemical_formula +isinfinite +n_dimensions +periodicity +species_type ``` -## Functions +## Species / atom properties ```@docs atomic_mass atomic_number atomic_symbol -chemical_formula -boundary_conditions -bounding_box -element -isinfinite -n_dimensions -periodicity -species_type velocity position +element +``` + +## Atom and system constructors + +```@docs +Atom +FlexibleSystem +AbstractSystem +atomic_system +isolated_system +periodic_system ``` diff --git a/docs/src/atomicsystems.md b/docs/src/atomicsystems.md deleted file mode 100644 index e2de1ca..0000000 --- a/docs/src/atomicsystems.md +++ /dev/null @@ -1,84 +0,0 @@ -# [Atomic systems](@id atomic-systems) -Since we anticipate atomic systems to be a commonly needed representation, -`AtomsBase` provides two flexible implementations for this setting. -One implementation follows the struct-of-arrays approach introducing the `AtomView` -type to conveniently expose atomic data. -The more flexible implementation is based on an array-of-structs approach -and can be easily customised, e.g. by adding custom properties or by swapping -the underlying `Atom` struct by a custom one. -In both cases the respective datastructures can be used either fully -or in parts in downstream packages and we hope these to develop into universally -useful types within the Julia ecosystem over time. - -## Struct of Arrays / FastSystem -The file [src/fast_system.jl](https://github.com/JuliaMolSim/AtomsBase.jl/blob/master/src/fast_system.jl) contains an implementation of -AtomsBase based on the struct-of-arrays approach. All species data is stored -as plain arrays, but for convenience indexing of individual atoms is supported -by a light-weight `AtomView`. See the implementation files -as well as the tests for how these can be used. - -## Atoms and FlexibleSystem -A flexible implementation of the interface is provided by the -`FlexibleSystem` and the `Atom` structs -for representing atomic systems. - -An `Atom` object can be constructed -just by passing an identifier (e.g. symbol like `:C`, atomic number like `6`) and a vector -of positions as -```julia -atom = Atom(:C, [0, 1, 2.]u"bohr") -``` -This automatically fills the atom with standard data such as the atomic mass. Such data -can be accessed using the `AtomsBase` interface functions -such as `atomic_mass(atom)`, `position(atom)`, `velocity(atom)`, `atomic_mass(atom)`, etc. -See [src/atom.jl](https://github.com/JuliaMolSim/AtomsBase.jl/blob/master/src/atom.jl) for details. - -Custom properties can be easily attached to an `Atom` by supplying arbitrary -keyword arguments upon construction. For example to attach a pseudopotential -for using the structure with [DFTK](https://dftk.org), construct the atom as -```julia -atom = Atom(:C, [0, 1, 2.]u"bohr", pseudopotential="hgh/lda/c-q4") -``` -which will make the pseudopotential identifier available as `atom.pseudopotential`. -Updating an atomic property proceeds similarly. E.g. -```julia -newatom = Atom(;atom=atom, atomic_mass=13u"u") -``` -makes a new carbon atom with all properties identical to `atom` (including custom ones), -but setting the `atomic_mass` to 13 units. - -Once the atoms are constructed these can be assembled into a system. -For example to place a hydrogen molecule into a cubic box of `10Å` and periodic -boundary conditions, use: -```julia -bounding_box = [[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 10.0]]u"Å" -boundary_conditions = [Periodic(), Periodic(), Periodic()] -hydrogen = FlexibleSystem([Atom(:H, [0, 0, 1.]u"bohr"), - Atom(:H, [0, 0, 3.]u"bohr")], - bounding_box, boundary_conditions) -``` -An update constructor is supported as well (see [src/flexible_system.jl](https://github.com/JuliaMolSim/AtomsBase.jl/blob/master/src/flexible_system.jl)). - -Oftentimes more convenient are the functions -`atomic_system`, `isolated_system`, `periodic_system`, -which cover some standard atomic system setups: -```julia -# Same hydrogen system with periodic BCs: -bounding_box = [[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 10.0]]u"Å" -hydrogen = periodic_system([:H => [0, 0, 1.]u"bohr", - :H => [0, 0, 3.]u"bohr"], - bounding_box) - -# Silicon unit cell using fractional positions -# (Common for solid-state simulations) -bounding_box = 10.26 / 2 * [[0, 0, 1], [1, 0, 1], [1, 1, 0]]u"bohr" -silicon = periodic_system([:Si => ones(3)/8, - :Si => -ones(3)/8], - bounding_box, fractional=true) - -# Isolated H2 molecule in vacuum (Infinite box and zero dirichlet BCs) -# (Standard setup for molecular simulations) -hydrogen = isolated_system([:H => [0, 0, 1.]u"bohr", - :H => [0, 0, 3.]u"bohr"]) - -``` diff --git a/docs/src/overview.md b/docs/src/overview.md index f453fd0..bbce24d 100644 --- a/docs/src/overview.md +++ b/docs/src/overview.md @@ -47,7 +47,7 @@ position(sys, i) # position of `i`th particle in `sys` Currently, this syntax only supports linear indexing. To simplify working with `AtomsBase`, default implementations for systems -composed of atoms are provided (see [Atomic systems](@ref atomic-systems)). +composed of atoms are provided (see [Tutorial](@ref)). ## Struct-of-Arrays vs. Array-of-Structs The "struct-of-arrays" (SoA) vs. "array-of-structs" (AoS) is a common design @@ -66,9 +66,33 @@ positions, then index into it for a single particle). The beauty of an abstract interface in Julia is that these details can be, in large part, abstracted away through method dispatch such that the end user sees the same expected behavior irrespective of how things are implemented "under the hood". -For concrete implementations see the section on [atomic systems](@ref atomic-systems). ## Boundary Conditions Finally, we include support for defining boundary conditions. Currently included are `Periodic` and `DirichletZero`. There should be one boundary condition specified for each spatial dimension represented. + +## Atomic system +Since we anticipate atomic systems to be a commonly needed representation, +`AtomsBase` provides two flexible implementations for this setting. +One implementation follows the struct-of-arrays approach introducing the `AtomView` +type to conveniently expose atomic data. +The more flexible implementation is based on an array-of-structs approach +and can be easily customised, e.g. by adding custom properties or by swapping +the underlying `Atom` struct by a custom one. +In both cases the respective datastructures can be used either fully +or in parts in downstream packages and we hope these to develop into universally +useful types within the Julia ecosystem over time. + +### Struct of Arrays / FastSystem +The file [src/fast_system.jl](https://github.com/JuliaMolSim/AtomsBase.jl/blob/master/src/fast_system.jl) contains an implementation of +AtomsBase based on the struct-of-arrays approach. All species data is stored +as plain arrays, but for convenience indexing of individual atoms is supported +by a light-weight `AtomView`. See the implementation files +as well as the tests for how these can be used. + +### Atoms and FlexibleSystem +A flexible implementation of the interface is provided by the +`FlexibleSystem` and the `Atom` structs +for representing atomic systems. +These are discussed in detail in [Tutorial](@ref). diff --git a/docs/src/tutorial.md b/docs/src/tutorial.md new file mode 100644 index 0000000..91762e3 --- /dev/null +++ b/docs/src/tutorial.md @@ -0,0 +1,177 @@ +# Tutorial + +This page gives an overview of using `AtomsBase` in practice and introduces +the conventions followed across the `AtomsBase` ecosystem. +It serves as a reference for both users interested in doing something +with an [`AbstractSystem`](@ref) object as well as developers wishing to integrate +their code with `AtomsBase`. + +For the examples we will mostly draw on the case of atomistic systems using the +[`FlexibleSystem`](@ref) data structure. See [Overview](@ref) for a more general +perspective of the `AtomsBase` interface. In practice we expect that the +[`Atom`](@ref) and [`FlexibleSystem`](@ref) data structure we focus on here +should provide good defaults for most purposes. + +## High-level introduction +The main purpose of AtomsBase is to conveniently pass atomistic data between Julia packages. +For example the following snippet loads an extxyz file +using [AtomsIO](https://github.com/mfherbst/AtomsIO.jl) +and returns it as an `AtomsBase`-compatible system (in `data`): +```julia +using AtomsIO +data = load_system("Si.extxyz") +``` +Next we use [ASEconvert](https://github.com/mfherbst/ASEconvert.jl) to convert +this system to python, such that we can make use of the +[atomistic simulation environment (ASE)](https://wiki.fysik.dtu.dk/ase/) +to form a `(2, 1, 1)` supercell, which is afterwards converted back +to Julia (by forming another `AtomsBase`-compatible system). +```julia +using ASEconvert +supercell = pyconvert(AbstractSystem, data * pytuple((2, 1, 1))) +``` +Finally the `supercell` is passed to [DFTK](https://dftk.org), +where we attach pseudopotentials and run a PBE calculation: +```julia +using DFTK +cell_with_pseudos = attach_psp(supercell, Si="hgh/pbe/si-q4") +model = model_PBE(cell_with_pseudos) +basis = PlaneWaveBasis(model, Ecut=30, kgrid=(5, 5, 5) +self_consistent_field(basis).energy.total +``` +For more high-level examples see also: +- The [DFTK documentation page on AtomsBase](https://docs.dftk.org/stable/examples/atomsbase/). +- The [AtomsIO documentation](https://mfherbst.github.io/AtomsIO.jl/stable) + +## Atom interface and conventions +An `Atom` object can be constructed +just by passing an identifier (e.g. symbol like `:C`, atomic number like `6`) and a vector +of positions as +````@example atom +using Unitful, UnitfulAtomic, AtomsBase # hide +atom = Atom(:C, [0, 1, 2.]u"bohr") +```` +This automatically fills the atom with standard data such as the atomic mass. +See also the reference of the [`Atom`](@ref) function for more ways to construct an atom. + +Such data can be accessed using the `AtomsBase` interface functions, such as: +````@example atom +atomic_mass(atom) +```` +````@example atom +atomic_symbol(atom) +```` +````@example atom +atomic_number(atom) +```` +````@example atom +position(atom) +```` +````@example atom +velocity(atom) +```` +See [src/atom.jl](https://github.com/JuliaMolSim/AtomsBase.jl/blob/master/src/atom.jl) +and the respective API documentation for details. +Notice in particular that [`atomic_number`](@ref) will the element, i.e. the type +of an atom, whereas [`atomic_symbol`](@ref) may be more specific and may e.g. uniquely specify +a precise atom in the structure. An example could be a deuterium atom +````@example +using Unitful, UnitfulAtomic, AtomsBase # hide +deuterium = Atom(1, atomic_symbol=:D, [0, 1, 2.]u"bohr") +```` + +### Optional atomic properties +Custom properties can be easily attached to an `Atom` by supplying arbitrary +keyword arguments upon construction. For example to attach a pseudopotential +for using the structure with [DFTK](https://dftk.org), construct the atom as +````@example atomprop +using Unitful, UnitfulAtomic, AtomsBase # hide +atom = Atom(:C, [0, 1, 2.]u"bohr", pseudopotential="hgh/lda/c-q4") +```` +which will make the pseudopotential identifier available as `atom.pseudopotential`. +Updating an atomic property proceeds similarly. E.g. +````@example atomprop +using Unitful, UnitfulAtomic, AtomsBase # hide +newatom = Atom(;atom=atom, atomic_mass=13u"u") +```` +makes a new carbon atom with all properties identical to `atom` (including custom ones), +but setting the `atomic_mass` to 13 units. + +To simplify interoperability some optional properties are reserved. For these: +- Throughout the `AtomsBase` ecosystem these property keys carry a well-defined meaning. + I.e. if they are supported by a code, the code expects them to hold the data specified below. +- If a consuming code cannot make use of these properties, it should at least warn the user about it. + For example if a library or simulation code does not support such a feature and drops the respective + information it should `@warn` or (even better) interrupt execution with an error. + +Property name | Unit / Type | Description +:------------------ | :----------------- | :--------------------- +`:charge` | `Charge` | Net charge of the atom +`:covalent_radius` | `Length` | Covalent radius tabulated for the atom +`:vdw_radius` | `Length` | VdW radius tabulated for the atom +`:magnetic_moments` | `Union{Float64,Vector{Float64}}` | Initial magnetic moment +`:pseudopotential` | `String` | Pseudopotential or PAW keyword or `""` if Coulomb potential employed + + +## System interface and conventions +Once the atoms are constructed these can be assembled into a system. +For example to place a hydrogen molecule into a cubic box of `10Å` and periodic +boundary conditions, use: +````@example system +using Unitful, UnitfulAtomic, AtomsBase # hide +bounding_box = [[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 10.0]]u"Å" +boundary_conditions = [Periodic(), Periodic(), Periodic()] +hydrogen = FlexibleSystem([Atom(:H, [0, 0, 1.]u"bohr"), + Atom(:H, [0, 0, 3.]u"bohr")], + bounding_box, boundary_conditions) +```` +An update constructor for systems is supported as well (see [`AbstractSystem`](@ref)). For example +````@example system +AbstractSystem(hydrogen; bounding_box=[[5.0, 0.0, 0.0], [0.0, 5.0, 0.0], [0.0, 0.0, 5.0]]u"Å") +```` +Note that `FlexibleSystem( ... )` would have worked as well in this example (since we are +updating a `FlexibleSystem`). However, using the `AbstractSystem` constructor to update the system +is more general as it allows for type-specific dispatching when updating other data structures +implementing the `AbstractSystem` interface. + +Oftentimes more convenient are the functions +[`atomic_system`](@ref), [`isolated_system`](@ref), [`periodic_system`](@ref), +which cover some standard atomic system setups. +For example to setup a hydrogen system with periodic BCs, we can issue +````@example +using Unitful, UnitfulAtomic, AtomsBase # hide +bounding_box = [[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 10.0]]u"Å" +hydrogen = periodic_system([:H => [0, 0, 1.]u"bohr", + :H => [0, 0, 3.]u"bohr"], + bounding_box) +```` +To setup a silicon unit cell we can use fractional coordinates +(which is common for solid-state simulations): +````@example +using Unitful, UnitfulAtomic, AtomsBase # hide +bounding_box = 10.26 / 2 * [[0, 0, 1], [1, 0, 1], [1, 1, 0]]u"bohr" +silicon = periodic_system([:Si => ones(3)/8, + :Si => -ones(3)/8], + bounding_box, fractional=true) +```` +Alternatively we can also place an isolated H2 molecule in vacuum +(Infinite box and zero dirichlet BCs), which is the standard setup for +molecular simulations: +````@example +using Unitful, UnitfulAtomic, AtomsBase # hide +hydrogen = isolated_system([:H => [0, 0, 1.]u"bohr", + :H => [0, 0, 3.]u"bohr"]) +```` + +### Optional system properties +Similar to atoms, systems also support storing arbitrary data, for example +```@example +using Unitful, UnitfulAtomic, AtomsBase # hide +hydrogen = isolated_system([:H => [0, 0, 1.]u"bohr", :H => [0, 0, 3.]u"bohr"]; extra_data=42) +``` +Again to simplify interoperability some optional properties are reserved, namely: + +Property name | Unit / Type | Description +:-------------- | :----------------- | :--------------------- +`:charge` | `Charge` | Total net system charge +`:multiplicity` | `Int` (unitless) | Multiplicity of the ground state targeted in the calculation diff --git a/src/atom.jl b/src/atom.jl index c44e0d3..9141e3d 100644 --- a/src/atom.jl +++ b/src/atom.jl @@ -32,6 +32,16 @@ function Base.propertynames(at::Atom, private::Bool=false) end end +""" + Atom(identifier::AtomId, position::AbstractVector; kwargs...) + Atom(identifier::AtomId, position::AbstractVector, velocity::AbstractVector; kwargs...) + +Construct an atomic located at the cartesian coordinates `position` with (optionally) +the given cartesian `velocity`. Note that `AtomId = Union{Symbol,AbstractString,Integer}`. + +Supported `kwargs` include `atomic_symbol`, `atomic_number`, `atomic_mass`, `charge`, +`multiplicity` as well as user-specific custom properties. +""" function Atom(identifier::AtomId, position::AbstractVector{L}, velocity::AbstractVector{V}=zeros(length(position))u"bohr/s"; @@ -46,10 +56,31 @@ function Atom(id::AtomId, position::AbstractVector, velocity::Missing; kwargs... Atom(id, position; kwargs...) end -# Update constructor: Amend any atom by extra data. +""" + Atom(atom::Atom; kwargs...) + Atom(; atom, kwargs...) + +Update constructor. Construct a new `Atom`, by amending the data contained +in the passed `atom` object. Note that the first version only works if `atom` is an `Atom`, +while the second version works on arbitrary species adhering to the `AtomsBase` conventions. +In library code the second version should therefore be preferred. + +Supported `kwargs` include `atomic_symbol`, `atomic_number`, `atomic_mass`, `charge`, +`multiplicity` as well as user-specific custom properties. + +# Examples +Construct a standard hydrogen atom located at the origin +```julia-repl +julia> hydrogen = Atom(:H, zeros(3)u"Å") +``` +and now amend its charge and atomic mass +```julia-repl +julia> Atom(; atom, atomic_mass=1.0u"u", charge=-1.0u"e_au") +``` +""" function Atom(;atom, kwargs...) extra = atom isa Atom ? atom.data : (; ) - Atom(atomic_symbol(atom), position(atom), velocity(atom); + Atom(atomic_number(atom), position(atom), velocity(atom); atomic_symbol=atomic_symbol(atom), atomic_number=atomic_number(atom), atomic_mass=atomic_mass(atom), @@ -68,10 +99,39 @@ Base.show(io::IO, mime::MIME"text/plain", at::Atom) = show_atom(io, mime, at) # # Special high-level functions to construct atomic systems # + +""" + atomic_system(atoms::AbstractVector, bounding_box, boundary_conditions; kwargs...) + +Construct a [`FlexibleSystem`](@ref) using the passed `atoms` and boundary box and conditions. +Extra `kwargs` are stored as custom system properties. + +# Examples +Construct a hydrogen molecule in a box, which is periodic only in the first two dimensions +```julia-repl +julia> bounding_box = [[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 10.0]]u"Å" +julia> boundary_conditions = [Periodic(), Periodic(), DirichletZero()] +julia> hydrogen = atomic_system([:H => [0, 0, 1.]u"bohr", + :H => [0, 0, 3.]u"bohr"], + bounding_box, boundary_conditions) +``` +""" atomic_system(atoms::AbstractVector{<:Atom}, box, bcs; kwargs...) = FlexibleSystem(atoms, box, bcs; kwargs...) atomic_system(atoms::AbstractVector, box, bcs; kwargs...) = FlexibleSystem(convert.(Atom, atoms), box, bcs; kwargs...) +""" + isolated_system(atoms::AbstractVector; kwargs...) + +Construct a [`FlexibleSystem`](@ref) by placing the passed `atoms` into an infinite vacuum +(standard setup for modelling molecular systems). Extra `kwargs` are stored as custom system properties. + +# Examples +Construct a hydrogen molecule +```julia-repl +julia> hydrogen = isolated_system([:H => [0, 0, 1.]u"bohr", :H => [0, 0, 3.]u"bohr"]) +``` +""" function isolated_system(atoms::AbstractVector{<:Atom}; kwargs...) # Use dummy box and boundary conditions D = n_dimensions(first(atoms)) @@ -79,6 +139,32 @@ function isolated_system(atoms::AbstractVector{<:Atom}; kwargs...) end isolated_system(atoms::AbstractVector; kwargs...) = isolated_system(convert.(Atom, atoms); kwargs...) + +""" + periodic_system(atoms::AbstractVector, bounding_box; fractional=false, kwargs...) + +Construct a [`FlexibleSystem`](@ref) with all boundaries of the `bounding_box` periodic +(standard setup for modelling solid-state systems). If `fractional` is true, atom coordinates +are given in fractional (and not in Cartesian) coordinates. +Extra `kwargs` are stored as custom system properties. + +# Examples +Setup a hydrogen molecule inside periodic BCs: +```julia-repl +julia> bounding_box = [[10.0, 0.0, 0.0], [0.0, 10.0, 0.0], [0.0, 0.0, 10.0]]u"Å" +julia> hydrogen = periodic_system([:H => [0, 0, 1.]u"bohr", + :H => [0, 0, 3.]u"bohr"], + bounding_box) +``` + +Setup a silicon unit cell using fractional positions +```julia-repl +julia> bounding_box = 10.26 / 2 * [[0, 0, 1], [1, 0, 1], [1, 1, 0]]u"bohr" +julia> silicon = periodic_system([:Si => ones(3)/8, + :Si => -ones(3)/8], + bounding_box, fractional=true) +``` +""" function periodic_system(atoms::AbstractVector, box::AbstractVector{<:AbstractVector}; fractional=false, kwargs...) diff --git a/src/flexible_system.jl b/src/flexible_system.jl index fc523e8..8c73548 100644 --- a/src/flexible_system.jl +++ b/src/flexible_system.jl @@ -14,6 +14,12 @@ end Base.hasproperty(system::FlexibleSystem, x::Symbol) = hasfield(FlexibleSystem, x) || haskey(system.data, x) Base.getproperty(system::FlexibleSystem, x::Symbol) = hasfield(FlexibleSystem, x) ? getfield(system, x) : getindex(system.data, x) +""" + FlexibleSystem(particles, box, boundary_conditions; kwargs...) + +Construct a flexible system, a versatile data structure for atomistic systems, +which puts an emphasis on flexibility rather than speed. +""" function FlexibleSystem( particles::AbstractVector{S}, box::AbstractVector{<:AbstractVector{L}}, @@ -25,20 +31,40 @@ function FlexibleSystem( throw(ArgumentError("Box must have D vectors of length D")) end FlexibleSystem{D, S, L}(convert.(Atom, particles), box, boundary_conditions, Dict(kwargs...)) - end -# Update constructor +""" + FlexibleSystem(system; kwargs...) + +Update constructor. See [`AbstractSystem`](@ref) for details. +""" function FlexibleSystem(system::AbstractSystem; particles=nothing, atoms=nothing, - box=bounding_box(system), + bounding_box=bounding_box(system), boundary_conditions=boundary_conditions(system), kwargs...) particles = something(particles, atoms, collect(system)) extra = system isa FlexibleSystem ? system.data : (; ) - FlexibleSystem(particles, box, boundary_conditions; extra..., kwargs...) + FlexibleSystem(particles, bounding_box, boundary_conditions; extra..., kwargs...) end -FlexibleSystem(;system::FlexibleSystem, kwargs...) = FlexibleSystem(system; kwargs...) + +""" + AbstractSystem(system::AbstractSystem; kwargs...) + +Update constructor. Construct a new system where one or more properties are changed, +which are given as `kwargs`. A subtype of `AbstractSystem` is returned, by default +a `FlexibleSystem`, but depending on the type of the passed system this might differ. + +Supported `kwargs` include `particles`, `atoms`, `bounding_box` and `boundary_conditions` +as well as user-specific custom properties. + +# Examples +Change the bounding box and the atoms of the passed system +```julia-repl +julia> AbstractSystem(system; bounding_box= ..., atoms = ... ) +``` +""" +AbstractSystem(system::AbstractSystem; kwargs...) = FlexibleSystem(system; kwargs...) bounding_box(sys::FlexibleSystem) = sys.box boundary_conditions(sys::FlexibleSystem) = sys.boundary_conditions diff --git a/src/show.jl b/src/show.jl index f0ed98a..bf4f57f 100644 --- a/src/show.jl +++ b/src/show.jl @@ -59,7 +59,7 @@ function show_system(io::IO, ::MIME"text/plain", system::AbstractSystem{D}) wher end # TODO Better would be some ascii-graphical representation of the structure - if length(system) < 20 + if length(system) < 10 extra_line && println(io) for atom in system println(io, " ", atom) diff --git a/test/atom.jl b/test/atom.jl index eb0c8a1..3714065 100644 --- a/test/atom.jl +++ b/test/atom.jl @@ -55,8 +55,9 @@ using Test # Test update constructor newatoms = [system[1], system[2]] - newsystem = FlexibleSystem(system; atoms=newatoms, + newsystem = AbstractSystem(system; atoms=newatoms, boundary_conditions=[Periodic(), Periodic(), Periodic()]) + @test newsystem isa FlexibleSystem @test length(newsystem) == 2 @test atomic_symbol(newsystem) == [:Si, :C] @test boundary_conditions(newsystem) == [Periodic(), Periodic(), Periodic()] From 9aa789ca062732b0943d7efb2b14442ccff39a0e Mon Sep 17 00:00:00 2001 From: "Michael F. Herbst" Date: Sat, 17 Dec 2022 22:23:18 +0100 Subject: [PATCH 05/10] Add Unitful and UnitfulAtomic --- docs/Project.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/Project.toml b/docs/Project.toml index 3441f66..d877b67 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,3 +1,5 @@ [deps] AtomsBase = "a963bdd2-2df7-4f54-a1ee-49d51e6be12a" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" +UnitfulAtomic = "a7773ee8-282e-5fa2-be4e-bd808c38a91a" From 79233a5bc2dc85ed954752195dad42cd1142b297 Mon Sep 17 00:00:00 2001 From: "Michael F. Herbst" Date: Tue, 20 Dec 2022 09:18:55 +0100 Subject: [PATCH 06/10] convert function --- src/flexible_system.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/flexible_system.jl b/src/flexible_system.jl index 8c73548..159b3ae 100644 --- a/src/flexible_system.jl +++ b/src/flexible_system.jl @@ -47,6 +47,7 @@ function FlexibleSystem(system::AbstractSystem; extra = system isa FlexibleSystem ? system.data : (; ) FlexibleSystem(particles, bounding_box, boundary_conditions; extra..., kwargs...) end +Base.convert(::Type{FlexibleSystem}, system::AbstractSystem) = FlexibleSystem(system) """ AbstractSystem(system::AbstractSystem; kwargs...) From 1db41954c7b50c744aa7375e14c09d4dfe6bfb94 Mon Sep 17 00:00:00 2001 From: "Michael F. Herbst" Date: Tue, 20 Dec 2022 09:22:00 +0100 Subject: [PATCH 07/10] version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index d047c89..30f7ed9 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "AtomsBase" uuid = "a963bdd2-2df7-4f54-a1ee-49d51e6be12a" authors = ["JuliaMolSim community"] -version = "0.2.4" +version = "0.2.5" [deps] PeriodicTable = "7b2266bf-644c-5ea3-82d8-af4bbd25a884" From 049056e8ba4a20ae33662ae8285a1c2dd37a1195 Mon Sep 17 00:00:00 2001 From: "Michael F. Herbst" Date: Tue, 20 Dec 2022 09:25:18 +0100 Subject: [PATCH 08/10] Missing test --- test/atom.jl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/atom.jl b/test/atom.jl index 3714065..98fa173 100644 --- a/test/atom.jl +++ b/test/atom.jl @@ -109,4 +109,12 @@ using Test Atom(:H, zeros(3) * u"Å")]) @test length(system) == 3 end + + @testset "Nothing or zero velocity" begin + at = Atom(:Si, ones(3) * u"Å"; extradata=42) + @test velocity(at) == zeros(3)u"Å/s" + + at = Atom(:Si, ones(3) * u"Å", missing; extradata=42) + @test velocity(at) == zeros(3)u"Å/s" + end end From 4ec681d07838333f21c39ad2f04c9bcaa6e2e7c6 Mon Sep 17 00:00:00 2001 From: "Michael F. Herbst" Date: Sun, 25 Dec 2022 21:41:38 +0100 Subject: [PATCH 09/10] Improve show --- src/show.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/show.jl b/src/show.jl index bf4f57f..3880094 100644 --- a/src/show.jl +++ b/src/show.jl @@ -75,7 +75,8 @@ end function show_atom(io::IO, at) pos = [(@sprintf "%8.6g" ustrip(p)) for p in position(at)] posunit = unit(eltype(position(at))) - print(io, "Atom($(atomic_symbol(at)), [", join(pos, ", "), "]u\"$posunit\"") + print(io, "Atom(", (@sprintf "%-3s" (string(atomic_symbol(at))) * ","), " [", + join(pos, ", "), "]u\"$posunit\"") if ismissing(velocity(at)) || iszero(velocity(at)) print(io, ")") else From e6fe15c11b400ab0db0947da4648333587a38b57 Mon Sep 17 00:00:00 2001 From: "Michael F. Herbst" Date: Mon, 2 Jan 2023 19:36:36 +0100 Subject: [PATCH 10/10] Update flexible_system.jl --- src/flexible_system.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/flexible_system.jl b/src/flexible_system.jl index 159b3ae..8c73548 100644 --- a/src/flexible_system.jl +++ b/src/flexible_system.jl @@ -47,7 +47,6 @@ function FlexibleSystem(system::AbstractSystem; extra = system isa FlexibleSystem ? system.data : (; ) FlexibleSystem(particles, bounding_box, boundary_conditions; extra..., kwargs...) end -Base.convert(::Type{FlexibleSystem}, system::AbstractSystem) = FlexibleSystem(system) """ AbstractSystem(system::AbstractSystem; kwargs...)