Commit 8ea11000 authored by Alvin Noe Ladines's avatar Alvin Noe Ladines Committed by Markus Scheidgen
Browse files

Removed backed in normalizing

parent f7e22b11
......@@ -4,18 +4,17 @@ import numpy as np
from typing import Tuple, List
from nomad.units import ureg
from nomad.parsing.legacy import Backend
from nomad.metainfo import Section
from nomad.utils import RestrictedDict
def get_basis_set(context, backend: Backend, logger) -> RestrictedDict:
def get_basis_set(context, entry_archive, logger) -> RestrictedDict:
"""Decide which type of basis set settings are applicable to the entry and
return a corresponding settings as a RestrictedDict.
Args:
context: The calculation context.
backend: Backend from which values are extracted.
entry_archive: EntryArchive from which values are extracted.
logger: Shared logger.
Returns:
......@@ -24,11 +23,11 @@ def get_basis_set(context, backend: Backend, logger) -> RestrictedDict:
returns None.
"""
settings: BasisSet = None
program_name = backend.entry_archive.section_run[0].program_name
program_name = entry_archive.section_run[0].program_name
if program_name == "exciting":
settings = BasisSetExciting(context, backend, logger)
settings = BasisSetExciting(context, entry_archive, logger)
elif program_name == "FHI-aims":
settings = BasisSetFHIAims(context, backend, logger)
settings = BasisSetFHIAims(context, entry_archive, logger)
else:
return None
......@@ -40,11 +39,11 @@ class BasisSet(ABC):
subclasses that inherit this class and hierarchically add new mandatory and
optional settings with the setup()-function.
"""
def __init__(self, context, backend, logger):
def __init__(self, context, entry_archive, logger):
"""
"""
self._ctx = context
self._backend = backend
self._entry_archive = entry_archive
self._logger = logger
mandatory, optional = self.setup()
self.settings = RestrictedDict(mandatory, optional, forbidden_values=[None])
......
......@@ -180,32 +180,32 @@ class EncyclopediaNormalizer(Normalizer):
# Fill structure related metainfo
struct: Any = None
if context.material_type == Material.material_type.type.bulk:
struct = MaterialBulkNormalizer(self.backend, self.logger)
struct = MaterialBulkNormalizer(self.entry_archive, self.logger)
elif context.material_type == Material.material_type.type.two_d:
struct = Material2DNormalizer(self.backend, self.logger)
struct = Material2DNormalizer(self.entry_archive, self.logger)
elif context.material_type == Material.material_type.type.one_d:
struct = Material1DNormalizer(self.backend, self.logger)
struct = Material1DNormalizer(self.entry_archive, self.logger)
if struct is not None:
struct.normalize(context)
# Fill method related metainfo
method = None
if context.method_type == Method.method_type.type.DFT or context.method_type == Method.method_type.type.DFTU:
method = MethodDFTNormalizer(self._backend, self.logger)
method = MethodDFTNormalizer(self.entry_archive, self.logger)
elif context.method_type == Method.method_type.type.GW:
method = MethodGWNormalizer(self._backend, self.logger)
method = MethodGWNormalizer(self.entry_archive, self.logger)
if method is not None:
method.normalize(context)
# Fill properties related metainfo
properties = PropertiesNormalizer(self.backend, self.logger)
properties = PropertiesNormalizer(self.entry_archive, self.logger)
properties.normalize(context)
def normalize(self, logger=None) -> None:
"""The caller will automatically log if the normalizer succeeds or ends
up with an exception.
"""
sec_enc = self.backend.entry_archive.section_metadata.m_create(EncyclopediaMetadata)
sec_enc = self.entry_archive.section_metadata.m_create(EncyclopediaMetadata)
status_enums = EncyclopediaMetadata.status.type
calc_enums = Calculation.calculation_type.type
......@@ -222,7 +222,6 @@ class EncyclopediaNormalizer(Normalizer):
try:
super().normalize(logger)
# Initialise metainfo structure
material = sec_enc.m_create(Material)
method = sec_enc.m_create(Method)
......
......@@ -35,7 +35,6 @@ from nomad.datamodel.encyclopedia import (
LatticeParameters,
)
from nomad.normalizing.encyclopedia.context import Context
from nomad.parsing.legacy import Backend
from nomad.metainfo import Section
from nomad import atomutils
from nomad.utils import hash
......@@ -48,9 +47,9 @@ class MaterialNormalizer():
"""A base class that is used for processing material-related information
in the Encylopedia.
"""
def __init__(self, backend: Backend, logger):
self.backend = backend
def __init__(self, entry_archive, logger):
self.logger = logger
self.entry_archive = entry_archive
def atom_labels(self, ideal: IdealizedStructure, std_atoms: Atoms) -> None:
ideal.atom_labels = std_atoms.get_chemical_symbols()
......@@ -372,7 +371,7 @@ class MaterialBulkNormalizer(MaterialNormalizer):
def normalize(self, context: Context) -> None:
# Fetch resources
sec_system = context.representative_system
sec_enc = self.backend.entry_archive.section_metadata.encyclopedia
sec_enc = self.entry_archive.section_metadata.encyclopedia
material = sec_enc.material
properties = sec_enc.properties
sec_symmetry = sec_system["section_symmetry"][0]
......@@ -498,7 +497,7 @@ class Material2DNormalizer(MaterialNormalizer):
def normalize(self, context: Context) -> None:
# Fetch resources
sec_enc = self.backend.entry_archive.section_metadata.encyclopedia
sec_enc = self.entry_archive.section_metadata.encyclopedia
material = sec_enc.material
repr_atoms = context.representative_system.m_cache["representative_atoms"] # Temporary value stored by SystemNormalizer
symmetry_analyzer = self.get_symmetry_analyzer(repr_atoms)
......@@ -716,7 +715,7 @@ class Material1DNormalizer(MaterialNormalizer):
def normalize(self, context: Context) -> None:
# Fetch resources
sec_system = context.representative_system
sec_enc = self.backend.entry_archive.section_metadata.encyclopedia
sec_enc = self.entry_archive.section_metadata.encyclopedia
material = sec_enc.material
repr_atoms = sec_system.m_cache["representative_atoms"] # Temporary value stored by SystemNormalizer
symmetry_analyzer = self.get_symmetry_analyzer(repr_atoms)
......
......@@ -33,10 +33,10 @@ class MethodNormalizer():
"""A base class that is used for processing method related information
in the Encylopedia.
"""
def __init__(self, backend, logger):
self.backend = backend
def __init__(self, entry_archive, logger):
self.logger = logger
self.section_run = backend.entry_archive.section_run[0]
self.entry_archive = entry_archive
self.section_run = entry_archive.section_run[0]
def method_id(self, method: Method, settings_basis_set: RestrictedDict, repr_method: Section):
method_dict = RestrictedDict(
......@@ -76,7 +76,7 @@ class MethodNormalizer():
)
# Only calculations from the same upload are grouped
eos_dict['upload_id'] = self.backend.entry_archive.section_metadata.upload_id
eos_dict['upload_id'] = self.entry_archive.section_metadata.upload_id
# Method
eos_dict["method_id"] = method.method_id
......@@ -106,11 +106,11 @@ class MethodNormalizer():
)
# Only calculations from the same upload are grouped
param_dict['upload_id'] = self.backend.entry_archive.section_metadata.upload_id
param_dict['upload_id'] = self.entry_archive.section_metadata.upload_id
# The same code and functional type is required
param_dict['program_name'] = self.backend["program_name"]
param_dict['program_version'] = self.backend["program_version"]
param_dict['program_name'] = self.section_run.program_name
param_dict['program_version'] = self.section_run.program_version
# Get a string representation of the geometry. It is included as the
# geometry should remain the same during parameter variation. By simply
......@@ -166,7 +166,7 @@ class MethodDFTNormalizer(MethodNormalizer):
"""
def core_electron_treatment(self, method: Method) -> None:
treatment = config.services.unavailable_value
code_name = self.backend["program_name"]
code_name = self.section_run.program_name
if code_name is not None:
core_electron_treatments = {
'VASP': 'pseudopotential',
......@@ -382,10 +382,10 @@ class MethodDFTNormalizer(MethodNormalizer):
# Fetch resources
repr_method = context.representative_method
repr_system = context.representative_system
sec_enc = self.backend.entry_archive.section_metadata.encyclopedia
sec_enc = self.entry_archive.section_metadata.encyclopedia
method = sec_enc.method
material = sec_enc.material
settings_basis_set = get_basis_set(context, self.backend, self.logger)
settings_basis_set = get_basis_set(context, self.entry_archive, self.logger)
# Fill metainfo
self.core_electron_treatment(method)
......@@ -421,7 +421,7 @@ class MethodGWNormalizer(MethodDFTNormalizer):
def normalize(self, context: Context) -> None:
# Fetch resources
repr_method = context.representative_method
sec_enc = self.backend.entry_archive.section_metadata.encyclopedia
sec_enc = self.entry_archive.section_metadata.encyclopedia
method = sec_enc.method
# Fill metainfo
......
......@@ -17,7 +17,6 @@ from nomad.datamodel.encyclopedia import (
Properties,
Energies,
)
from nomad.parsing.legacy import Backend
from nomad.metainfo import Section
from nomad.normalizing.encyclopedia.context import Context
......@@ -26,8 +25,8 @@ class PropertiesNormalizer():
"""A base class that is used for processing calculated quantities that
should be extracted to Encyclopedia.
"""
def __init__(self, backend: Backend, logger):
self.backend = backend
def __init__(self, entry_archive, logger):
self.entry_archive = entry_archive
self.logger = logger
def electronic_band_structure(self, properties: Properties, calc_type: str, material_type: str, context: Context, sec_system: Section) -> None:
......@@ -131,7 +130,7 @@ class PropertiesNormalizer():
"""
try:
resolved_section = None
frame_sequences = self.backend.entry_archive.section_run[0].section_frame_sequence
frame_sequences = self.entry_archive.section_run[0].section_frame_sequence
for frame_sequence in reversed(frame_sequences):
thermodynamical_props = frame_sequence.section_thermodynamical_properties
for thermodynamical_prop in thermodynamical_props:
......@@ -224,7 +223,7 @@ class PropertiesNormalizer():
return
# Fetch resources
sec_enc = self.backend.entry_archive.section_metadata.encyclopedia
sec_enc = self.entry_archive.section_metadata.encyclopedia
properties = sec_enc.properties
calc_type = context.calc_type
material_type = context.material_type
......
......@@ -12,17 +12,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Any, Dict, cast
from typing import Any, Dict
import numpy as np
import re
import ase.data
from string import ascii_uppercase
import pint.quantity
from nomad.parsing.legacy import Backend
from nomad.normalizing.normalizer import SystemBasedNormalizer
from nomad.units import ureg
from nomad.datamodel import OptimadeEntry, Species, DFTMetadata, EntryMetadata
from nomad.datamodel.metainfo.public import section_system
species_re = re.compile(r'^([A-Z][a-z]?)(\d*)$')
......@@ -43,16 +43,15 @@ class OptimadeNormalizer(SystemBasedNormalizer):
Normalizes geometry, classifies, system_type, and runs symmetry analysis.
'''
backend = cast(Backend, self._backend)
if backend.entry_archive.section_metadata is None:
backend.entry_archive.m_create(EntryMetadata)
if backend.entry_archive.section_metadata.dft is None:
backend.entry_archive.section_metadata.m_create(DFTMetadata)
optimade = backend.entry_archive.section_metadata.dft.m_create(OptimadeEntry)
if self.entry_archive.section_metadata is None:
self.entry_archive.m_create(EntryMetadata)
if self.entry_archive.section_metadata.dft is None:
self.entry_archive.section_metadata.m_create(DFTMetadata)
optimade = self.entry_archive.section_metadata.dft.m_create(OptimadeEntry)
def get_value(key: str, default: Any = None, numpy: bool = False, unit=None) -> Any:
def get_value(quantity_def, default: Any = None, numpy: bool = False, unit=None) -> Any:
try:
value = self._backend.get_value(key, index)
value = self.section_run.section_system[-1].m_get(quantity_def)
if type(value) == np.ndarray and not numpy:
return value.tolist()
if isinstance(value, list) and numpy:
......@@ -70,7 +69,7 @@ class OptimadeNormalizer(SystemBasedNormalizer):
from nomad.normalizing.system import normalized_atom_labels
nomad_species = get_value('atom_labels')
nomad_species = get_value(section_system.atom_labels)
# elements
atoms = normalized_atom_labels(nomad_species)
......@@ -89,8 +88,8 @@ class OptimadeNormalizer(SystemBasedNormalizer):
for element in optimade.elements]
# formulas
optimade.chemical_formula_reduced = get_value('chemical_composition_reduced')
optimade.chemical_formula_hill = get_value('chemical_composition_bulk_reduced')
optimade.chemical_formula_reduced = get_value(section_system.chemical_composition_reduced)
optimade.chemical_formula_hill = get_value(section_system.chemical_composition_bulk_reduced)
optimade.chemical_formula_descriptive = optimade.chemical_formula_hill
optimade.chemical_formula_anonymous = ''
for i in range(len(optimade.elements)):
......@@ -102,11 +101,11 @@ class OptimadeNormalizer(SystemBasedNormalizer):
# sites
optimade.nsites = len(nomad_species)
optimade.species_at_sites = nomad_species
optimade.lattice_vectors = get_value('lattice_vectors', numpy=True, unit=ureg.m)
optimade.cartesian_site_positions = get_value('atom_positions', numpy=True, unit=ureg.m)
optimade.lattice_vectors = get_value(section_system.lattice_vectors, numpy=True, unit=ureg.m)
optimade.cartesian_site_positions = get_value(section_system.atom_positions, numpy=True, unit=ureg.m)
optimade.dimension_types = [
1 if value else 0
for value in get_value('configuration_periodic_dimensions')]
for value in get_value(section_system.configuration_periodic_dimensions)]
# species
for species_label in set(nomad_species):
......
......@@ -24,6 +24,9 @@ from matid.classifications import Class0D, Atom, Class1D, Material2D, Surface, C
from nomad import atomutils, archive
from nomad import utils, config
from nomad.datamodel.metainfo.public import section_symmetry, section_std_system, \
section_primitive_system, section_original_system, section_springer_material, \
section_prototype, section_system
from .normalizer import SystemBasedNormalizer
......@@ -89,10 +92,13 @@ class SystemNormalizer(SystemBasedNormalizer):
Returns: True, iff the normalization was successful
'''
if self.section_run is None:
self.logger.error('section_run is not present.')
return False
def get_value(key: str, default: Any = None, numpy: bool = True) -> Any:
def get_value(quantity_def, default: Any = None, numpy: bool = True) -> Any:
try:
value = self._backend.get_value(key, system.m_parent_index)
value = system.m_get(quantity_def)
if not numpy and type(value).__module__ == np.__name__:
value = value.tolist()
......@@ -103,18 +109,15 @@ class SystemNormalizer(SystemBasedNormalizer):
except (KeyError, IndexError):
return default
def set_value(key: str, value: Any):
self._backend.addValue(key, value)
if is_representative:
self._backend.addValue('is_representative', is_representative)
system.is_representative = is_representative
# analyze atoms labels
atom_labels = get_value('atom_labels', numpy=False)
atom_labels = get_value(section_system.atom_labels, numpy=False)
if atom_labels is not None:
atom_labels = normalized_atom_labels(atom_labels)
atom_species = get_value('atom_species', numpy=False)
atom_species = get_value(section_system.atom_species, numpy=False)
if atom_labels is None and atom_species is None:
self.logger.warn('system has neither atom species nor labels')
return False
......@@ -127,7 +130,7 @@ class SystemNormalizer(SystemBasedNormalizer):
self.logger.error('system has atom species that are out of range')
return False
self._backend.addArrayValues('atom_labels', atom_labels)
system.atom_labels = atom_labels
# At this point we should have atom labels.
try:
......@@ -144,7 +147,7 @@ class SystemNormalizer(SystemBasedNormalizer):
if atom_species is None:
atom_species = atoms.get_atomic_numbers().tolist()
self._backend.addArrayValues('atom_species', atom_species)
system.atom_species = atom_species
else:
if not isinstance(atom_species, list):
atom_species = [atom_species]
......@@ -153,14 +156,14 @@ class SystemNormalizer(SystemBasedNormalizer):
'atom species do not match labels',
atom_labels=atom_labels[:10], atom_species=atom_species[:10])
atom_species = atoms.get_atomic_numbers().tolist()
set_value('atom_species', atom_species)
system.atom_species = atom_species
# periodic boundary conditions
pbc = get_value('configuration_periodic_dimensions', numpy=False)
pbc = get_value(section_system.configuration_periodic_dimensions, numpy=False)
if pbc is None:
pbc = [False, False, False]
self.logger.warning('missing configuration_periodic_dimensions')
set_value('configuration_periodic_dimensions', pbc)
system.configuration_periodic_dimensions = pbc
try:
atoms.set_pbc(pbc)
except Exception as e:
......@@ -169,12 +172,12 @@ class SystemNormalizer(SystemBasedNormalizer):
return False
# formulas
set_value('chemical_composition', atoms.get_chemical_formula(mode='all'))
set_value('chemical_composition_reduced', atoms.get_chemical_formula(mode='reduce'))
set_value('chemical_composition_bulk_reduced', atoms.get_chemical_formula(mode='hill'))
system.chemical_composition = atoms.get_chemical_formula(mode='all')
system.chemical_composition_reduced = atoms.get_chemical_formula(mode='reduce')
system.chemical_composition_bulk_reduced = atoms.get_chemical_formula(mode='hill')
# positions
atom_positions = get_value('atom_positions', None, numpy=True)
atom_positions = get_value(section_system.atom_positions, numpy=True)
if atom_positions is None:
self.logger.warning('no atom positions, skip further system analysis')
return False
......@@ -191,11 +194,11 @@ class SystemNormalizer(SystemBasedNormalizer):
return False
# lattice vectors
lattice_vectors = get_value('lattice_vectors', numpy=True)
lattice_vectors = get_value(section_system.lattice_vectors, numpy=True)
if lattice_vectors is None:
lattice_vectors = get_value('simulation_cell', numpy=True)
lattice_vectors = get_value(section_system.simulation_cell, numpy=True)
if lattice_vectors is not None:
set_value('lattice_vectors', lattice_vectors)
system.lattice_vectors = lattice_vectors
if lattice_vectors is None:
if any(pbc):
self.logger.error('no lattice vectors but periodicity', pbc=pbc)
......@@ -213,7 +216,7 @@ class SystemNormalizer(SystemBasedNormalizer):
atoms.cell.tolist() if atoms.cell is not None else None,
atoms.pbc.tolist()]
configuration_id = utils.hash(json.dumps(configuration).encode('utf-8'))
set_value('configuration_raw_gid', configuration_id)
system.configuration_raw_gid = configuration_id
if is_representative:
# Save the Atoms as a temporary variable
......@@ -226,8 +229,7 @@ class SystemNormalizer(SystemBasedNormalizer):
system_size=len(atoms)):
self.system_type_analysis(atoms)
system_type = self._backend.get_value("system_type")
system_type = system.system_type
# Symmetry analysis
if atom_positions is not None and (lattice_vectors is not None or not any(pbc)) and system_type == "bulk":
with utils.timer(
......@@ -240,7 +242,7 @@ class SystemNormalizer(SystemBasedNormalizer):
def system_type_analysis(self, atoms: Atoms) -> None:
'''
Determine the system type with MatID. Write the system type to the
backend.
entry_archive.
Args:
atoms: The structure to analyse
......@@ -269,22 +271,23 @@ class SystemNormalizer(SystemBasedNormalizer):
system_type = '2D'
else:
self.logger.info("system type analysis not run due to large system size")
self._backend.addValue('system_type', system_type)
idx = self.section_run.m_cache["representative_system_idx"]
self.section_run.section_system[idx].system_type = system_type
self.section_run.section_system[-1].system_type = system_type
def symmetry_analysis(self, system, atoms: ase.Atoms) -> None:
'''Analyze the symmetry of the material being simulated. Only performed
for bulk materials.
We feed in the parsed values in section_system to the the symmetry
analyzer. The analysis results are written to the backend.
analyzer. The analysis results are written to the entry_archive.
Args:
atoms: The atomistic structure to analyze.
Returns:
None: The method should write symmetry variables
to the backend which is member of this class.
to the entry_archive which is member of this class.
'''
# Try to use MatID's symmetry analyzer to analyze the ASE object.
try:
......@@ -329,7 +332,8 @@ class SystemNormalizer(SystemBasedNormalizer):
# Write data extracted from MatID's symmetry analysis to the
# representative section_system.
sec_symmetry = self._backend.openSection("section_symmetry", return_section=True)
sec_symmetry = system.m_create(section_symmetry)
sec_symmetry.m_cache["symmetry_analyzer"] = symm
sec_symmetry.symmetry_method = 'MatID (spg)'
......@@ -343,21 +347,21 @@ class SystemNormalizer(SystemBasedNormalizer):
sec_symmetry.origin_shift = origin_shift
sec_symmetry.transformation_matrix = transform
sec_std = self._backend.openSection("section_std_system", return_section=True)
sec_std = sec_symmetry.m_create(section_std_system)
sec_std.lattice_vectors_std = conv_cell
sec_std.atom_positions_std = conv_pos
sec_std.atomic_numbers_std = conv_num
sec_std.wyckoff_letters_std = conv_wyckoff
sec_std.equivalent_atoms_std = conv_equivalent_atoms
sec_prim = self._backend.openSection("section_primitive_system", return_section=True)
sec_prim = sec_symmetry.m_create(section_primitive_system)
sec_prim.lattice_vectors_primitive = prim_cell
sec_prim.atom_positions_primitive = prim_pos
sec_prim.atomic_numbers_primitive = prim_num
sec_prim.wyckoff_letters_primitive = prim_wyckoff
sec_prim.equivalent_atoms_primitive = prim_equivalent_atoms
sec_orig = self._backend.openSection("section_original_system", return_section=True)
sec_orig = sec_symmetry.m_create(section_original_system)
sec_orig.wyckoff_letters_original = orig_wyckoff
sec_orig.equivalent_atoms_original = orig_equivalent_atoms
......@@ -367,25 +371,24 @@ class SystemNormalizer(SystemBasedNormalizer):
def springer_classification(self, atoms, space_group_number):
normalized_formula = formula_normalizer(atoms)
springer_data = query_springer_data(normalized_formula, space_group_number)
idx = self.section_run.m_cache["representative_system_idx"]
for material in springer_data.values():
self._backend.openNonOverlappingSection('section_springer_material')
sec_springer_mat = self.section_run.section_system[idx].m_create(section_springer_material)
self._backend.addValue('springer_id', material['spr_id'])
self._backend.addValue('springer_alphabetical_formula', material['spr_aformula'])
self._backend.addValue('springer_url', material['spr_url'])
sec_springer_mat.springer_id = material['spr_id']
sec_springer_mat.springer_alphabetical_formula = material['spr_aformula']
sec_springer_mat.springer_url = material['spr_url']
compound_classes = material['spr_compound']
if compound_classes is None:
compound_classes = []
self._backend.addArrayValues('springer_compound_class', compound_classes)
sec_springer_mat.springer_compound_class = compound_classes
classifications = material['spr_classification']
if classifications is None:
classifications = []
self._backend.addArrayValues('springer_classification', classifications)
self._backend.closeNonOverlappingSection('section_springer_material')
sec_springer_mat.springer_classification = classifications
# Check the 'springer_classification' and 'springer_compound_class' information
# found is the same for all springer_id's
......@@ -424,7 +427,8 @@ class SystemNormalizer(SystemBasedNormalizer):
aflow_prototype_name,
protoDict.get("Pearsons Symbol", "-")
)
sec_prototype = self._backend.openSection("section_prototype", return_section=True)
idx = self.section_run.m_cache["representative_system_idx"]
sec_prototype = self.section_run.section_system[idx].m_create(section_prototype)
sec_prototype.prototype_label = prototype_label
sec_prototype.prototype_aflow_id = aflow_prototype_id
sec_prototype.prototype_aflow_url = aflow_prototype_url
......
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