def inspect_and_update_stage(self): """Update geometry, parent folder and the new &MOTION settings.""" last_stage = self.ctx.stages[-1] if 'output_structure' in last_stage.outputs: self.ctx.structure = last_stage.outputs.output_structure self.report('Structure updated for next stage') else: self.report( 'New structure NOT found and NOT updated for next stage') self.ctx.parent_calc_folder = last_stage.outputs.remote_folder last_stage.outputs.output_parameters.label = '{}_{}_valid'.format( self.ctx.stage_tag, self.ctx.settings_tag) self.ctx.stage_idx += 1 next_stage_tag = 'stage_{}'.format(self.ctx.stage_idx) if next_stage_tag in self.ctx.protocol: self.ctx.stage_tag = next_stage_tag self.ctx.next_stage_exists = True dict_merge(self.ctx.cp2k_param, self.ctx.protocol[self.ctx.stage_tag]) else: self.ctx.next_stage_exists = False self.report("All stages computed, finishing...")
def run_isotherms(self): """Compute isotherms at different temperatures.""" self.ctx.ntemp = len(self.inputs.parameters['temperature_list']) # create inputs: exposed are code and metadata inputs = self.exposed_inputs(IsothermWorkChain) inputs['geometric'] = self.ctx.geom if 'block' in self.ctx.geom_only.outputs: inputs['raspa_base']['raspa']["block_pocket"] = { "block_file": self.ctx.geom_only.outputs.block } # Update the parameters with only one temperature and submit for i in range(self.ctx.ntemp): self.ctx.parameters_singletemp = get_parameters_singletemp( i, self.inputs.parameters) dict_merge( inputs, { 'metadata': { 'label': "Isotherm_{}".format(i), 'call_link_label': 'run_isotherm_{}'.format(i), }, 'parameters': self.ctx.parameters_singletemp }) running = self.submit(IsothermWorkChain, **inputs) self.to_context(**{'isotherm_{}'.format(i): running})
def run_zeopp(self): """Perform Zeo++ block and VOLPO calculations.""" # Skip zeopp calculation if the geometric properties are already provided by IsothermMultiTemp if self.ctx.multitemp_mode == 'run_single_temp': return None # create inputs: exposed are code and metadata inputs = self.exposed_inputs(ZeoppCalculation, 'zeopp') # Set inputs for zeopp dict_merge( inputs, { 'metadata': { 'label': "ZeoppVolpoBlock", 'call_link_label': 'run_zeopp_block_and_volpo', }, 'structure': self.inputs.structure, 'atomic_radii': get_atomic_radii(self.ctx.parameters), 'parameters': get_zeopp_parameters(self.ctx.molecule, self.ctx.parameters) }) running = self.submit(ZeoppCalculation, **inputs) self.report("Running zeo++ block and volpo for {} Calculation<{}>".format(self.ctx.molecule['name'], running.id)) return ToContext(zeopp=running)
def run_geo_opt(self): """Prepare inputs, submit and direct output to context.""" self.ctx.base_inp = AttributeDict( self.exposed_inputs(Cp2kBaseWorkChain, 'cp2k_base')) self.ctx.base_inp['cp2k']['structure'] = self.ctx.system # Overwrite the generated input with the custom cp2k/parameters, update metadata and submit if 'parameters' in self.exposed_inputs(Cp2kBaseWorkChain, 'cp2k_base')['cp2k']: dict_merge( self.ctx.cp2k_param, self.exposed_inputs( Cp2kBaseWorkChain, 'cp2k_base')['cp2k']['parameters'].get_dict()) self.ctx.base_inp['cp2k']['parameters'] = Dict( dict=self.ctx.cp2k_param) self.ctx.base_inp['metadata'].update({ 'label': 'geo_opt_molecule', 'call_link_label': 'run_geo_opt_molecule' }) self.ctx.base_inp['cp2k']['metadata'].update({'label': 'GEO_OPT'}) self.ctx.base_inp['cp2k']['metadata']['options'][ 'parser_name'] = 'lsmo.cp2k_advanced_parser' running_base = self.submit(Cp2kBaseWorkChain, **self.ctx.base_inp) self.report("Optimize molecule position in the structure.") return ToContext(stages=append_(running_base))
def run_stage(self): """Check for restart, prepare input, submit and direct output to context.""" # Update structure self.ctx.base_inp['cp2k']['structure'] = self.ctx.structure # Check if it is needed to restart the calculation and provide the parent folder and new structure if self.ctx.parent_calc_folder: self.ctx.base_inp['cp2k'][ 'parent_calc_folder'] = self.ctx.parent_calc_folder self.ctx.cp2k_param['FORCE_EVAL']['DFT']['SCF'][ 'SCF_GUESS'] = 'RESTART' self.ctx.cp2k_param['FORCE_EVAL']['DFT'][ 'WFN_RESTART_FILE_NAME'] = './parent_calc/aiida-RESTART.wfn' else: self.ctx.cp2k_param['FORCE_EVAL']['DFT']['SCF'][ 'SCF_GUESS'] = 'ATOMIC' # Overwrite the generated input with the custom cp2k/parameters if 'parameters' in self.exposed_inputs(Cp2kBaseWorkChain, 'cp2k_base')['cp2k']: dict_merge( self.ctx.cp2k_param, AttributeDict( self.exposed_inputs( Cp2kBaseWorkChain, 'cp2k_base')['cp2k']['parameters'].get_dict())) self.ctx.base_inp['cp2k']['parameters'] = Dict( dict=self.ctx.cp2k_param).store() # Update labels self.ctx.base_inp['metadata'].update({ 'label': '{}_{}'.format(self.ctx.stage_tag, self.ctx.settings_tag), 'call_link_label': 'run_{}_{}'.format(self.ctx.stage_tag, self.ctx.settings_tag), }) self.ctx.base_inp['cp2k']['metadata'].update({ 'label': self.ctx.base_inp['cp2k']['parameters'].get_dict()['GLOBAL'] ['RUN_TYPE'] }) running_base = self.submit(Cp2kBaseWorkChain, **self.ctx.base_inp) self.report("submitted Cp2kBaseWorkChain for {}/{}".format( self.ctx.stage_tag, self.ctx.settings_tag)) return ToContext(stages=append_(running_base))
def run_geometric(self): """Perform Zeo++ block and VOLPO calculation with IsothermWC.""" # create inputs: exposed are code and metadata inputs = self.exposed_inputs(IsothermWorkChain) # Set inputs for zeopp dict_merge( inputs, { 'metadata': { 'label': "IsothermGeometric", 'call_link_label': 'run_geometric', }, }) running = self.submit(IsothermWorkChain, **inputs) self.report("Computing common gemetric properties") return ToContext(geom_only=running)
def inspect_and_update_settings_stage0(self): # pylint: disable=inconsistent-return-statements """Inspect the stage0/settings_{idx} calculation and check if it is needed to update the settings and resubmint the calculation.""" self.ctx.settings_ok = True # Settings/structure are bad: there are problems in parsing the output file # and, most probably, the calculation didn't even start the scf cycles if 'output_parameters' in self.ctx.stages[-1].outputs: cp2k_out = self.ctx.stages[-1].outputs.output_parameters else: self.report('ERROR_PARSING_OUTPUT') return self.exit_codes.ERROR_PARSING_OUTPUT # pylint: disable=no-member # Settings are bad: the SCF did not converge in the final step if not cp2k_out["motion_step_info"]["scf_converged"][-1]: self.report("BAD SETTINGS: the SCF did not converge") self.ctx.settings_ok = False self.ctx.settings_idx += 1 else: # SCF converged, but the computed bandgap needs to be checked self.report("Bandgaps spin1/spin2: {:.3f} and {:.3f} ev".format( cp2k_out["bandgap_spin1_au"] * HARTREE2EV, cp2k_out["bandgap_spin2_au"] * HARTREE2EV)) bandgap_thr_ev = self.ctx.protocol['bandgap_thr_ev'] if ot_has_small_bandgap(self.ctx.cp2k_param, cp2k_out, bandgap_thr_ev): self.report("BAD SETTINGS: band gap is < {:.3f} eV".format( bandgap_thr_ev)) self.ctx.settings_ok = False self.ctx.settings_idx += 1 # Update the settings tag, check if it is available and overwrite if not self.ctx.settings_ok: cp2k_out.label = '{}_{}_discard'.format(self.ctx.stage_tag, self.ctx.settings_tag) next_settings_tag = 'settings_{}'.format(self.ctx.settings_idx) if next_settings_tag in self.ctx.protocol: self.ctx.settings_tag = next_settings_tag dict_merge(self.ctx.cp2k_param, self.ctx.protocol[self.ctx.settings_tag]) else: return self.exit_codes.ERROR_NO_MORE_SETTINGS # pylint: disable=no-member
def run_isotherms(self): """Run Isotherm work chain for CO2 and N2.""" inputs = self.exposed_inputs(IsothermWorkChain) self.report( "Run Isotherm work chain for CO2 and N2, in CifData<{}> (label: {} )" .format(self.inputs.structure.pk, self.inputs.structure.label)) for mol in ['co2', 'n2']: dict_merge( inputs, { 'metadata': { 'call_link_label': 'run_isotherm_for_{}'.format(mol), }, 'molecule': Str(mol), 'parameters': self.inputs.parameters }) running = self.submit(IsothermWorkChain, **inputs) self.to_context(**{'isotherm_{}'.format(mol): running})
def run_bsse(self): """Update parameters and run BSSE calculation. BSSE assumes that the molecule has no charge and unit multiplicity: this can be customized from builder.cp2k_base.cp2k.parameters. """ self.ctx.cp2k_param['GLOBAL']['RUN_TYPE'] = 'BSSE' dict_merge( self.ctx.cp2k_param, get_bsse_section(natoms_a=self.ctx.natoms_structure, natoms_b=self.ctx.natoms_molecule, mult_a=self.ctx.cp2k_param['FORCE_EVAL']['DFT'] ['MULTIPLICITY'], mult_b=1, charge_a=0, charge_b=0)) # Overwrite the generated input with the custom cp2k/parameters, update structure and metadata, and submit if 'parameters' in self.exposed_inputs(Cp2kBaseWorkChain, 'cp2k_base')['cp2k']: dict_merge( self.ctx.cp2k_param, self.exposed_inputs( Cp2kBaseWorkChain, 'cp2k_base')['cp2k']['parameters'].get_dict()) self.ctx.base_inp['cp2k']['parameters'] = Dict( dict=self.ctx.cp2k_param) self.ctx.base_inp['cp2k']['structure'] = self.ctx.stages[ -1].outputs.output_structure self.ctx.base_inp['metadata'].update({ 'label': 'bsse', 'call_link_label': 'run_bsse' }) self.ctx.base_inp['cp2k']['metadata'].update({'label': 'BSSE'}) self.ctx.base_inp['cp2k']['metadata']['options'][ 'parser_name'] = 'lsmo.cp2k_bsse_parser' running_base = self.submit(Cp2kBaseWorkChain, **self.ctx.base_inp) self.report( "Run BSSE calculation to compute corrected binding energy.") return ToContext(stages=append_(running_base))
def setup_multistage(self): """Setup initial parameters.""" # Store the workchain inputs in context (to be modified later) self.ctx.base_inp = AttributeDict( self.exposed_inputs(Cp2kBaseWorkChain, 'cp2k_base')) # Check if an input parent_calc_folder is provided if 'parent_calc_folder' in self.inputs: self.ctx.parent_calc_folder = self.inputs.parent_calc_folder else: self.ctx.parent_calc_folder = None # Read yaml file selected as SinglefileData or chosen with the tag, and overwrite with custom modifications if 'protocol_yaml' in self.inputs: self.ctx.protocol = yaml.safe_load( self.inputs.protocol_yaml.open()) else: thisdir = os.path.dirname(os.path.abspath(__file__)) yamlfullpath = os.path.join( thisdir, 'cp2k_multistage_protocols', self.inputs.protocol_tag.value + '.yaml') with open(yamlfullpath, 'r') as stream: self.ctx.protocol = yaml.safe_load(stream) dict_merge(self.ctx.protocol, self.inputs.protocol_modify.get_dict()) # Initialize self.ctx.settings_ok = False self.ctx.stage_idx = 0 self.ctx.stage_tag = 'stage_{}'.format(self.ctx.stage_idx) self.ctx.settings_idx = 0 self.ctx.settings_tag = 'settings_{}'.format(self.ctx.settings_idx) self.ctx.structure = self.inputs.structure # Resize the unit cell if min(perp_with) < inputs.min_cell_size self.ctx.resize = check_resize_unit_cell_legacy( self.ctx.structure, self.inputs.min_cell_size) # Dict if self.ctx.resize['nx'] > 1 or self.ctx.resize[ 'ny'] > 1 or self.ctx.resize['nz'] > 1: resized_struct = resize_unit_cell(self.ctx.structure, self.ctx.resize) self.ctx.structure = resized_struct self.report( "Unit cell resized by {}x{}x{} (StructureData<{}>)".format( self.ctx.resize['nx'], self.ctx.resize['ny'], self.ctx.resize['nz'], resized_struct.pk)) else: self.report("Unit cell was NOT resized") # Generate input parameters and store them self.ctx.cp2k_param = deepcopy(self.ctx.protocol['settings_0']) while self.inputs.starting_settings_idx > self.ctx.settings_idx: # overwrite untill the desired starting setting are obtained self.ctx.settings_idx += 1 self.ctx.settings_tag = 'settings_{}'.format(self.ctx.settings_idx) if self.ctx.settings_tag in self.ctx.protocol: dict_merge(self.ctx.cp2k_param, self.ctx.protocol[self.ctx.settings_tag]) else: return self.exit_codes.ERROR_MISSING_INITIAL_SETTINGS # pylint: disable=no-member kinds = get_kinds_section(self.ctx.structure, self.ctx.protocol) dict_merge(self.ctx.cp2k_param, kinds) multiplicity = get_input_multiplicity(self.ctx.structure, self.ctx.protocol) dict_merge(self.ctx.cp2k_param, multiplicity) dict_merge(self.ctx.cp2k_param, self.ctx.protocol['stage_0'])
def setup(self): """Setup initial parameters.""" # Read yaml file selected as SinglefileData or chosen with the tag, and overwrite with custom modifications if 'protocol_yaml' in self.inputs: self.ctx.protocol = yaml.safe_load( self.inputs.protocol_yaml.open()) else: thisdir = os.path.dirname(os.path.abspath(__file__)) yamlfullpath = os.path.join( thisdir, 'cp2k_multistage_protocols', self.inputs.protocol_tag.value + '.yaml') with open(yamlfullpath, 'r') as stream: self.ctx.protocol = yaml.safe_load(stream) dict_merge(self.ctx.protocol, self.inputs.protocol_modify.get_dict()) # Initialize self.ctx.settings_ok = False self.ctx.settings_idx = 0 self.ctx.settings_tag = 'settings_{}'.format(self.ctx.settings_idx) self.ctx.system = aiida_structure_merge(self.inputs.structure, self.inputs.molecule) self.ctx.natoms_structure = len(self.inputs.structure.get_ase()) self.ctx.natoms_molecule = len(self.inputs.molecule.get_ase()) # Generate input parameters self.ctx.cp2k_param = deepcopy(self.ctx.protocol['settings_0']) while self.inputs.starting_settings_idx < self.ctx.settings_idx: # overwrite untill the desired starting setting are obtained self.ctx.settings_idx += 1 self.ctx.settings_tag = 'settings_{}'.format(self.ctx.settings_idx) if self.ctx.settings_tag in self.ctx.protocol: dict_merge(self.ctx.cp2k_param, self.ctx.protocol[self.ctx.settings_tag]) else: return self.exit_codes.ERROR_MISSING_INITIAL_SETTINGS # pylint: disable=no-member dict_merge( self.ctx.cp2k_param, get_kinds_with_ghost_section(self.ctx.system, self.ctx.protocol)) dict_merge(self.ctx.cp2k_param, get_input_multiplicity(self.ctx.system, self.ctx.protocol)) dict_merge( self.ctx.cp2k_param, { 'GLOBAL': { 'RUN_TYPE': 'GEO_OPT' }, 'FORCE_EVAL': { 'DFT': { 'SCF': { 'SCF_GUESS': 'ATOMIC' } } }, 'MOTION': { 'GEO_OPT': { 'MAX_ITER': 200 }, # Can be adjusted from builder.cp2k_base.cp2k.parameters 'CONSTRAINT': { 'FIXED_ATOMS': { 'LIST': "1..{}".format(self.ctx.natoms_structure) } } } })