Commit 3f3130f0 authored by Lauri Himanen's avatar Lauri Himanen
Browse files

Fixed vacuum gap and periodicity detection for 2D and 1D systems.

parent 82efe21c
Pipeline #70717 failed with stages
in 34 minutes and 31 seconds
......@@ -347,10 +347,6 @@ class EncyclopediaNormalizer(Normalizer):
# Put the encyclopedia section into backend
self._backend.add_mi2_section(sec_enc)
self.fill(context)
self.logger.info(
"Successfully created metainfo for Encyclopedia.",
enc_status="success"
)
except Exception as e:
self.logger.info(
......@@ -358,6 +354,11 @@ class EncyclopediaNormalizer(Normalizer):
enc_status="failure",
exc_info=e,
)
else:
self.logger.info(
"Successfully created metainfo for Encyclopedia.",
enc_status="success"
)
class MaterialNormalizer():
......@@ -738,29 +739,22 @@ class Material2DNormalizer(MaterialNormalizer):
material.lattice_parameters = np.array([a, b, 0.0, alpha, 0.0, 0.0])
def periodicity(self, material: Material, std_atoms: Atoms) -> None:
# Determine the periodicity by examining vacuum gaps
vacuum_directions = structure.find_vacuum_directions(
std_atoms,
threshold=config.normalize.cluster_threshold
)
# If one axis is not periodic, return. This only happens if the vacuum
# gap is not aligned with a cell vector.
if sum(vacuum_directions) != 1:
raise ValueError("Could not detect the periodic dimensions in a 2D system.")
material.periodicity = np.invert(vacuum_directions)
# MatID already provides the correct periodicity
material.periodicity = std_atoms.get_pbc()
def get_symmetry_analyzer(self, original_system: Atoms) -> SymmetryAnalyzer:
# Determine the periodicity by examining vacuum gaps
vacuum_directions = structure.find_vacuum_directions(original_system, threshold=7.0)
periodicity = np.invert(vacuum_directions)
# Get dimension of system by also taking into account the covalent radii
dimensions = matid.geometry.get_dimensions(original_system, [True, True, True])
basis_dimensions = np.linalg.norm(original_system.get_cell(), axis=1)
gaps = basis_dimensions - dimensions
periodicity = gaps <= config.normalize.cluster_threshold
# If two axis are not periodic, return. This only happens if the vacuum
# gap is not aligned with a cell vector.
# gap is not aligned with a cell vector or if the linear gap search is
# unsufficient (the structure is "wavy" making also the gap highly
# nonlinear).
if sum(periodicity) != 2:
self.logger.warn("Could not detect the periodic dimensions in a 2D system.")
return False
raise ValueError("Could not detect the periodic dimensions in a 2D system.")
# Center the system in the non-periodic direction, also taking
# periodicity into account. The get_center_of_mass()-function in MatID
......@@ -843,18 +837,18 @@ class Material1DNormalizer(MaterialNormalizer):
material.lattice_parameters = np.array([a, 0.0, 0.0, 0.0, 0.0, 0.0])
def periodicity(self, material: Material, prim_atoms: Atoms) -> None:
# Determine the periodicity by examining vacuum gaps
vacuum_directions = structure.find_vacuum_directions(
prim_atoms,
threshold=config.normalize.cluster_threshold
)
# Get dimension of system by also taking into account the covalent radii
dimensions = matid.geometry.get_dimensions(prim_atoms, [True, True, True])
basis_dimensions = np.linalg.norm(prim_atoms.get_cell(), axis=1)
gaps = basis_dimensions - dimensions
periodicity = gaps <= config.normalize.cluster_threshold
# If one axis is not periodic, return. This only happens if the vacuum
# gap is not aligned with a cell vector.
if sum(vacuum_directions) != 2:
if sum(periodicity) != 1:
raise ValueError("Could not detect the periodic dimensions in a 1D system.")
material.periodicity = np.invert(vacuum_directions)
material.periodicity = periodicity
def get_structure_fingerprint(self, prim_atoms: Atoms) -> str:
"""Calculates a numeric fingerprint that coarsely encodes the atomic
......@@ -939,7 +933,7 @@ class Material1DNormalizer(MaterialNormalizer):
return fingerprint
def get_symmetry_analyzer(self, original_system: Atoms) -> SymmetryAnalyzer:
"""For 1D systems the symmetery is analyzed from the original system
"""For 1D systems the symmetry is analyzed from the original system
with enforced full periodicity.
Args:
......@@ -1058,7 +1052,7 @@ class MethodNormalizer():
try:
method_dict.check(recursive=True)
except (KeyError, ValueError) as e:
self.logger.info("Could not create method hash, missing required information.", exc_info=e)
self.logger.info("Could not create method hash, missing required information: {}".format(str(e)))
else:
method.method_hash = method_dict.hash()
......@@ -1089,7 +1083,7 @@ class MethodNormalizer():
try:
eos_dict.check(recursive=True)
except (KeyError, ValueError) as e:
self.logger.info("Could not create EOS hash, missing required information.", exc_info=e)
self.logger.info("Could not create EOS hash, missing required information: {}".format(str(e)))
else:
method.group_eos_hash = eos_dict.hash()
......@@ -1142,7 +1136,7 @@ class MethodNormalizer():
try:
param_dict.check(recursive=True)
except (KeyError, ValueError) as e:
self.logger.info("Could not create parameter variation hash, missing required information.", exc_info=e)
self.logger.info("Could not create parameter variation hash, missing required information: {}".format(str(e)))
else:
method.group_parametervariation_hash = param_dict.hash()
......
......@@ -20,7 +20,6 @@ from functools import reduce
from typing import List, Dict, Tuple
import numpy as np
from ase import Atoms
from scipy.spatial import Voronoi # pylint: disable=no-name-in-module
from matid.symmetry import WyckoffSet
......@@ -219,46 +218,6 @@ def get_formula_string(symbols: List[str], counts: List[int]) -> str:
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
def get_normalized_wyckoff(atomic_numbers: np.array, wyckoff_letters: np.array) -> Dict[str, Dict[str, int]]:
"""Returns a normalized Wyckoff sequence for the given atomic numbers and
corresponding wyckoff letters. In a normalized sequence the chemical
......
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