Commit 31a9cbfd authored by Lauri Himanen's avatar Lauri Himanen
Browse files

Added 2D example.

parent 2588c036
......@@ -232,6 +232,7 @@ class Calculation(MSection):
class Encyclopedia(MSection):
m_def = Section(
name="encyclopedia",
a_flask=dict(skip_none=True),
a_elastic=dict(type=InnerDoc)
)
......
......@@ -376,9 +376,7 @@ class EncyclopediaNormalizer(Normalizer):
except KeyError:
self.logger.info("System type information not available for encyclopedia")
else:
if stype == "2D / surface":
system_type = system_enums.two_d
if stype == system_enums.bulk or stype == system_enums.one_d:
if stype == system_enums.bulk or stype == system_enums.one_d or stype == system_enums.two_d:
system_type = stype
material.system_type = system_type
......@@ -389,8 +387,12 @@ class EncyclopediaNormalizer(Normalizer):
def fill(self, run_type, system_type, representative_system):
# Fill structure related metainfo
struct = None
if system_type == Material.system_type.type.bulk:
struct = StructureBulk()
elif system_type == Material.system_type.type.two_d:
struct = Structure2D()
if struct is not None:
struct.fill(self._backend, representative_system)
def normalize(self, logger=None) -> None:
......@@ -426,17 +428,16 @@ class Structure():
"""A base class that is used for processing structure related information
in the Encylopedia.
"""
@abstractmethod
def atom_labels(self, material: Material, std_atoms: ase.Atoms) -> None:
pass
material.atom_labels = std_atoms.get_chemical_symbols()
@abstractmethod
def atom_positions(self, material: Material, std_atoms: ase.Atoms) -> None:
pass
material.atom_positions = std_atoms.get_scaled_positions(wrap=False)
@abstractmethod
def atomic_density(self, calculation: Calculation, repr_system: ase.Atoms) -> None:
pass
orig_n_atoms = len(repr_system)
orig_volume = repr_system.get_volume() * (1e-10)**3
calculation.atomic_density = float(orig_n_atoms / orig_volume)
@abstractmethod
def bravais_lattice(self, material: Material, section_system: Dict) -> None:
......@@ -454,21 +455,20 @@ class Structure():
def cell_primitive(self, material: Material, std_atoms: ase.Atoms) -> None:
pass
@abstractmethod
def cell_volume(self, calculation: Calculation, std_atoms: ase.Atoms) -> None:
pass
calculation.cell_volume = float(std_atoms.get_volume() * 1e-10**3)
@abstractmethod
def crystal_system(self, material: Material, section_system: Dict) -> None:
pass
@abstractmethod
def formula(self, material: Material, names: list, counts: list) -> None:
pass
def formula(self, material: Material, names: List[str], counts: List[int]) -> None:
formula = structure.get_formula_string(names, counts)
material.formula = formula
@abstractmethod
def formula_reduced(self, material: Material, names: list, counts_reduced: list) -> None:
pass
formula = structure.get_formula_string(names, counts_reduced)
material.formula_reduced = formula
@abstractmethod
def has_free_wyckoff_parameters(self, material: Material, wyckoff_sets: Dict) -> None:
......@@ -478,9 +478,10 @@ class Structure():
def lattice_parameters(self, calculation: Calculation, std_atoms: ase.Atoms) -> None:
pass
@abstractmethod
def mass_density(self, calculation: Calculation, repr_system: ase.Atoms) -> None:
pass
orig_volume = repr_system.get_volume() * (1e-10)**3
mass = structure.get_summed_atomic_mass(repr_system.get_atomic_numbers())
calculation.mass_density = float(mass / orig_volume)
@abstractmethod
def material_hash(self, material: Material, section_system: Dict) -> None:
......@@ -490,9 +491,8 @@ class Structure():
def material_name(self, material: Material, symbols: list, numbers: list) -> None:
pass
@abstractmethod
def number_of_atoms(self, material: Material, std_atoms: ase.Atoms) -> None:
pass
material.number_of_atoms = len(std_atoms)
@abstractmethod
def periodicity(self, material: Material) -> None:
......@@ -510,22 +510,6 @@ class Structure():
class StructureBulk(Structure):
"""Processes structure related metainfo for Encyclopedia bulk structures.
"""
def atom_labels(self, material: Material, std_atoms: ase.Atoms) -> None:
material.atom_labels = std_atoms.get_chemical_symbols()
def atom_positions(self, material: Material, std_atoms: ase.Atoms) -> None:
material.atom_positions = std_atoms.get_scaled_positions(wrap=False)
def atomic_density(self, calculation: Calculation, repr_system: ase.Atoms) -> None:
orig_n_atoms = len(repr_system)
orig_volume = repr_system.get_volume() * (1e-10)**3
calculation.atomic_density = float(orig_n_atoms / orig_volume)
def mass_density(self, calculation: Calculation, repr_system: ase.Atoms) -> None:
orig_volume = repr_system.get_volume() * (1e-10)**3
mass = structure.get_summed_atomic_mass(repr_system.get_atomic_numbers())
calculation.mass_density = float(mass / orig_volume)
def material_hash(self, material: Material, section_symmetry: Dict) -> None:
# Get symmetry information from the section
space_group_number = section_symmetry["space_group_number"]
......@@ -536,9 +520,6 @@ class StructureBulk(Structure):
norm_hash_string = structure.get_symmetry_string(space_group_number, wyckoff_sets)
material.material_hash = sha512(norm_hash_string.encode('utf-8')).hexdigest()
def number_of_atoms(self, material: Material, std_atoms: ase.Atoms) -> None:
material.number_of_atoms = len(std_atoms)
def bravais_lattice(self, material: Material, section_symmetry: Dict) -> None:
bravais_lattice = section_symmetry["bravais_lattice"]
material.bravais_lattice = bravais_lattice
......@@ -562,20 +543,9 @@ class StructureBulk(Structure):
cell_prim *= 1e-10
material.cell_primitive = cell_prim
def cell_volume(self, calculation: Calculation, std_atoms: ase.Atoms) -> None:
calculation.cell_volume = float(std_atoms.get_volume() * 1e-10**3)
def crystal_system(self, material: Material, section_symmetry: Dict) -> None:
material.crystal_system = section_symmetry["crystal_system"]
def formula(self, material: Material, names: List[str], counts: List[int]) -> None:
formula = structure.get_formula_string(names, counts)
material.formula = formula
def formula_reduced(self, material: Material, names: list, counts_reduced: list) -> None:
formula = structure.get_formula_string(names, counts_reduced)
material.formula_reduced = formula
def has_free_wyckoff_parameters(self, material: Material, symmetry_analyzer: SymmetryAnalyzer) -> None:
has_free_param = symmetry_analyzer.get_has_free_wyckoff_parameters()
material.has_free_wyckoff_parameters = has_free_param
......@@ -763,3 +733,53 @@ class StructureBulk(Structure):
self.periodicity(material)
self.point_group(material, sec_symmetry)
self.wyckoff_groups(material, wyckoff_sets)
class Structure2D(Structure):
"""Processes structure related metainfo for Encyclopedia 2D structures.
"""
def material_hash(self, material: Material, section_symmetry: Dict) -> None:
pass
def cell_angles_string(self, calculation: Calculation) -> None:
pass
def cell_normalized(self, material: Material, std_atoms: ase.Atoms) -> None:
pass
def cell_primitive(self, material: Material, prim_atoms: ase.Atoms) -> None:
pass
def lattice_parameters(self, calculation: Calculation, std_atoms: ase.Atoms) -> None:
pass
def periodicity(self, material: Material) -> None:
pass
def fill(self, backend, representative_system: Dict) -> None:
# Fetch resources
sec_enc = backend.get_mi2_section(Encyclopedia.m_def)
material = sec_enc.material
calculation = sec_enc.calculation
sec_symmetry = representative_system["section_symmetry"][0]
std_atoms = sec_symmetry["section_std_system"][0].tmp["std_atoms"] # Temporary value stored by SystemNormalizer
prim_atoms = sec_symmetry["section_primitive_system"][0].tmp["prim_atoms"] # Temporary value stored by SystemNormalizer
repr_atoms = sec_symmetry["section_original_system"][0].tmp["orig_atoms"] # Temporary value stored by SystemNormalizer
names, counts = structure.get_hill_decomposition(prim_atoms.get_chemical_symbols(), reduced=False)
greatest_common_divisor = reduce(gcd, counts)
reduced_counts = np.array(counts) / greatest_common_divisor
# Fill structural information
self.mass_density(calculation, repr_atoms)
self.material_hash(material, sec_symmetry)
self.number_of_atoms(material, std_atoms)
self.atom_labels(material, std_atoms)
self.atomic_density(calculation, repr_atoms)
self.cell_normalized(material, std_atoms)
self.cell_volume(calculation, std_atoms)
self.cell_primitive(material, prim_atoms)
self.formula(material, names, counts)
self.formula_reduced(material, names, reduced_counts)
self.lattice_parameters(calculation, std_atoms)
self.cell_angles_string(calculation)
self.periodicity(material)
......@@ -112,7 +112,7 @@ class SystemBasedNormalizer(Normalizer, metaclass=ABCMeta):
else:
sequences.append(frames)
# If no frame_sequences exist, consider all existing sccs
# If no frame_sequences exist, consider all existing sccs as a sequence
if len(sequences) == 0:
try:
sccs = self._backend.get_sections(s_scc)
......
......@@ -23,8 +23,8 @@ import sqlite3
import functools
import fractions
from matid import SymmetryAnalyzer
from matid.geometry import get_dimensionality
from matid import SymmetryAnalyzer, Classifier
from matid.classifications import Class0D, Atom, Class1D, Material2D, Surface, Class3D
from nomadcore.structure_types import structure_types_by_spacegroup as str_types_by_spg
......@@ -259,36 +259,37 @@ class SystemNormalizer(SystemBasedNormalizer):
return True
def system_type_analysis(self, atoms) -> None:
def system_type_analysis(self, atoms: ase.Atoms) -> None:
"""
Determine the dimensionality and hence the system type of the system with
MatID. Write the system type to the backend.
Determine the system type with MatID. Write the system type to the
backend.
Args:
atoms
"""
system_type = config.services.unavailable_value
try:
if atoms.get_number_of_atoms() > config.normalize.system_classification_with_clusters_threshold:
# it is too expensive to run MatID's cluster detection, just check pbc
dimensionality = np.sum(atoms.get_pbc())
if atoms.get_number_of_atoms() <= config.normalize.system_classification_with_clusters_threshold:
try:
classifier = Classifier(cluster_threshold=config.normalize.cluster_threshold)
cls = classifier.classify(atoms)
except Exception as e:
self.logger.error(
'matid project system classification failed', exc_info=e, error=str(e))
else:
dimensionality = get_dimensionality(
atoms, cluster_threshold=3.1, return_clusters=False)
if dimensionality is None:
pass
elif dimensionality == 0:
if atoms.get_number_of_atoms() == 1:
if isinstance(cls, Class3D):
system_type = 'bulk'
elif isinstance(cls, Atom):
system_type = 'atom'
else:
elif isinstance(cls, Class0D):
system_type = 'molecule / cluster'
elif dimensionality == 1:
system_type = '1D'
elif dimensionality == 2:
system_type = '2D / surface'
elif dimensionality == 3:
system_type = 'bulk'
except Exception as e:
self.logger.error(
'matid project system classification failed', exc_info=e, error=str(e))
elif isinstance(cls, Class1D):
system_type = '1D'
elif isinstance(cls, Surface):
system_type = 'surface'
elif isinstance(cls, Material2D):
system_type = '2D'
else:
self.logger.info("System type analysis not run due to large system size.")
self._backend.addValue('system_type', system_type)
......
##################################################################################
# 17.06.2014
# graphene
# using a kgrid of 24x24x1
# tight basis settings
# C Tier 2
###########################################################################################
##################################################################################
# SCF Input
##################################################################################
#
# Functional
xc pbe
many_body_dispersion
mbd_scs_dip_cutoff 600
mbd_cfdm_dip_cutoff 600
mbd_scs_vacuum_axis .false. .false. .true.
#
# Charge Mixing
mixer pulay
empty_states 4
# Smearing
occupation_type gaussian 0.1
# Convergence
sc_accuracy_rho 1.E-4
sc_accuracy_eev 5.E-6
sc_accuracy_etot 1.E-5
#
sc_iter_limit 400
# kgrid
k_grid 16 16 1
k_offset 0.03125 0.03125 1.0
################################################################################
#
# FHI-aims code project
# Volker Blum, Fritz Haber Institute Berlin, 2009
#
# Suggested "tight" defaults for C atom (to be pasted into control.in file)
#
################################################################################
species C
# global species definitions
nucleus 6
mass 12.0107
#
l_hartree 8
#
cut_pot 4.0 2.0 1.0
basis_dep_cutoff 1e-4
#
radial_base 34 7.0
radial_multiplier 2
angular_grids specified
# division 0.2187 50
division 0.4416 110
division 0.6335 194
division 0.7727 302
division 0.8772 434
division 0.9334 590
# division 0.9924 770
# division 1.0230 974
# division 1.5020 1202
# outer_grid 974
outer_grid 590
################################################################################
#
# Definition of "minimal" basis
#
################################################################################
# valence basis states
valence 2 s 2.
valence 2 p 2.
# ion occupancy
ion_occ 2 s 1.
ion_occ 2 p 1.
################################################################################
#
# Suggested additional basis functions. For production calculations,
# uncomment them one after another (the most important basis functions are
# listed first).
#
# Constructed for dimers: 1.0 A, 1.25 A, 1.5 A, 2.0 A, 3.0 A
#
################################################################################
# "First tier" - improvements: -1214.57 meV to -155.61 meV
hydro 2 p 1.7
hydro 3 d 6
hydro 2 s 4.9
# "Second tier" - improvements: -67.75 meV to -5.23 meV
hydro 4 f 9.8
hydro 3 p 5.2
hydro 3 s 4.3
hydro 5 g 14.4
hydro 3 d 6.2
# "Third tier" - improvements: -2.43 meV to -0.60 meV
# hydro 2 p 5.6
# hydro 2 s 1.4
# hydro 3 d 4.9
# hydro 4 f 11.2
# "Fourth tier" - improvements: -0.39 meV to -0.18 meV
# hydro 2 p 2.1
# hydro 5 g 16.4
# hydro 4 d 13.2
# hydro 3 s 13.6
# hydro 4 f 17.6
# Further basis functions - improvements: -0.08 meV and below
# hydro 3 s 2
# hydro 3 p 6
# hydro 4 d 20
##############################################
# 17.06.2014
# bulk properties of graphene
# lattice parameter a=2.46559807494, c=20.00
##############################################
#
lattice_vector 1.23279904 -2.13527057 0.00000000
lattice_vector 1.23279904 2.13527057 0.00000000
lattice_vector 0.00000000 0.00000000 20.00
#
# Fractional coordinates:
# L1 L2 L3
atom_frac 0.00000000 0.00000000 0.00000000 C
atom_frac 0.66666666 0.33333333 0.00000000 C
......@@ -78,3 +78,13 @@ def phonon() -> Encyclopedia:
backend = run_normalize(backend)
enc = backend.get_mi2_section(Encyclopedia.m_def)
return enc
@pytest.fixture
def twod() -> Encyclopedia:
parser_name = "parsers/fhi-aims"
filepath = "tests/data/normalizers/encyclopedia/fhiaims_2d_singlepoint/aims.out"
backend = parse_file((parser_name, filepath))
backend = run_normalize(backend)
enc = backend.get_mi2_section(Encyclopedia.m_def)
return enc
......@@ -16,7 +16,7 @@ import pytest
import numpy as np
from nomad.metainfo.encyclopedia import Encyclopedia
from tests.normalizing.conftest import geometry_optimization, molecular_dynamics, phonon # pylint: disable=unused-import
from tests.normalizing.conftest import geometry_optimization, molecular_dynamics, phonon, twod # pylint: disable=unused-import
def test_geometry_optimization(geometry_optimization: Encyclopedia):
......@@ -72,3 +72,10 @@ def test_bulk_information(geometry_optimization: Encyclopedia):
assert go.calculation.cell_angles_string is not None
assert go.calculation.mass_density == 4 * 22.98976928 * 1.6605389e-27 / 1e-30 # Atomic mass in kg / cell volume
assert go.calculation.cell_volume == 1e-30
def test_2d_information(twod: Encyclopedia):
"""Tests that information for 2D systems is correctly processed."
"""
enc = twod
assert enc.material.system_type == "2D"
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment