Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
nomad-lab
nomad-FAIR
Commits
67c17a0e
Commit
67c17a0e
authored
Feb 18, 2020
by
Lauri Himanen
Browse files
Merged.
parent
f9476740
Changes
6
Hide whitespace changes
Inline
Side-by-side
gaussian
@
15d0110c
Compare
022a2af6
...
15d0110c
Subproject commit
022a2af6bad45364dbdfac6b6c913f04186ac7d4
Subproject commit
15d0110cbeda05aaea05e4d30ba3aeb0874dafef
quantum-espresso
@
b39569c5
Compare
fe15759f
...
b39569c5
Subproject commit
fe15759f080e8176d88af91447949243608b0d7e
Subproject commit
b39569c5fa69254c90f91ec430d28a0941efbe95
nomad/cli/admin/admin.py
View file @
67c17a0e
...
...
@@ -396,9 +396,8 @@ def prototypes_update(ctx, filepath, matches_only):
)
# Try to first see if the space group can be matched with the one in AFLOW
tolerance
=
config
.
normalize
.
symmetry_tolerance
try
:
symm
=
SymmetryAnalyzer
(
atoms
,
tolerance
)
symm
=
SymmetryAnalyzer
(
atoms
,
config
.
normalize
.
prototype_symmetry_
tolerance
)
spg_number
=
symm
.
get_space_group_number
()
wyckoff_matid
=
symm
.
get_wyckoff_letters_conventional
()
norm_system
=
symm
.
get_conventional_system
()
...
...
@@ -419,7 +418,5 @@ def prototypes_update(ctx, filepath, matches_only):
.
format
(
n_prototypes
,
n_unmatched
,
n_failed
)
)
aflow_prototypes
[
"matid_symmetry_tolerance"
]
=
tolerance
# Write data file to the specified path
write_prototype_data_file
(
aflow_prototypes
,
filepath
)
nomad/config.py
View file @
67c17a0e
...
...
@@ -71,6 +71,19 @@ class NomadConfig(dict):
CELERY_WORKER_ROUTING
=
'worker'
CELERY_QUEUE_ROUTING
=
'queue'
version
=
'0.7.6'
commit
=
gitinfo
.
commit
release
=
'devel'
domain
=
'DFT'
service
=
'unknown nomad service'
auxfile_cutoff
=
100
parser_matching_size
=
9128
console_log_level
=
logging
.
WARNING
max_upload_size
=
32
*
(
1024
**
3
)
raw_file_strip_cutoff
=
1000
use_empty_parsers
=
False
springer_db_relative_path
=
'normalizing/data/SM_all08.db'
springer_db_path
=
os
.
path
.
join
(
os
.
path
.
dirname
(
os
.
path
.
abspath
(
__file__
)),
springer_db_relative_path
)
rabbitmq
=
NomadConfig
(
host
=
'localhost'
,
...
...
@@ -78,11 +91,6 @@ rabbitmq = NomadConfig(
password
=
'rabbitmq'
)
def
rabbitmq_url
():
return
'pyamqp://%s:%s@%s//'
%
(
rabbitmq
.
user
,
rabbitmq
.
password
,
rabbitmq
.
host
)
celery
=
NomadConfig
(
max_memory
=
64e6
,
# 64 GB
timeout
=
1800
,
# 1/2 h
...
...
@@ -155,20 +163,6 @@ tests = NomadConfig(
)
def
api_url
(
ssl
:
bool
=
True
):
return
'%s://%s/%s/api'
%
(
'https'
if
services
.
https
and
ssl
else
'http'
,
services
.
api_host
.
strip
(
'/'
),
services
.
api_base_path
.
strip
(
'/'
))
def
gui_url
():
base
=
api_url
(
True
)[:
-
3
]
if
base
.
endswith
(
'/'
):
base
=
base
[:
-
1
]
return
'%s/gui'
%
base
mail
=
NomadConfig
(
enabled
=
False
,
with_login
=
False
,
...
...
@@ -187,8 +181,16 @@ normalize = NomadConfig(
# Symmetry tolerance controls the precision used by spglib in order to find
# symmetries. The atoms are allowed to move 1/2*symmetry_tolerance from
# their symmetry positions in order for spglib to still detect symmetries.
# The unit is angstroms.
# The unit is angstroms. The value of 0.1 is used e.g. by Materials Project
# according to
# https://pymatgen.org/pymatgen.symmetry.analyzer.html#pymatgen.symmetry.analyzer.SpacegroupAnalyzer
symmetry_tolerance
=
0.1
,
# The symmetry tolerance used in aflow prototype matching. Should only be
# changed before re-running the prototype detection.
prototype_symmetry_tolerance
=
0.1
,
# Maximum number of atoms in the single cell of a 2D material for it to be
# considered valid.
max_2d_single_cell_size
=
7
,
# The distance tolerance between atoms for grouping them into the same
# cluster. Used in detecting system type.
cluster_threshold
=
3.1
,
...
...
@@ -208,21 +210,43 @@ datacite = NomadConfig(
password
=
'*'
)
version
=
'0.7.6'
commit
=
gitinfo
.
commit
release
=
'devel'
domain
=
'DFT'
service
=
'unknown nomad service'
auxfile_cutoff
=
100
parser_matching_size
=
9128
console_log_level
=
logging
.
WARNING
max_upload_size
=
32
*
(
1024
**
3
)
raw_file_strip_cutoff
=
1000
use_empty_parsers
=
False
def
rabbitmq_url
():
return
'pyamqp://%s:%s@%s//'
%
(
rabbitmq
.
user
,
rabbitmq
.
password
,
rabbitmq
.
host
)
springer_db_relative_path
=
'normalizing/data/SM_all08.db'
springer_db_path
=
os
.
path
.
join
(
os
.
path
.
dirname
(
os
.
path
.
abspath
(
__file__
)),
springer_db_relative_path
)
def
api_url
(
ssl
:
bool
=
True
):
return
'%s://%s/%s/api'
%
(
'https'
if
services
.
https
and
ssl
else
'http'
,
services
.
api_host
.
strip
(
'/'
),
services
.
api_base_path
.
strip
(
'/'
))
def
gui_url
():
base
=
api_url
(
True
)[:
-
3
]
if
base
.
endswith
(
'/'
):
base
=
base
[:
-
1
]
return
'%s/gui'
%
base
def
check_config
():
"""Used to check that the current configuration is valid. Should only be
called once after the final config is loaded.
Raises:
AssertionError: if there is a contradiction or invalid values in the
config file settings.
"""
# The AFLOW symmetry information is checked once on import
proto_symmetry_tolerance
=
normalize
.
prototype_symmetry_tolerance
symmetry_tolerance
=
normalize
.
symmetry_tolerance
if
proto_symmetry_tolerance
!=
symmetry_tolerance
:
raise
AssertionError
(
"The AFLOW prototype information is outdated due to changed tolerance "
"for symmetry detection. Please update the AFLOW prototype information "
"by running the CLI command 'nomad admin ops prototype-update "
"--matches-only'."
)
def
normalize_loglevel
(
value
,
default_level
=
logging
.
INFO
):
...
...
@@ -353,3 +377,4 @@ def load_config(config_file: str = os.environ.get('NOMAD_CONFIG', 'nomad.yaml'))
load_config
()
check_config
()
nomad/normalizing/data/aflow_prototypes.py
View file @
67c17a0e
# -*- coding: utf-8 -*-
aflow_prototypes = {
"matid_symmetry_tolerance": 0.1,
"prototypes_by_spacegroup": {
1: [
{
nomad/normalizing/structure.py
View file @
67c17a0e
...
...
@@ -19,16 +19,237 @@ import numpy as np
from
nomad.normalizing.data.aflow_prototypes
import
aflow_prototypes
from
nomad
import
config
# The AFLOW symmetry information is checked once on import
old_symmetry_tolerance
=
aflow_prototypes
[
"matid_symmetry_tolerance"
]
symmetry_tolerance
=
config
.
normalize
.
symmetry_tolerance
if
old_symmetry_tolerance
!=
symmetry_tolerance
:
raise
AssertionError
(
"The AFLOW prototype information is outdated due to changed "
"tolerance for symmetry detection. Please update the AFLOW "
"prototype information by running once the function "
"'update_aflow_prototype_information'."
)
def
get_summed_atomic_mass
(
atomic_numbers
:
np
.
ndarray
)
->
float
:
"""Calculates the summed atomic mass for the given atomic numbers.
Args:
atomic_numbers: Array of valid atomic numbers
Returns:
The atomic mass in kilograms.
"""
# It is assumed that the atomic numbers are valid at this point.
mass
=
np
.
sum
(
NUMBER_TO_MASS_MAP_KG
[
atomic_numbers
])
return
mass
def
get_symmetry_string
(
space_group
:
int
,
wyckoff_sets
:
Dict
)
->
str
:
"""Used to serialize symmetry information into a string. The Wyckoff
positions are assumed to be normalized and ordered as is the case if using
the matid-library.
Args:
space_group: 3D space group number
wyckoff_sets: Wyckoff sets that map a Wyckoff letter to related
information
Returns:
A string that encodes the symmetry properties of an atomistic
structure.
"""
wyckoff_strings
=
[]
for
group
in
wyckoff_sets
:
element
=
group
.
element
wyckoff_letter
=
group
.
wyckoff_letter
n_atoms
=
len
(
group
.
indices
)
i_string
=
"{} {} {}"
.
format
(
element
,
wyckoff_letter
,
n_atoms
)
wyckoff_strings
.
append
(
i_string
)
wyckoff_string
=
", "
.
join
(
sorted
(
wyckoff_strings
))
string
=
"{} {}"
.
format
(
space_group
,
wyckoff_string
)
return
string
def
get_lattice_parameters
(
normalized_cell
:
np
.
ndarray
)
->
np
.
ndarray
:
"""Calculate the lattice parameters for the normalized cell.
Args:
normalized_cell: The normalized cell as a 3x3 array. Each row is a
basis vector.
Returns:
Six parameters a, b, c, alpha, beta, gamma (in this order) as a numpy
array. Here is an explanation of each parameter:
a = length of first basis vector
b = length of second basis vector
c = length of third basis vector
alpha = angle between b and c
beta = angle between a and c
gamma = angle between a and b
"""
if
normalized_cell
is
None
:
return
None
# Lengths
lengths
=
np
.
linalg
.
norm
(
normalized_cell
,
axis
=
1
)
a
,
b
,
c
=
lengths
# Angles
angles
=
np
.
zeros
(
3
)
for
i
in
range
(
3
):
j
=
(
i
+
1
)
%
3
k
=
(
i
+
2
)
%
3
angles
[
i
]
=
np
.
dot
(
normalized_cell
[
j
],
normalized_cell
[
k
])
/
(
lengths
[
j
]
*
lengths
[
k
])
angles
=
np
.
clip
(
angles
,
-
1.0
,
1.0
)
alpha
,
beta
,
gamma
=
np
.
arccos
(
angles
)
return
[
a
,
b
,
c
,
alpha
,
beta
,
gamma
]
def
get_hill_decomposition
(
atom_labels
:
np
.
ndarray
,
reduced
:
bool
=
False
)
->
Tuple
[
List
[
str
],
List
[
int
]]:
"""Given a list of atomic labels, returns the chemical formula using the
Hill system (https://en.wikipedia.org/wiki/Hill_system) with an exception
for binary ionic compounds where the cation is always given first.
Args:
atom_labels: Atom labels.
reduced: Whether to divide the number of atoms by the greatest common
divisor
Returns:
An ordered list of chemical symbols and the corresponding counts.
"""
# Count occurancy of elements
names
=
[]
counts
=
[]
unordered_names
,
unordered_counts
=
np
.
unique
(
atom_labels
,
return_counts
=
True
)
element_count_map
=
dict
(
zip
(
unordered_names
,
unordered_counts
))
# Apply basic Hill system:
# 1. Is Carbon part of the system?
if
"C"
in
element_count_map
:
names
.
append
(
"C"
)
counts
.
append
(
element_count_map
[
"C"
])
del
element_count_map
[
'C'
]
# 1a. add hydrogren
if
"H"
in
element_count_map
:
names
.
append
(
"H"
)
counts
.
append
(
element_count_map
[
"H"
])
del
element_count_map
[
"H"
]
# 2. all remaining elements in alphabetic order
for
element
in
sorted
(
element_count_map
):
names
.
append
(
element
)
counts
.
append
(
element_count_map
[
element
])
# 3. Binary ionic compounds: cation first, anion second
# If any of the most electronegative elements is first
# by alphabetic order, we move it to second
if
len
(
counts
)
==
2
and
names
!=
[
"C"
,
"H"
]:
order
=
{
"F"
:
1
,
"O"
:
2
,
"N"
:
3
,
"Cl"
:
4
,
"Br"
:
5
,
"C"
:
6
,
"Se"
:
7
,
"S"
:
8
,
"I"
:
9
,
"As"
:
10
,
"H"
:
11
,
"P"
:
12
,
"Ge"
:
13
,
"Te"
:
14
,
"B"
:
15
,
"Sb"
:
16
,
"Po"
:
17
,
"Si"
:
18
,
"Bi"
:
19
}
if
(
names
[
0
]
in
order
):
if
(
names
[
1
]
in
order
):
if
(
order
[
names
[
0
]]
<
order
[
names
[
1
]]):
# For non-metals:
# Swap symbols and counts if first element
# is more electronegative than the second one,
# because the more electronegative element is the anion
names
[
0
],
names
[
1
]
=
names
[
1
],
names
[
0
]
counts
[
0
],
counts
[
1
]
=
counts
[
1
],
counts
[
0
]
else
:
# Swap symbols and counts always if second element
# is any other element,i.e.,
# put non-metal last because it is the anion
names
[
0
],
names
[
1
]
=
names
[
1
],
names
[
0
]
counts
[
0
],
counts
[
1
]
=
counts
[
1
],
counts
[
0
]
# TODO: implement all further exceptions regarding ordering
# in chemical formulas:
# - ionic compounds (ordering wrt to ionization)
# - oxides, acids, hydroxides...
# Reduce if requested
if
reduced
:
greatest_common_divisor
=
reduce
(
gcd
,
counts
)
counts
=
np
.
array
(
counts
)
/
greatest_common_divisor
return
names
,
counts
def
get_formula_string
(
symbols
:
List
[
str
],
counts
:
List
[
int
])
->
str
:
"""Used to form a single formula string from a list of chemical speices and
their counts.
Args:
symbols: List of chemical species
counts: List of chemical species occurences
Returns:
The formula as a string.
"""
formula
=
""
for
symbol
,
count
in
zip
(
symbols
,
counts
):
if
count
>
1
:
formula
+=
"%s%d"
%
(
symbol
,
count
)
else
:
formula
+=
symbol
return
formula
def
find_vacuum_directions
(
system
:
Atoms
,
threshold
:
float
)
->
np
.
array
:
"""Searches for vacuum gaps that are separating the periodic copies.
Args:
system: The structure to analyze
threshold: Vacuum threshold in angstroms
Returns:
np.ndarray: An array with a boolean for each lattice basis
direction indicating if there is enough vacuum to separate the
copies in that direction.
"""
rel_pos
=
system
.
get_scaled_positions
()
pbc
=
system
.
get_pbc
()
# Find the maximum vacuum gap for all basis vectors
gaps
=
np
.
empty
(
3
,
dtype
=
bool
)
for
axis
in
range
(
3
):
if
not
pbc
[
axis
]:
gaps
[
axis
]
=
True
continue
comp
=
rel_pos
[:,
axis
]
ind
=
np
.
sort
(
comp
)
ind_rolled
=
np
.
roll
(
ind
,
1
,
axis
=
0
)
distances
=
ind
-
ind_rolled
# The first distance is from first to last, so it needs to be
# wrapped around
distances
[
0
]
+=
1
# Find maximum gap in cartesian coordinates
max_gap
=
np
.
max
(
distances
)
basis
=
system
.
get_cell
()[
axis
,
:]
max_gap_cartesian
=
np
.
linalg
.
norm
(
max_gap
*
basis
)
has_vacuum_gap
=
max_gap_cartesian
>=
threshold
gaps
[
axis
]
=
has_vacuum_gap
return
gaps
>>>>>>>
Stashed
changes
def
get_normalized_wyckoff
(
atomic_numbers
:
np
.
array
,
wyckoff_letters
:
np
.
array
)
->
Dict
[
str
,
Dict
[
str
,
int
]]:
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment