Source code for aiida_crystal17.parsers.migrate

"""
module to create inputs from existing CRYSTAL17 runs
"""
import os
import tempfile

import ase
from aiida.parsers.exceptions import OutputParsingError
from aiida_crystal17.parsers.inputd12_read import extract_data
from ejplugins.crystal import CrystalOutputPlugin


# pylint: disable=too-many-locals
[docs]def create_inputs(inpath, outpath): """ create ``crystal17.main`` input nodes from an existing run NB: none of the nodes are stored, also existing basis will be retrieved if availiable :param inpath: path to .d12 file :param outpath: path to .out file :return: dictionary of inputs, with keys 'structure', 'parameters', 'settings', 'structure', 'basis' """ from aiida.orm import DataFactory, CalculationFactory calc_cls = CalculationFactory('crystal17.main') basis_cls = DataFactory('crystal17.basisset') struct_cls = DataFactory('structure') structsettings_cls = DataFactory('crystal17.structsettings') inputs = {} with open(inpath) as f: d12content = f.read() output_dict, basis_sets, atom_props = extract_data(d12content) cryparse = CrystalOutputPlugin() if not os.path.exists(outpath): raise OutputParsingError( "The raw data file does not exist: {}".format(outpath)) with open(outpath) as f: try: data = cryparse.read_file(f, log_warnings=False) except IOError as err: raise OutputParsingError( "Error in CRYSTAL 17 run output: {}".format(err)) # we retrieve the initial primitive geometry and symmetry atoms = _create_atoms(data) # convert fragment (i.e. unfixed) to fixed if "fragment" in atom_props: frag = atom_props.pop("fragment") atom_props["fixed"] = [ i + 1 for i in range(atoms.get_number_of_atoms()) if i + 1 not in frag ] atoms.set_tags(_create_tags(atom_props, atoms)) structure = struct_cls(ase=atoms) inputs['structure'] = structure settings_dict = {"kinds": {}} for key, vals in atom_props.items(): settings_dict["kinds"][key] = [ structure.sites[i - 1].kind_name for i in vals ] settings_dict["operations"] = data["initial"]["primitive_symmops"] # TODO retrieve centering code, crystal system and spacegroup settings_dict["space_group"] = 1 settings_dict["crystal_type"] = 1 settings_dict["centring_code"] = 1 settings = structsettings_cls(data=settings_dict) parameters = calc_cls.prepare_and_validate(output_dict, structure, settings) inputs['parameters'] = parameters inputs['settings'] = settings inputs["basis"] = {} for bset in basis_sets: bfile = tempfile.NamedTemporaryFile(delete=False) try: with open(bfile.name, "w") as f: f.write(bset) bdata, _ = basis_cls.get_or_create( bfile.name, use_first=False, store_basis=False) # TODO report if bases created or retrieved finally: os.remove(bfile.name) inputs["basis"][bdata.element] = bdata return inputs
def _create_atoms(data, section="initial"): """create ase.Atoms from ejplugins parsed data""" cell_data = data[section]["primitive_cell"] cell_vectors = [] for n in "a b c".split(): assert cell_data["cell_vectors"][n]["units"] == "angstrom" cell_vectors.append(cell_data["cell_vectors"][n]["magnitude"]) ccoords = cell_data["ccoords"]["magnitude"] atoms = ase.Atoms( cell=cell_vectors, pbc=cell_data["pbc"], symbols=cell_data["symbols"], positions=ccoords) return atoms def _create_tags(atom_props, atoms): """create tags based on atom properties""" kinds = {} for i, symbol in enumerate(atoms.get_chemical_symbols()): signature = [] kinds[symbol] = kinds.get(symbol, {}) for key, val in atom_props.items(): if i + 1 in val: signature.append(key) signature = ".".join(signature) kinds[symbol][signature] = kinds[symbol].get(signature, []) + [i + 1] tags = [] for i, symbol in enumerate(atoms.get_chemical_symbols()): for j, key in enumerate(sorted(kinds[symbol].keys())): if i + 1 in kinds[symbol][key]: tags.append(j) assert len(tags) == atoms.get_number_of_atoms() return tags