Skip to content
490 changes: 430 additions & 60 deletions Artifacts.toml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "PseudoPotentialData"
uuid = "5751a51d-ac76-4487-a056-413ecf6fbe19"
authors = ["Michael F. Herbst <info@michael-herbst.com> and contributors"]
version = "0.2.0"
version = "0.2.1"

[deps]
Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
Expand Down
22 changes: 9 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,25 @@

Package providing programmatic access
to standard pseudopotential data files for solid-state calculations.
The combination of a string identifier for the
The combination of the identifier for the
pseudopotential family and the element
symbol provides a unique and reproducible mapping to a pseudopotential file.
In case the pseudopotential data file happens to be missing on the computer
it will be automatically download as needed.
it will be automatically download.

For example, the following code automatically downloads the pseudopotential
file for silicon of the [stringent pseudodojo](http://www.pseudo-dojo.org/)
family for LDA pseudopotentials
(referred to by the identifier `dojo.nc.sr.lda.v0_4_1.oncvpsp3.standard.upf`)
family for LDA pseudopotentials (referred to by
the identifier `dojo.nc.sr.lda.v0_4_1.standard.upf`)
and places the full path to the downloaded pseudopotential file
into the `filename` variable:

```julia
using PseudoPotentialData
identifier = "dojo.nc.sr.lda.v0_4_1.oncvpsp3.standard.upf"
family = PseudoFamily(identifier)
family = PseudoFamily("dojo.nc.sr.lda.v0_4_1.standard.upf")
filename = family[:Si]
```

For a list of available identifiers see
```julia
PseudoPotentialData.family_identifiers()
```
Details on the naming convention of these keys and their respective
meaning provides the [PseudoPotentialData documenation](https://juliamolsim.github.io/PseudoPotentialData.jl/).
Some metadata for each pseudopotential family and each file
(including for example recommended cutoffs) are also easily accessible.
See the [PseudoPotentialData documenation](https://juliamolsim.github.io/PseudoPotentialData.jl/)
for more details.
60 changes: 40 additions & 20 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,12 @@ care to automatically download it as needed.

For example, the following code automatically downloads the pseudopotential
file of the [stringent pseudodojo](http://www.pseudo-dojo.org/) pseudopotential
for LDA pseudopotentials (referred to by the identifier `dojo.nc.sr.lda.v0_4_1.oncvpsp3.standard.upf`)
for LDA pseudopotentials (referred to by the identifier `dojo.nc.sr.lda.v0_4_1.standard.upf`)
and places the full path to the downloaded pseudopotential file into the `filename` variable:

```@example index-example
using PseudoPotentialData
identifier = "dojo.nc.sr.lda.v0_4_1.oncvpsp3.standard.upf"
family = PseudoFamily(identifier)
family = PseudoFamily("dojo.nc.sr.lda.v0_4_1.standard.upf")
filename = pseudofile(family, :Si)
```
As you see this will be a string such as
Expand All @@ -39,45 +38,66 @@ For multiple elements you can similarly use
pseudofile.(family, [:C, :Si])
```

A `PseudoFamily` is furthermore an `AbstractDict{Symbol,String}`
A [`PseudoFamily`](@ref) struct is furthermore an `AbstractDict{Symbol,String}`
for the mapping of element symbol to file path, e.g. one can perform
index lookup
```@example index-example
family[:Si]
```
or iterate over pairs
iterate over pairs
```@example index-example
for (k, v) in family
println(k, " => ", v)
break
end
```
or get the list of available elements as the list of keys:
```@example index-example
collect(keys(family))
```

Metadata on the pseudopotential family and individual elements
in the family can be accessed via the [`pseudometa`](@ref) function:
```@example index-example
pseudometa(family)
```
or for an element:
```@example index-example
pseudometa(family, :Si)
```
Notably this often contains recommended values of the kinetic energy
cutoffs of plane-wave bases. These can be also accessed more conveniently via
```@example index-example
recommended_cutoff(family, :Si)
```
Note, that the `Ecut` and `Ecut_density` values are in atomic Hartree units.

## Available keys and naming convention
A list of available pseudopotential identifiers is available as
## Available pseudopotential families and naming convention
A list of available pseudopotential families is available as
```@example index-example
PseudoPotentialData.family_identifiers()
```

The naming convention is as that each pseudo family name consists
of a list of fields, which are concatenated using a `.` (dot).
These are:
1. An identifier for the pseudo family (like `dojo` for the [PseudoDojo](http://www.pseudo-dojo.org/) family of potentials.
2. The type of pseudopotential (`nc`: norm-conserving, `us`: ultrasoft, `paw`: projected augmented wave)
3. Details on the level of relativistic effects employed when generating the pseudo (`fr`: Full relativistic, `sr`: Scalar relativistic, `nr`: No relativistic)
4. The functional for which the pseudopotential was prepared
5. The version of the pseudopotential construction (with version points replaced by underscores)
6. The program used to generate the pseudopotential
7. Some additional comments specifying the pseudopotential.
1. `collection`: An identifier for the pseudo collection (like `dojo` for the [PseudoDojo](http://www.pseudo-dojo.org/) family of potentials.
2. `type`: The type of pseudopotential (`nc`: norm-conserving, `us`: ultrasoft, `paw`: projected augmented wave)
3. `relativistic`: Details on the level of relativistic effects employed when generating the pseudo (`fr`: Full relativistic, `sr`: Scalar relativistic, `nr`: No relativistic)
4. `functional`: The functional for which the pseudopotential was prepared
5. `version`: The version of the pseudopotential construction (with version points replaced by underscores)
6. `extra`: Some additional comments specifying the pseudopotential.
E.g. for PseudoDojo potentials there is usually a `stringent` version
(requiring slightly larger cutoffs) and a `standard` version being a bit softer.
8. The format of the pseudopotential files in this library.
7. `extension`: The file format of the pseudopotential files in this library.

For a given [`PseudoFamily`](@ref) object the above fields
(as well as typically additional metadata information) can also be
accessed via the [`pseudometa`](@ref) function as indicated above.

More details on the meaning of these keys
will be provided at a later stage.
Some information is also available in the README of the
[PseudoLibrary](https://github.com/JuliaMolSim/PseudoLibrary/blob/7c4b71a3b9d70a229d757aa6d546ef22b83a85a9/README.md)
repository.
More details on the available pseudopotential families is given in the
[PseudoLibrary](https://github.com/JuliaMolSim/PseudoLibrary)
repository, which manages the data underlying this package.

## Interface

Expand Down
12 changes: 3 additions & 9 deletions src/PseudoPotentialData.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,18 @@ using Compat: @compat
using LazyArtifacts
using TOML

export PseudoFamily, pseudofile
export PseudoFamily
export pseudofile, pseudometa, recommended_cutoff
@compat public families
@compat public family_identifiers

export available_elements, has_element

include("pseudofamily.jl")

"""Get the list of available pseudopotential family identifiers."""
function family_identifiers()
artifact_file = find_artifacts_toml(@__FILE__)
@assert !isnothing(artifact_file)

# TODO For compatibility already use new keys
artifactmap_inv = Dict(v => k for (k, v) in artifactmap)
map(collect(keys(TOML.parsefile(artifact_file)))) do key
artifactmap_inv[key]
end
sort(collect(keys(TOML.parsefile(artifact_file))))
end

"""The list of all known pseudopotential families."""
Expand Down
145 changes: 96 additions & 49 deletions src/pseudofamily.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@
identifier::String
#
# metadata
extension::String # Filename expected as $(symbol).$(extension)
functional::String # DFT functional keyword (or "" if unspecified)
version::VersionNumber
# TODO More things will probably follow
# - Type of pseudisation (norm-conserving, PAW, ...)
# - References to papers describing these pseudopotentials
# - Elements available
collection::String # Pseudo collection this family is from (e.g. "dojo")
type::String # Pseudofamily type ("nc", "us", "paw")
relativistic::String # Kind of relativistic effects ("fr", "sr")
functional::String # DFT functional keyword
version::VersionNumber # Version of the pseudofamily
program::String # Program used to generate the pseudos
extra::Vector{String} # Additional specifiers for this family
extension::String # Filename expected as $(symbol).$(extension)
elements::Vector{Symbol} # Available elements
#
additional_metadata::Dict{String,Any}
# TODO References to relevant papers ?
end

"""
Expand All @@ -19,14 +24,35 @@
an element symbol to the full path of the pseudopotential file.
"""
function PseudoFamily(identifier::AbstractString)
if occursin(".oncvpsp3.", identifier)
newid = replace(identifier, ".oncvpsp3." => ".")
@warn "Identifier $identifier is deprecated. Use $newid instead."
return PseudoFamily(newid)

Check warning on line 30 in src/pseudofamily.jl

View check run for this annotation

Codecov / codecov/patch

src/pseudofamily.jl#L28-L30

Added lines #L28 - L30 were not covered by tests
end

artifact_file = find_artifacts_toml(@__FILE__)
@assert !isnothing(artifact_file)
meta = artifact_meta(artifactmap[identifier], artifact_file)
isnothing(meta) && throw(ArgumentError("Invalid pseudo identifier: $identifier"))
meta = artifact_meta(identifier, artifact_file)
isnothing(meta) && throw(ArgumentError("Invalid PseudoFamily identifier: $identifier"))

additional_metadata = Dict{String,Any}()
for k in keys(meta)
k in mandatory_family_keys && continue
k in ("lazy", "git-tree-sha1", "download", "pseudolibrary_version") && continue
additional_metadata[k] = meta[k]
end

PseudoFamily(identifier,
meta["extension"],
meta["collection"],
meta["type"],
meta["relativistic"],
meta["functional"],
VersionNumber(meta["version"]))
VersionNumber(meta["version"]),
meta["program"],
meta["extra"],
meta["extension"],
Symbol.(meta["elements"]),
additional_metadata)
end
Base.Broadcast.broadcastable(l::PseudoFamily) = Ref(l)

Expand All @@ -35,56 +61,60 @@
end
Base.show(io::IO, ::MIME"text/plain", family::PseudoFamily) = show(io, family)

#
# Helper functions (not exported)
#

# TODO For compatibility, map new to old ... will disappear at some point
const artifactmap = Dict(
"dojo.nc.fr.pbesol.v0_4.oncvpsp3.standard.upf" => "pd_nc_fr_pbesol_standard_0.4_upf",
"dojo.nc.fr.pbesol.v0_4.oncvpsp3.stringent.upf" => "pd_nc_fr_pbesol_stringent_0.4_upf",
"dojo.nc.fr.pbe.v0_4.oncvpsp3.standard.upf" => "pd_nc_fr_pbe_standard_0.4_upf",
"dojo.nc.fr.pbe.v0_4.oncvpsp3.stringent.upf" => "pd_nc_fr_pbe_stringent_0.4_upf",
"dojo.nc.sr.lda.v0_4_1.oncvpsp3.standard.upf" => "pd_nc_sr_lda_standard_0.4.1_upf",
"dojo.nc.sr.lda.v0_4_1.oncvpsp3.stringent.upf" => "pd_nc_sr_lda_stringent_0.4.1_upf",
"dojo.nc.sr.pbesol.v0_4_1.oncvpsp3.standard.upf" => "pd_nc_sr_pbesol_standard_0.4.1_upf",
"dojo.nc.sr.pbe.v0_4_1.oncvpsp3.standard.upf" => "pd_nc_sr_pbe_standard_0.4.1_upf",
"dojo.nc.sr.pbe.v0_4_1.oncvpsp3.stringent.upf" => "pd_nc_sr_pbe_stringent_0.4.1_upf",
"dojo.paw.pbe.v1_1.jth.standard.xml" => "pd_paw_pbe_standard_1.1_xml",
)

"""
Return the directory containing the pseudo files.
This downloads the artifact if necessary.
Get the full path to the file containing the pseudopotential information
for a particular `element` (identified by an atomic symbol) and a particular
pseudopotential `family`.
"""
function artifact_directory(family::PseudoFamily)
@artifact_str "$(artifactmap[family.identifier])"
end
pseudofile(family::PseudoFamily, element::Symbol) = family[element]

"""Return the list of all pseudopotential files in the artifact"""
function available_elements(family::PseudoFamily)
# TODO Once this is part of the metadata, do this without downloading
files = filter!(endswith(family.extension),
readdir(artifact_directory(family)))
map(files) do file
base, _ = splitext(file)
Symbol(base)
"""Return collection of metadata of the pseudofamily."""
function pseudometa(family::PseudoFamily)
d = copy(family.additional_metadata)
for k in mandatory_family_keys
d[k] = getproperty(family, Symbol(k))
end
d
end

"""
Get the full path to the file containing the pseudopotential information
for a particular `element` (identified by an atomic symbol) and a particular
pseudopotential `family`.
Return collection of metadata of the pseudopotential
identified by this `family` and `element`.
"""
pseudofile(family::PseudoFamily, element::Symbol) = family[element]
function pseudometa(family::PseudoFamily, element::Symbol)
file = joinpath(artifact_directory(family), "$(element).toml")
isfile(file) || throw(KeyError(element))
open(TOML.parse, file)
end

"""
Return the recommended kinetic energy cutoff, supersampling and density
cutoff for the pseudopotential indentified by this `family` and `element`.
`Ecut` and `Ecut_density` are returned in Hartree.
"""
function recommended_cutoff(family::PseudoFamily, element::Symbol)
data = pseudometa(family, element)

Ecut::Union{Missing,Float64} = missing
supersampling::Union{Missing,Float64} = missing
Ecut_density::Union{Missing,Float64} = missing
if haskey(data, "Ecut") && data["Ecut"] > 0
Ecut = data["Ecut"]
if haskey(data, "supersampling")
supersampling = data["supersampling"]
Ecut_density = supersampling^2 * Ecut
end
end

(; Ecut, supersampling, Ecut_density)
end

#
# AbstractDict interface
#

Base.keys(family::PseudoFamily) = available_elements(family)
Base.length(family::PseudoFamily) = length(available_elements(family))
Base.keys(family::PseudoFamily) = family.elements
Base.length(family::PseudoFamily) = length(family.elements)

function Base.getindex(family::PseudoFamily, element::Symbol)
file = joinpath(artifact_directory(family), "$(element)." * family.extension)
Expand All @@ -96,7 +126,24 @@
if state == length(family)
return nothing
else
element = available_elements(family)[state+1]
element = family.elements[state+1]
return (element => family[element], state+1)
end
end

#
# Helper functions and constants (not exported)
#

# Keys from the Artifact.toml, which are mandatory directly stored in the above struct
const mandatory_family_keys = ("collection", "type", "relativistic", "functional", "version",
"program", "extra", "extension", "elements")


"""
Return the directory containing the pseudo files.
This downloads the artifact if necessary.
"""
function artifact_directory(family::PseudoFamily)
@artifact_str "$(family.identifier)"
end
Loading
Loading