def calculate_phonons(self): self.ctx.inputs.crystal.structure = self.ctx.optimise.outputs.output_structure self.ctx.inputs.crystal.parameters = get_data_class('dict')(dict=self.ctx.crystal_parameters.phonons) self.ctx.inputs.crystal.options = get_data_class('dict')( dict=self.construct_metadata(PHONON_LABEL)) crystal_run = self.submit(BaseCrystalWorkChain, **self.ctx.inputs.crystal) return self.to_context(phonons=crystal_run)
def define(cls, spec): super(BasePropertiesWorkChain, cls).define(spec) # define inputs spec.input('code', valid_type=Code) spec.input('wavefunction', valid_type=get_data_class('singlefile'), required=True) spec.input('parameters', valid_type=get_data_class('dict'), required=True) spec.input('options', valid_type=get_data_class('dict'), required=True, help="Calculation options") # define workchain routine spec.outline(cls.init_calculation, cls.run_calculation, cls.retrieve_results) # define outputs spec.output('output_bands', valid_type=get_data_class('array.bands'), required=False) spec.output('output_dos', valid_type=get_data_class('array'), required=False)
def calculate_elastic_constants(self): if self.ctx.need_elastic_constants: # run elastic calc with optimised structure self.ctx.inputs.crystal.structure = self.ctx.optimise.outputs.output_structure options = self.construct_metadata(ELASTIC_LABEL) if "oxidation_states" in self.ctx.optimise.outputs: options["use_oxidation_states"] = self.ctx.optimise.outputs.oxidation_states.get_dict() self.ctx.inputs.crystal.parameters = get_data_class('dict')( dict=self.ctx.crystal_parameters.elastic_constants) self.ctx.inputs.crystal.options = get_data_class('dict')(dict=options) crystal_run = self.submit(BaseCrystalWorkChain, **self.ctx.inputs.crystal) return self.to_context(elastic_constants=crystal_run) else: self.logger.warning("Skipping elastic constants calculation")
def listfamilies(element, list_pks, with_description): """List available families of CRYSTAL Basis Set files.""" basis_family_cls = get_data_class('crystal_dft.basis_family') bases, groups = basis_family_cls.get_families(filter_elements=element) table = [['Family', 'Num Basis Sets']] if with_description: table[0].append('Description') if list_pks: table[0].append('Pks') for basis in bases: row = [basis.name, '--'] if with_description: row.append('Predefined') if list_pks: row.append('') table.append(row) for group in groups: row = [group.label, len(group.nodes)] if with_description: row.append(group.description) if list_pks: row.append(",".join([str(n.pk) for n in group.nodes])) table.append(row) if len(table) > 1: click.echo(tabulate.tabulate(table, headers='firstrow')) click.echo() elif element: click.echo( 'No Basis Set family contains all given elements and symbols.') else: click.echo('No Basis Set family available.')
def createpredefined(): """Create predefined basis families""" basis_family_cls = get_data_class('crystal_dft.basis_family') with cli_spinner(): created = basis_family_cls.create_predefined() msg = 'Created {} predefined basis families'.format(len(created)) if created: msg += ':\n{}'.format(', '.join(created)) click.echo(msg)
def define(cls, spec): super(RunCryWorkChain, cls).define(spec) # define inputs spec.input('crystal_code', valid_type=Code) spec.input('properties_code', valid_type=Code) spec.expose_inputs(BaseCrystalWorkChain, include=['structure', 'basis_family']) spec.input('crystal_parameters', valid_type=get_data_class('parameter'), required=True) spec.input('properties_parameters', valid_type=get_data_class('parameter'), required=True) spec.input('options', valid_type=get_data_class('parameter'), required=True, help="Calculation options") # define workchain routine spec.outline(cls.init_inputs, cls.run_crystal_calc, cls.run_properties_calc, cls.retrieve_results) # define outputs spec.expose_outputs(BaseCrystalWorkChain) spec.expose_outputs(BasePropertiesWorkChain)
def uploadfamily(path, ext, name, description): """Upload a family of CRYSTAL Basis Set files.""" basis_family_cls = get_data_class('crystal_dft.basis_family') with cli_spinner(): nfiles, created, uploaded = basis_family_cls.upload( name, path, extension=".{}".format(ext), description=description ) msg = 'Basis set files found: {}, out of them uploaded: {}'.format(nfiles, len(uploaded)) if uploaded: msg += ' (for elements: {})'.format(', '.join(sorted(list(uploaded)))) msg += ' to {}basis family {}'.format('newly created ' if created else '', name) click.echo(msg)
def get_geometry(self): """ Getting geometry from MPDS database """ key = os.getenv('MPDS_KEY') client = MPDSDataRetrieval(api_key=key, verbose=False) query_dict = self.inputs.mpds_query.get_dict() # Add direct structures submitting support: FIXME assert query_dict or self.inputs.struct_in if not query_dict: return self.inputs.struct_in # insert props: atomic structure to query. Might check if it's already set to smth query_dict['props'] = 'atomic structure' try: answer = client.get_data( query_dict, fields={'S': [ 'cell_abc', 'sg_n', 'basis_noneq', 'els_noneq' ]} ) except APIError as ex: if ex.code == 429: self.logger.warning("Too many parallel MPDS requests, chilling") time.sleep(random.choice([2 * 2**m for m in range(5)])) return self.get_geometry() else: raise structs = [client.compile_crystal(line, flavor='ase') for line in answer] structs = list(filter(None, structs)) if not structs: raise APIError('No crystal structures returned') minimal_struct = min([len(s) for s in structs]) # get structures with minimal number of atoms and find the one with median cell vectors cells = np.array([s.get_cell().reshape(9) for s in structs if len(s) == minimal_struct]) median_cell = np.median(cells, axis=0) median_idx = int(np.argmin(np.sum((cells - median_cell) ** 2, axis=1) ** 0.5)) return get_data_class('structure')(ase=structs[median_idx])
def _set_default_parameters(self, parameters): """Set defaults to calculation parameters""" parameters_dict = parameters.get_dict() from aiida_crystal_dft.io.f9 import Fort9 with self.inputs.wavefunction.open() as f: file_name = f.name wf = Fort9(file_name) if 'band' in parameters_dict: # automatic generation of k-point path if 'bands' not in parameters_dict['band']: self.logger.info( 'Proceeding with automatic generation of k-points path') structure = wf.get_structure() shrink, points, path = get_shrink_kpoints_path(structure) parameters_dict['band']['shrink'] = shrink parameters_dict['band']['bands'] = path # automatic generation of first and last band if 'first' not in parameters_dict['band']: parameters_dict['band']['first'] = 1 if 'last' not in parameters_dict['band']: parameters_dict['band']['last'] = wf.get_ao_number() if 'dos' in parameters_dict: # automatic generation of projections in case no projections are given # TODO: explicit asking for automatic projections if ('projections_atoms' not in parameters_dict['dos'] and 'projections_orbitals' not in parameters_dict['dos']): self.logger.info( 'Proceeding with automatic generation of dos atomic projections' ) parameters_dict['dos'][ 'projections_atoms'] = get_dos_projections_atoms( wf.get_atomic_numbers()) # automatic generation of first and last band if 'first' not in parameters_dict['dos']: parameters_dict['dos']['first'] = 1 if 'last' not in parameters_dict['dos']: parameters_dict['dos']['last'] = wf.get_ao_number() return get_data_class('dict')(dict=parameters_dict)
def define(cls, spec): super(MPDSCrystalWorkchain, cls).define(spec) # define code inputs spec.input('crystal_code', valid_type=Code, required=True) spec.input('properties_code', valid_type=Code, required=False) # MPDS phase id spec.input('mpds_query', valid_type=get_data_class('dict'), required=True) # Add direct structures submitting support: FIXME spec.input('struct_in', valid_type=get_data_class('structure'), required=False) # Basis set spec.expose_inputs(BaseCrystalWorkChain, include=['basis_family']) # Parameters (include OPTGEOM, FREQCALC and ELASTCON) spec.input('crystal_parameters', valid_type=get_data_class('dict'), required=True) spec.input('properties_parameters', valid_type=get_data_class('dict'), required=False) spec.input('options', valid_type=get_data_class('dict'), required=True, help="Calculation options") # define workchain routine spec.outline(cls.init_inputs, cls.validate_inputs, cls.optimize_geometry, if_(cls.needs_phonons)( cls.calculate_phonons), cls.calculate_elastic_constants, # correctly finalized if_(cls.correctly_finalized("elastic_constants"))( cls.print_exit_status), if_(cls.needs_properties_run)( cls.run_properties_calc), cls.retrieve_results) # define outputs spec.output('phonons_parameters', valid_type=get_data_class('dict'), required=False) spec.output('elastic_parameters', valid_type=get_data_class('dict'), required=False) spec.expose_outputs(BaseCrystalWorkChain) spec.expose_outputs(BasePropertiesWorkChain)
def run_properties_calc(self): self.ctx.inputs.properties.wavefunction = self.ctx.optimise.outputs.output_wavefunction self.ctx.inputs.properties.options = get_data_class('dict')( dict=self.construct_metadata(PROPERTIES_LABEL)) properties_run = self.submit(BasePropertiesWorkChain, **self.ctx.inputs.properties) return self.to_context(properties=properties_run)
def optimize_geometry(self): self.ctx.inputs.crystal.parameters = get_data_class('dict')(dict=self.ctx.crystal_parameters.optimise) self.ctx.inputs.crystal.options = get_data_class('dict')(dict=self.construct_metadata(GEOMETRY_LABEL)) crystal_run = self.submit(BaseCrystalWorkChain, **self.ctx.inputs.crystal) return self.to_context(optimise=crystal_run)
def define(cls, spec): super(BaseCrystalWorkChain, cls).define(spec) # define inputs spec.input('code', valid_type=Code) spec.input('structure', valid_type=get_data_class('structure'), required=True) spec.input('parameters', valid_type=get_data_class('dict'), required=True) spec.input('basis_family', valid_type=get_data_class('crystal_dft.basis_family'), required=True) spec.input('clean_workdir', valid_type=get_data_class('bool'), required=False, default=lambda: get_data_node('bool', False)) spec.input('options', valid_type=get_data_class('dict'), required=True, help="Calculation options") # define workchain routine spec.outline( cls.init_inputs, while_(not_(cls._converged))( cls.init_calculation, cls.run_calculation, ), cls.retrieve_results, cls.finalize) # define outputs spec.output('output_structure', valid_type=get_data_class('structure'), required=False) spec.output('primitive_structure', valid_type=get_data_class('structure'), required=False) spec.output('output_parameters', valid_type=get_data_class('dict'), required=False) spec.output('output_wavefunction', valid_type=get_data_class('singlefile'), required=False) spec.output('output_trajectory', valid_type=get_data_class('array.trajectory'), required=False) spec.output('oxidation_states', valid_type=get_data_class('dict'), required=False) # define error codes spec.exit_code(300, 'ERROR_CRYSTAL', message='CRYSTAL error') spec.exit_code(400, 'ERROR_UNKNOWN', message='Unknown error')