Source code for directupsampling.utils

from collections import Counter
from collections.abc import Iterable

import numpy as np
from ase.atoms import Atoms
from ase.calculators.calculator import Calculator, all_properties
from ase.calculators.mixing import LinearCombinationCalculator
from ase.calculators.singlepoint import SinglePointCalculator


[docs] def ase2mlip_stress(ase_stress: np.ndarray, cell_volume: float): mlip_stress = -1.0 * ase_stress * cell_volume return mlip_stress
[docs] def mlip2ase_stress(mlip_stress: np.ndarray, cell_volume: float): ase_stress = -1.0 * mlip_stress / cell_volume return ase_stress
[docs] def get_calcs_names(*calcs: Calculator): names = [] for i, calc in enumerate(calcs): if calc is not None: if type(calc) is str: name = calc else: name = calc.name else: name = i names.append(name) c = Counter(names) for name, occurance in c.most_common(): if occurance > 1: i = 0 for _ in range(len(names)): if names[_] == name: names[_] = name + f"{i}" i += 1 return tuple(names)
[docs] def chunks(iterable: Iterable, n: int, fill_none: bool = False) -> list: """Yield n chunks from an iterable.""" chunked = [[] for _ in range(n)] for i, item in enumerate(iterable): chunked[i % n].append(item) if fill_none: max_len = max(len(_) for _ in chunked) for chunk in chunked: if len(chunk) < max_len: chunk.append(None) return chunked
# m = len(lst)//n # for i in range(0, len(lst)-m, m): # if i < len(lst)-2*m: # yield lst[i:i + m] # else: # yield lst[i:]
[docs] def gather_chunks(chunked: list[list] | None): lst = [] if chunked is not None: for _ in range(np.max([len(__) for __ in chunked])): for sublst in chunked: if len(sublst) > 0: lst.append(sublst.pop(0)) return lst
[docs] def set_volume(atoms: Atoms, volume: float) -> Atoms: """Set the volume of an Atoms object, keeping the shape of the cell. Parameters ---------- atoms : Atoms ASE Atoms object. volume : float Volume of the simulation cell. Returns ------- Atoms """ cell = atoms.get_cell() current_volume = atoms.get_volume() cell *= (volume / current_volume) ** (1 / 3) atoms.set_cell(cell, scale_atoms=True) return atoms
[docs] def make_single_point_calculator(atoms: Atoms): if isinstance(atoms.calc, LinearCombinationCalculator): sp_calcs = [ SinglePointCalculator(atoms, **_.results) for _ in atoms.calc.mixer.calcs ] for sp_calc, calc in zip(sp_calcs, atoms.calc.mixer.calcs): sp_calc.name = calc.name sp_calc.implemented_properties = ( calc.implemented_properties ) # needed at least for ASE <=3.23 new_calc = LinearCombinationCalculator(sp_calcs, atoms.calc.mixer.weights) else: # Below fixes issue with SinglePointCalculator and some calculators # Revise when stable ASE release has stable fix results = {k: v for k, v in atoms.calc.results.items() if k in all_properties} new_calc = SinglePointCalculator(atoms, **results) new_calc.name = atoms.calc.name return new_calc