Source code for aiida_supercell.calculations

"""AiiDA-Supercell plugin"""

import os

from aiida.engine import CalcJob
from aiida.plugins import DataFactory
from aiida import orm
from aiida.common import CalcInfo, CodeInfo, exceptions


[docs]class SupercellCalculation(CalcJob): """ This is a SupercellCalculation, subclass of JobCalculation, to prepare input for enumerating structures using Supercell program """ _INPUT_FILE = 'aiida.cif' _OUTPUT_FOLDER = 'Output' _OUTPUT_FILE_PREFIX = 'aiida_supercell' _OUTPUT_FILE = 'output.log' _PARSER = 'supercell'
[docs] @classmethod def define(cls, spec): super(SupercellCalculation, cls).define(spec) # Input parameters spec.input( 'structure', valid_type=(orm.StructureData, orm.SinglefileData), required=True, help='Input structure' ) spec.input('random_seed', valid_type=orm.Int, required=False, help='Random seed number') spec.input('charges', valid_type=orm.Dict, required=False, help='Dictionary of formal charges to be used') spec.input( 'calculate_coulomb_energies', valid_type=orm.Bool, required=False, help='Whether to calculate Coulomb energies' ) spec.input( 'charge_balance_method', valid_type=orm.Str, required=False, help='Method to use for charge balancing' ) spec.input( 'merge_symmetric', valid_type=orm.Bool, required=False, help='Whether to merge symmetrically distinct configurations' ) spec.input( 'tolerance', valid_type=orm.Float, default=lambda: orm.Float(0.75), required=False, help='The maximum distance (in Angstroms) between sites that should be contained within the same group.' ) spec.input('supercell_size', valid_type=orm.List, required=True, help='Supercell size for enumeration') spec.input( 'save_as_archive', valid_type=orm.Bool, required=False, help='Whether to save resulting structures as archive' ) spec.input( 'sample_structures', valid_type=orm.Dict, required=False, help='How to sample structures from huge configuration space' ) spec.input('metadata.options.withmpi', valid_type=bool, default=False) # Set parser name to the metadata spec.input('metadata.options.parser_name', valid_type=str, default=cls._PARSER, non_db=True) # Add output_filename attribute to the metadata spec.input('metadata.options.output_filename', valid_type=str, default=cls._OUTPUT_FILE) # Exit codes spec.exit_code( 100, 'ERROR_NO_RETRIEVED_FOLDER', message='The retrieved folder data node could not be accessed.' ) spec.exit_code(101, 'ERROR_ON_INPUT_STRUCTURE', message='Input structure could not be processed.') # Output parameters spec.output('output_parameters', valid_type=orm.Dict, required=True, help='the results of the calculation') spec.output_namespace( 'output_structures', valid_type=orm.StructureData, required=True, dynamic=True, help='relaxed structure' )
# pylint: disable=too-many-statements,too-many-branches
[docs] def prepare_for_submission(self, folder): """Create the input files from the input nodes passed to this instance of the `CalcJob`. :param folder: an `aiida.common.folders.Folder` to temporarily write files on disk :return: `aiida.common.datastructures.CalcInfo` instance """ try: self._write_structure(self.inputs.structure, folder) except exceptions.FailedError: self.exit_codes.ERROR_ON_INPUT_STRUCTURE # pylint: disable=no-member, pointless-statement # get settings settings = self.inputs.settings.get_dict() if 'setting' in self.inputs else {} cmdline_arguments = ['-i', self._INPUT_FILE] if 'supercell_size' in self.inputs: s = self.inputs.supercell_size cmdline_arguments.append(f'-s {s[0]}x{s[1]}x{s[2]}') if self.inputs.merge_symmetric: cmdline_arguments.append('-m') cmdline_arguments.append('-t') cmdline_arguments.append(self.inputs.tolerance.value) if self.inputs.calculate_coulomb_energies: cmdline_arguments.append('-q') cmdline_arguments.append('-g') cmdline_arguments.append(f'-c {self.inputs.charge_balance_method.value}') if 'random_seed' in self.inputs: cmdline_arguments.append(f'--random-seed={self.inputs.random_seed.value}') if 'charges' in self.inputs: charges = self.inputs.charges.get_dict() for key, value in charges.items(): arg = '-p ' + key + ':c=' + str(value) cmdline_arguments.append(arg) if 'sample_structures' in self.inputs: for key, value in self.inputs.sample_structures.get_dict().items(): if key == 'low_energy': cmdline_arguments.append('-n') cmdline_arguments.append(f'l{value}') if key == 'high_energy': cmdline_arguments.append('-n') cmdline_arguments.append(f'h{value}') if key == 'random': cmdline_arguments.append('-n') cmdline_arguments.append(f'r{value}') if key == 'first': cmdline_arguments.append('-n') cmdline_arguments.append(f'f{value}') if key == 'last': cmdline_arguments.append('-n') cmdline_arguments.append(f'a{value}') if key == 'degeneracy': cmdline_arguments.append('-n') cmdline_arguments.append(f'w{value}') if not self.inputs.save_as_archive: cmdline_arguments.append('-o') cmdline_arguments.append(f'{self._OUTPUT_FOLDER}/{self._OUTPUT_FILE_PREFIX}') else: cmdline_arguments.append('-a') cmdline_arguments.append(f'{self._OUTPUT_FOLDER}/{self._OUTPUT_FILE_PREFIX}.tar.gz') # create code info codeinfo = CodeInfo() codeinfo.cmdline_params = settings.pop('cmdline', []) + cmdline_arguments codeinfo.join_files = True codeinfo.code_uuid = self.inputs.code.uuid codeinfo.stdout_name = self._OUTPUT_FILE # create calc info calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.cmdline_params = codeinfo.cmdline_params calcinfo.codes_info = [codeinfo] # Retrive list calcinfo.retrieve_list = [('./Output', '.', 0), self._OUTPUT_FILE] if self.inputs.calculate_coulomb_energies: calcinfo.retrieve_list += [('./Output/aiida_supercell*.txt', '.', 0)] if 'save_as_archive' in self.inputs: if self.inputs.save_as_archive: calcinfo.retrieve_list += [('./Output/aiida_supercell.tar.gz', '.', 0)] calcinfo.retrieve_list += settings.pop('additional_retrieve_list', []) # Create output directory os.makedirs(folder.get_abs_path(self._OUTPUT_FOLDER)) return calcinfo
[docs] @staticmethod def _write_structure(structure, folder): """Function that writes a structure and takes care of element tags""" path = os.path.join(folder.get_abs_path('aiida.cif')) if isinstance(structure, orm.SinglefileData): cif_content = structure.get_content() with open(path, mode='w') as fobj: fobj.write(cif_content) elif isinstance(structure, orm.StructureData): strc_pmg = structure.get_pymatgen_structure() strc_pmg.to(fmt='cif', filename=path)
#EOF