def test_find_last_submitted_calcjob(fixture_localhost, generate_calc_job_node, generate_work_chain_node): from aiida_fleur.tools.common_fleur_wf import find_last_submitted_calcjob from aiida.common.links import LinkType from aiida.common.exceptions import NotExistent node1 = generate_calc_job_node('fleur.fleur', fixture_localhost) node2 = generate_calc_job_node('fleur.fleur', fixture_localhost) node3 = generate_calc_job_node('fleur.fleur', fixture_localhost) node_main = generate_work_chain_node('fleur.base_relax', fixture_localhost) node_main.store() with pytest.raises(NotExistent): result = find_last_submitted_calcjob(node_main) node1.add_incoming(node_main, link_type=LinkType.CALL_CALC, link_label='CALL') node2.add_incoming(node_main, link_type=LinkType.CALL_CALC, link_label='CALL') node3.add_incoming(node_main, link_type=LinkType.CALL_CALC, link_label='CALL') node1.store() node2.store() node3.store() result = find_last_submitted_calcjob(node_main) assert result == node3.uuid
def return_results(self): """ return the results of the calculations This should run through and produce output nodes even if everything failed, therefore it only uses results from context. """ if self.ctx.last_base_wc: try: last_calc_uuid = find_last_submitted_calcjob( self.ctx.last_base_wc) except NotExistent: last_calc_uuid = None else: last_calc_uuid = None try: # if something failed, we still might be able to retrieve something last_calc_out = self.ctx.last_base_wc.outputs.output_parameters retrieved = self.ctx.last_base_wc.outputs.retrieved last_calc_out_dict = last_calc_out.get_dict() except (NotExistent, AttributeError): last_calc_out = None last_calc_out_dict = {} retrieved = None last_nmmp_distance = None if self.ctx.last_nmmp_distance > 0.0: last_nmmp_distance = self.ctx.last_nmmp_distance outputnode_dict = {} outputnode_dict['workflow_name'] = self.__class__.__name__ outputnode_dict['workflow_version'] = self._workflowversion outputnode_dict['material'] = self.ctx.formula outputnode_dict['conv_mode'] = self.ctx.wf_dict.get('mode') outputnode_dict['loop_count'] = self.ctx.loop_count outputnode_dict['iterations_total'] = last_calc_out_dict.get( 'number_of_iterations_total', None) outputnode_dict['distance_charge'] = self.ctx.last_charge_density outputnode_dict['distance_charge_all'] = self.ctx.distance outputnode_dict['total_energy'] = last_calc_out_dict.get( 'energy_hartree', None) outputnode_dict['total_energy_all'] = self.ctx.total_energy outputnode_dict['force_diff_last'] = self.ctx.forcediff outputnode_dict['force_largest'] = last_calc_out_dict.get( 'force_largest', None) outputnode_dict['distance_charge_units'] = 'me/bohr^3' outputnode_dict['total_energy_units'] = 'Htr' outputnode_dict['nmmp_distance'] = last_nmmp_distance outputnode_dict['nmmp_distance_all'] = self.ctx.nmmp_distance outputnode_dict['last_calc_uuid'] = last_calc_uuid outputnode_dict['total_wall_time'] = self.ctx.total_wall_time outputnode_dict['total_wall_time_units'] = 's' outputnode_dict['info'] = self.ctx.info outputnode_dict['warnings'] = self.ctx.warnings outputnode_dict['errors'] = self.ctx.errors if self.ctx.successful and self.ctx.reached_conv: if len(self.ctx.total_energy ) <= 1: # then len(self.ctx.all_forces) <= 1 too self.report( 'STATUS: Done, the convergence criteria are reached.\n' 'INFO: The charge density of the FLEUR calculation ' 'converged after {} FLEUR runs, {} iterations and {} sec ' 'walltime to {} "me/bohr^3" \n' 'INFO: Did not manage to get energy and largest force difference ' 'between two last iterations, probably converged in a single iteration' ''.format( self.ctx.loop_count, last_calc_out_dict.get('number_of_iterations_total', None), self.ctx.total_wall_time, outputnode_dict['distance_charge'])) if self.ctx.last_nmmp_distance > 0.0: self.report( 'INFO: The LDA+U density matrix is converged to {} change ' 'of all matrix elements'.format( self.ctx.last_nmmp_distance)) else: self.report( 'STATUS: Done, the convergence criteria are reached.\n' 'INFO: The charge density of the FLEUR calculation ' 'converged after {} FLEUR runs, {} iterations and {} sec ' 'walltime to {} "me/bohr^3" \n' 'INFO: The total energy difference of the last two iterations ' 'is {} Htr and largest force difference is {} Htr/bohr' ''.format( self.ctx.loop_count, last_calc_out_dict.get('number_of_iterations_total', None), self.ctx.total_wall_time, outputnode_dict['distance_charge'], self.ctx.energydiff, self.ctx.forcediff)) if self.ctx.last_nmmp_distance > 0.0: self.report( 'INFO: The LDA+U density matrix is converged to {} change ' 'of all matrix elements'.format( self.ctx.last_nmmp_distance)) elif self.ctx.successful and not self.ctx.reached_conv: if len(self.ctx.total_energy ) <= 1: # then len(self.ctx.all_forces) <= 1 too self.report( 'STATUS/WARNING: Done, the maximum number of runs ' 'was reached.\n INFO: The ' 'charge density of the FLEUR calculation, ' 'after {} FLEUR runs, {} iterations and {} sec ' 'walltime is {} "me/bohr^3"\n' 'INFO: can not extract energy and largest force difference between' ' two last iterations, probably converged in a single iteration' ''.format( self.ctx.loop_count, last_calc_out_dict.get('number_of_iterations_total', None), self.ctx.total_wall_time, outputnode_dict['distance_charge'])) else: self.report( 'STATUS/WARNING: Done, the maximum number of runs ' 'was reached.\n INFO: The ' 'charge density of the FLEUR calculation, ' 'after {} FLEUR runs, {} iterations and {} sec ' 'walltime is {} "me/bohr^3"\n' 'INFO: The total energy difference of the last two iterations ' 'is {} Htr and largest force difference is {} Htr/bohr\n' ''.format( self.ctx.loop_count, last_calc_out_dict.get('number_of_iterations_total', None), self.ctx.total_wall_time, outputnode_dict['distance_charge'], self.ctx.energydiff, self.ctx.forcediff)) if self.ctx.last_nmmp_distance > 0.0: self.report( 'INFO: The LDA+U density matrix is converged to {} change ' 'of all matrix elements'.format( self.ctx.last_nmmp_distance)) else: # Termination ok, but not converged yet... if self.ctx.abort: # some error occurred, do not use the output. self.report('STATUS/ERROR: I abort, see logs and ' 'errors/warning/hints in output_scf_wc_para') outputnode_t = Dict(dict=outputnode_dict) # this is unsafe so far, because last_calc_out could not exist... if last_calc_out: outdict = create_scf_result_node(outpara=outputnode_t, last_calc_out=last_calc_out, last_calc_retrieved=retrieved) else: outdict = create_scf_result_node(outpara=outputnode_t) # Now it always returns changed fleurinp that was actually used in the calculation if self.ctx.fleurinp is not None: outdict['fleurinp'] = self.ctx.fleurinp if last_calc_out: outdict['last_fleur_calc_output'] = last_calc_out #outdict['output_scf_wc_para'] = outputnode for link_name, node in outdict.items(): self.out(link_name, node) if not self.ctx.reached_conv: return self.exit_codes.ERROR_DID_NOT_CONVERGE
def get_res(self): """ Check how the last Fleur calculation went Parse some results. """ self.report('INFO: get results FLEUR') mode = self.ctx.wf_dict.get('mode') if self.ctx.parse_last: last_base_wc = self.ctx.last_base_wc fleur_calcjob = load_node( find_last_submitted_calcjob(last_base_wc)) walltime = last_base_wc.outputs.output_parameters.dict.walltime if isinstance(walltime, int): self.ctx.total_wall_time = self.ctx.total_wall_time + walltime with fleur_calcjob.outputs.retrieved.open( fleur_calcjob.process_class._OUTXML_FILE_NAME, 'r') as outxmlfile: output_dict = outxml_parser(outxmlfile, minimal_mode=True, list_return=True, iteration_to_parse='all', ignore_validation=True) energies = output_dict.get('energy_hartree', []) if energies is not None: self.ctx.total_energy.extend(energies) if 'overall_density_convergence' in output_dict: distances = output_dict['overall_density_convergence'] else: distances = output_dict.get('density_convergence', []) if distances is not None: self.ctx.distance.extend(distances) if 'ldau_info' in output_dict: nmmp_distances = output_dict['ldau_info'].get( 'nmmp_distances', []) if nmmp_distances is not None: self.ctx.nmmp_distance.extend(nmmp_distances) if mode == 'force': forces = output_dict.get('force_atoms', []) if forces is not None: for force_iter in forces: self.ctx.all_forces.append( [force for atom, force in force_iter]) else: errormsg = 'ERROR: scf wc was not successful, check log for details' self.control_end_wc(errormsg) return self.exit_codes.ERROR_FLEUR_CALCULATION_FAILED if not self.ctx.distance: # if fleur relaxes an already converged crystal it stops directly if mode == 'force': self.report( 'INFO: System already force converged, could not extract distance.' ) self.ctx.last_charge_density = None else: errormsg = 'ERROR: did not manage to extract charge density from the calculation' self.control_end_wc(errormsg) return self.exit_codes.ERROR_FLEUR_CALCULATION_FAILED else: self.ctx.last_charge_density = self.ctx.distance[-1] if self.ctx.nmmp_distance: self.ctx.last_nmmp_distance = max(self.ctx.nmmp_distance[-1])
def return_results(self): ''' return the results of the calculations ''' # TODO more here self.report('BandDOS workflow Done') self.report( f'A bandstructure was calculated and is found under pk={self.ctx.banddos_calc.pk}, ' f'calculation {self.ctx.banddos_calc}') from aiida_fleur.tools.common_fleur_wf import find_last_submitted_calcjob if self.ctx.banddos_calc: try: last_calc_uuid = find_last_submitted_calcjob( self.ctx.banddos_calc) except NotExistent: last_calc_uuid = None else: last_calc_uuid = None try: # if something failed, we still might be able to retrieve something last_calc_out = self.ctx.banddos_calc.outputs.output_parameters retrieved = self.ctx.banddos_calc.outputs.retrieved if 'fleurinpdata' in self.ctx.banddos_calc.inputs: fleurinp = self.ctx.banddos_calc.inputs.fleurinpdata else: fleurinp = get_fleurinp_from_remote_data( self.ctx.banddos_calc.inputs.parent_folder) last_calc_out_dict = last_calc_out.get_dict() except (NotExistent, AttributeError): last_calc_out = None last_calc_out_dict = {} retrieved = None fleurinp = None #check if band file exists: if not succesful = False #TODO be careful with general bands.X bandfiles = ['bands.1', 'bands.2', 'banddos.hdf'] bandfile_res = [] if retrieved: bandfile_res = retrieved.list_object_names() for name in bandfiles: if name in bandfile_res: self.ctx.successful = True if not self.ctx.successful: self.report( '!NO bandstructure file was found, something went wrong!') # # get efermi from last calculation scf_results = None efermi_scf = 0 bandgap_scf = 0 if 'remote' in self.inputs: for w in self.inputs.remote.get_incoming().all(): if isinstance(w.node, CalcJobNode): scf_results = load_node(w.node.pk).res efermi_scf = scf_results.fermi_energy bandgap_scf = scf_results.bandgap efermi_band = last_calc_out_dict.get('fermi_energy', None) bandgap_band = last_calc_out_dict.get('bandgap', None) diff_efermi = None if efermi_band is not None: diff_efermi = efermi_scf - efermi_band diff_bandgap = None if bandgap_band is not None: diff_bandgap = bandgap_scf - bandgap_band outputnode_dict = {} outputnode_dict['workflow_name'] = self.__class__.__name__ outputnode_dict['Warnings'] = self.ctx.warnings outputnode_dict['successful'] = self.ctx.successful outputnode_dict['last_calc_uuid'] = last_calc_uuid outputnode_dict['last_calc_pk'] = self.ctx.banddos_calc.pk outputnode_dict['mode'] = self.ctx.wf_dict.get('mode') outputnode_dict['fermi_energy_band'] = efermi_band outputnode_dict['bandgap_band'] = bandgap_band outputnode_dict['fermi_energy_scf'] = efermi_scf outputnode_dict['bandgap_scf'] = bandgap_scf outputnode_dict['diff_efermi'] = diff_efermi outputnode_dict['diff_bandgap'] = diff_bandgap outputnode_dict['bandgap_units'] = 'eV' outputnode_dict['fermi_energy_units'] = 'Htr' outputnode_t = Dict(dict=outputnode_dict) if last_calc_out: outdict = create_band_result_node(outpara=outputnode_t, last_calc_out=last_calc_out, last_calc_retrieved=retrieved) if self.ctx.wf_dict.get( 'mode' ) == 'band' and fleurinp is not None and retrieved is not None: bands = create_aiida_bands_data(fleurinp=fleurinp, retrieved=retrieved) if isinstance(bands, BandsData): outdict['output_banddos_wc_bands'] = bands elif self.ctx.wf_dict.get( 'mode') == 'dos' and retrieved is not None: dos = create_aiida_dos_data(retrieved=retrieved) if isinstance(dos, XyData): outdict['output_banddos_wc_dos'] = dos else: outdict = create_band_result_node(outpara=outputnode_t) if retrieved: outdict['last_calc_retrieved'] = retrieved #TODO parse Bandstructure for link_name, node in outdict.items(): self.out(link_name, node)
def get_res(self): """ Check how the last Fleur calculation went Parse some results. """ self.report('INFO: get results FLEUR') xpath_energy = '/fleurOutput/scfLoop/iteration/totalEnergy/@value' xpath_iter = '/fleurOutput/scfLoop/iteration' xpath_force = 'totalForcesOnRepresentativeAtoms/forceTotal' # be aware of magnetism xpath_distance = '/fleurOutput/scfLoop/iteration/densityConvergence/chargeDensity/@distance' overallchargedensity_xpath = ('/fleurOutput/scfLoop/iteration/densityConvergence/' 'overallChargeDensity/@distance') mode = self.ctx.wf_dict.get('mode') if self.ctx.parse_last: last_base_wc = self.ctx.last_base_wc fleur_calcjob = load_node(find_last_submitted_calcjob(last_base_wc)) walltime = last_base_wc.outputs.output_parameters.dict.walltime if isinstance(walltime, int): self.ctx.total_wall_time = self.ctx.total_wall_time + walltime with fleur_calcjob.outputs.retrieved.open(fleur_calcjob.process_class._OUTXML_FILE_NAME, 'r') as outxmlfile_opened: tree = etree.parse(outxmlfile_opened) root = tree.getroot() energies = eval_xpath2(root, xpath_energy) for energy in energies: self.ctx.total_energy.append(float(energy)) overall_distances = eval_xpath2(root, overallchargedensity_xpath) if not overall_distances: distances = eval_xpath2(root, xpath_distance) for distance in distances: self.ctx.distance.append(float(distance)) else: for distance in overall_distances: self.ctx.distance.append(float(distance)) if mode == 'force': iter_all = eval_xpath2(root, xpath_iter) for iteration in iter_all: forces = eval_xpath2(iteration, xpath_force) forces_in_iter = [] for force in forces: force_x = float(get_xml_attribute(force, 'F_x')) force_y = float(get_xml_attribute(force, 'F_y')) force_z = float(get_xml_attribute(force, 'F_z')) forces_in_iter.append(force_x) forces_in_iter.append(force_y) forces_in_iter.append(force_z) self.ctx.all_forces.append(forces_in_iter) else: errormsg = 'ERROR: scf wc was not successful, check log for details' self.control_end_wc(errormsg) return self.exit_codes.ERROR_FLEUR_CALCULATION_FAILED if not self.ctx.distance: # if fleur relaxes an already converged crystal it stops directly if mode == 'force': self.report('INFO: System already force converged, could not extract distance.') self.ctx.last_charge_density = None else: errormsg = 'ERROR: did not manage to extract charge density from the calculation' self.control_end_wc(errormsg) return self.exit_codes.ERROR_FLEUR_CALCULATION_FAILED else: self.ctx.last_charge_density = self.ctx.distance[-1]
def return_results(self): ''' return the results of the calculations ''' # TODO more here self.report('Band workflow Done') self.report('A bandstructure was calculated for fleurinpdata {} and is found under pk={}, ' 'calculation {}'.format(self.ctx.fleurinp_scf, self.ctx.last_calc.pk, self.ctx.last_calc)) from aiida_fleur.tools.common_fleur_wf import find_last_submitted_calcjob if self.ctx.last_calc: try: last_calc_uuid = find_last_submitted_calcjob(self.ctx.last_calc) except NotExistent: last_calc_uuid = None else: last_calc_uuid = None try: # if something failed, we still might be able to retrieve something last_calc_out = self.ctx.last_calc.outputs.output_parameters retrieved = self.ctx.last_calc.outputs.retrieved last_calc_out_dict = last_calc_out.get_dict() except (NotExistent, AttributeError): last_calc_out = None last_calc_out_dict = {} retrieved = None #check if band file exists: if not succesful = False #TODO be careful with general bands.X # bandfilename = 'bands.1' # ['bands.1', 'bands.2', ...] # bandfile =retrieved.open(bandfilename).name # if os.path.isfile(bandfile): # self.ctx.successful = True # else: # bandfile = None # self.report('!NO bandstructure file was found, something went wrong!') # # get efermi from last calculation scf_results = None efermi_scf = 0 bandgap_scf = 0 if 'remote' in self.inputs: for w in self.inputs.remote.get_incoming().all(): if isinstance(w.node, CalcJobNode): scf_results = load_node(w.node.pk).res efermi_scf = scf_results.fermi_energy bandgap_scf = scf_results.bandgap efermi_band = last_calc_out_dict.get('fermi_energy', None) bandgap_band = last_calc_out_dict.get('bandgap', None) diff_efermi = None if efermi_band is not None: diff_efermi = efermi_scf - efermi_band diff_bandgap = None if bandgap_band is not None: diff_bandgap = bandgap_scf - bandgap_band outputnode_dict = {} outputnode_dict['workflow_name'] = self.__class__.__name__ outputnode_dict['Warnings'] = self.ctx.warnings outputnode_dict['successful'] = self.ctx.successful outputnode_dict['last_calc_uuid'] = last_calc_uuid outputnode_dict['last_calc_pk'] = self.ctx.last_calc.pk outputnode_dict['fermi_energy_band'] = efermi_band outputnode_dict['bandgap_band'] = bandgap_band outputnode_dict['fermi_energy_scf'] = efermi_scf outputnode_dict['bandgap_scf'] = bandgap_scf outputnode_dict['diff_efermi'] = diff_efermi outputnode_dict['diff_bandgap'] = diff_bandgap outputnode_dict['bandgap_units'] = 'eV' outputnode_dict['fermi_energy_units'] = 'Htr' # outputnode_dict['bandfile'] = bandfile outputnode_t = Dict(dict=outputnode_dict) if last_calc_out: outdict = create_band_result_node(outpara=outputnode_t, last_calc_out=last_calc_out, last_calc_retrieved=retrieved) else: outdict = create_band_result_node(outpara=outputnode_t) #TODO parse Bandstructure for link_name, node in six.iteritems(outdict): self.out(link_name, node)