def _generate_film_structure(): """Return a `StructureData` representing bulk silicon.""" from aiida.orm import StructureData from aiida_fleur.common.constants import BOHR_A a = 7.497 * BOHR_A cell = [[0.7071068 * a, 0.0, 0.0], [0.0, 1.0 * a, 0.0], [0.0, 0.0, 0.7071068 * a]] structure = StructureData(cell=cell) structure.append_atom(position=(0., 0., -1.99285 * BOHR_A), symbols='Fe') structure.append_atom(position=(0.5 * 0.7071068 * a, 0.5 * a, 0.0), symbols='Pt') structure.append_atom(position=(0., 0., 1.99285 * BOHR_A), symbols='Fe') structure.pbc = (True, True, False) return structure
def get_results_relax(self): """ Generates results of the workchain. Creates a new structure data node which is an optimized structure. """ if self.ctx.wf_dict.get('relaxation_type', 'atoms') is None: input_scf = AttributeDict( self.exposed_inputs(FleurScfWorkChain, namespace='scf')) if 'structure' in input_scf: structure = input_scf.structure elif 'fleurinp' in input_scf: structure = input_scf.fleurinp.get_structuredata_ncf() else: pass self.ctx.final_structure = structure self.ctx.total_energy_last = None #total_energy self.ctx.total_energy_units = None #total_energy_units self.ctx.final_cell = structure.cell self.ctx.final_atom_positions = None #atom_positions self.ctx.atomtype_info = None return try: relax_out = self.ctx.scf_res.outputs.last_fleur_calc_output except NotExistent: return self.exit_codes.ERROR_NO_SCF_OUTPUT relax_out = relax_out.get_dict() try: cell = relax_out['relax_brav_vectors'] atom_positions = relax_out['relax_atom_positions'] film = relax_out['film'] total_energy = relax_out['energy'] total_energy_units = relax_out['energy_units'] atomtype_info = relax_out['relax_atomtype_info'] except KeyError: return self.exit_codes.ERROR_NO_RELAX_OUTPUT self.ctx.total_energy_last = total_energy self.ctx.total_energy_units = total_energy_units self.ctx.final_cell = cell self.ctx.final_atom_positions = atom_positions self.ctx.atomtype_info = atomtype_info if film == 'True': self.ctx.pbc = (True, True, False) else: self.ctx.pbc = (True, True, True) # we build the structure here, that way we can run an scf afterwards # construct it in a way which preserves the species information from the initial input structure if self.ctx.final_cell: np_cell = np.array(self.ctx.final_cell) * BOHR_A structure = StructureData(cell=np_cell.tolist()) #self.report('############ {}'.format(atomtype_info)) for i, atom in enumerate(self.ctx.final_atom_positions): species_name = atomtype_info[i][0] element = atomtype_info[i][1] np_pos = np.array(atom) pos_abs = np_pos @ np_cell if self.ctx.pbc == (True, True, True): structure.append_atom(position=(pos_abs[0], pos_abs[1], pos_abs[2]), symbols=element, name=species_name) else: # assume z-direction is orthogonal to xy structure.append_atom(position=(pos_abs[0], pos_abs[1], atom[3] * BOHR_A), symbols=element, name=species_name) structure.pbc = self.ctx.pbc self.ctx.final_structure = structure
def test_x_and_bunchatom_input( aiida_profile, fixture_sandbox, generate_calc_job, fixture_code, ): """Test that plugin can deal (ignores) with other StructureData features Currently we assume atoms, deal with vacancies, i.e we leave them out ignore the x element. This is important for interoperability with kkr # TODO often we do natoms= len(n.sites), which would be false in the case of vacancies. """ from aiida.orm import StructureData struc_Fe7Nb = StructureData() struc_Fe7Nb.cell = [[3.3168796764431, 0.0, 0.0], [1.6584398382215, 2.3453881115923, 0.0], [0.0, 0.0, 13.349076054836]] struc_Fe7Nb.pbc = (True, True, False) elements = ['X', 'X', 'X', 'Fe', 'Nb', 'Nb'] positions = [[0.0, 0.0, 1.1726940557829], [1.6584398382215, 0.0, 3.5180821673487], [0.0, 0.0, 5.8634702789145], [1.6584398382215, 0.0, 8.2088583904803], [0.0, 0.0, 10.096376717551], [1.6584398382215, 0.0, 12.46832205832]] for el, pos in zip(elements, positions): struc_Fe7Nb.append_atom(symbols=[el], position=pos) entry_point_name = 'fleur.inpgen' parameters = {} inputs = { 'code': fixture_code(entry_point_name), 'structure': struc_Fe7Nb, 'metadata': { 'options': { 'resources': { 'num_machines': 1 }, 'max_wallclock_seconds': int(100), 'withmpi': False } } } calc_info = generate_calc_job(fixture_sandbox, entry_point_name, inputs) codes_info = calc_info.codes_info cmdline_params = ['+all', '-explicit', '-f', 'aiida.in'] local_copy_list = [] retrieve_list = [ 'inp.xml', 'out', 'shell.out', 'out.error', 'struct.xsf', 'aiida.in' ] retrieve_temporary_list = [] # Check the attributes of the returned `CalcInfo` assert isinstance(calc_info, datastructures.CalcInfo) assert sorted(calc_info.retrieve_list) == sorted(retrieve_list) with fixture_sandbox.open('aiida.in') as handle: input_written = handle.read() print(input_written) assert ' 3\n' in input_written # test for natoms # Test not none of the vacany elements was written into the input file for line in input_written.split('\n'): assert 'X' not in line assert ' 0 ' not in line
def test_fleur_mae_FePt_film(self, run_with_cache, fleur_local_code, inpgen_local_code): """ full example using mae workflow with FePt film structure as input. """ from aiida.orm import Code, load_node, Dict, StructureData options = Dict( dict={ 'resources': { 'num_machines': 1, 'num_mpiprocs_per_machine': 1 }, 'max_wallclock_seconds': 60 * 60, 'queue_name': '', 'custom_scheduler_commands': '' }) wf_para_scf = { 'fleur_runmax': 2, 'itmax_per_run': 120, 'density_converged': 0.4, 'serial': False, 'mode': 'density' } wf_para_scf = Dict(dict=wf_para_scf) wf_para = Dict( dict={ 'sqa_ref': [0.7, 0.7], 'use_soc_ref': False, 'sqas_theta': [0.0, 1.57079, 1.57079], 'sqas_phi': [0.0, 0.0, 1.57079], 'serial': False, 'soc_off': [], 'inpxml_changes': [], }) bohr_a_0 = 0.52917721092 # A a = 7.497 * bohr_a_0 cell = [[0.7071068 * a, 0.0, 0.0], [0.0, 1.0 * a, 0.0], [0.0, 0.0, 0.7071068 * a]] structure = StructureData(cell=cell) structure.append_atom(position=(0.0, 0.0, -1.99285 * bohr_a_0), symbols='Fe', name='Fe123') structure.append_atom(position=(0.5 * 0.7071068 * a, 0.5 * a, 0.0), symbols='Pt') structure.append_atom(position=(0., 0., 2.65059 * bohr_a_0), symbols='Pt') structure.pbc = (True, True, False) parameters = Dict( dict={ 'atom': { 'element': 'Pt', 'lmax': 6 }, 'atom2': { 'element': 'Fe', 'lmax': 6, }, 'comp': { 'kmax': 3.2, }, 'kpt': { 'div1': 8, #20, 'div2': 12, #24, 'div3': 1 } }) FleurCode = fleur_local_code InpgenCode = inpgen_local_code inputs = { 'scf': { 'wf_parameters': wf_para_scf, 'structure': structure, 'calc_parameters': parameters, 'options': options, 'inpgen': InpgenCode, 'fleur': FleurCode }, 'wf_parameters': wf_para, 'fleur': FleurCode, 'options': options } # now run calculation out, node = run_with_cache(inputs, process_class=FleurMaeWorkChain) print(out) print(node) assert node.is_finished_ok outpara = out.get('out', None) assert outpara is not None outpara = outpara.get_dict() print(outpara) # check output assert outpara.get('warnings') == [] assert outpara.get('phi') == [0.0, 0.0, 1.57079] assert outpara.get('theta') == [0.1, 1.57079, 1.57079] assert outpara.get('is_it_force_theorem') assert outpara.get('maes') == [ 0.0039456509729923, 0.0026014085035566, 0.0 ]
def get_structuredata_ncf(self): """ This routine returns an AiiDA Structure Data type produced from the ``inp.xml`` file. not a calcfunction :param self: a FleurinpData instance to be parsed into a StructureData :returns: StructureData node, or None """ from aiida.orm import StructureData from aiida_fleur.tools.StructureData_util import rel_to_abs, rel_to_abs_f # StructureData = DataFactory(‘structure’) # Disclaimer: this routine needs some xpath expressions. these are hardcoded here, # therefore maintainance might be needed, if you want to circumvent this, you have # to get all the paths from somewhere. ####### # all hardcoded xpaths used and attributes names: bravaismatrix_bulk_xpath = '/fleurInput/cell/bulkLattice/bravaisMatrix/' bravaismatrix_film_xpath = '/fleurInput/cell/filmLattice/bravaisMatrix/' species_xpath = '/fleurInput/atomSpecies/species' all_atom_groups_xpath = '/fleurInput/atomGroups/atomGroup' species_attrib_name = 'name' species_attrib_element = 'element' row1_tag_name = 'row-1' row2_tag_name = 'row-2' row3_tag_name = 'row-3' atom_group_attrib_species = 'species' atom_group_tag_abspos = 'absPos' atom_group_tag_relpos = 'relPos' atom_group_tag_filmpos = 'filmPos' ######## if 'inp.xml' not in self.files: print( 'cannot get a StructureData because fleurinpdata has no inp.xml file yet' ) # TODO what to do in this case? return None # read in inpxml if self._schema_file_path: # Schema there, parse with schema xmlschema_doc = etree.parse(self._schema_file_path) xmlschema = etree.XMLSchema(xmlschema_doc) parser = etree.XMLParser(attribute_defaults=True, encoding='utf-8') with self.open(path='inp.xml', mode='r') as inpxmlfile: tree = etree.parse(inpxmlfile, parser) tree.xinclude() # remove comments from inp.xml comments = tree.xpath('//comment()') for c in comments: p = c.getparent() p.remove(c) if not xmlschema.validate(tree): raise ValueError( 'Input file is not validated against the schema.') else: # schema not there, parse without print('parsing inp.xml without XMLSchema') with self.open(path='inp.xml', mode='r') as inpxmlfile: tree = etree.parse(inpxmlfile) tree.xinclude() # remove comments from inp.xml comments = tree.xpath('//comment()') for c in comments: p = c.getparent() p.remove(c) root = tree.getroot() # Fleur uses atomic units, convert to Angstrom # get cell matrix from inp.xml row1 = root.xpath(bravaismatrix_bulk_xpath + row1_tag_name) # [0].text.split() cell = None if row1: # bulk calculation row1 = row1[0].text.split() row2 = root.xpath(bravaismatrix_bulk_xpath + row2_tag_name)[0].text.split() row3 = root.xpath(bravaismatrix_bulk_xpath + row3_tag_name)[0].text.split() # TODO? allow math? for i, cor in enumerate(row1): row1[i] = float(cor) * BOHR_A for i, cor in enumerate(row2): row2[i] = float(cor) * BOHR_A for i, cor in enumerate(row3): row3[i] = float(cor) * BOHR_A cell = [row1, row2, row3] # create new structure Node struc = StructureData(cell=cell) struc.pbc = [True, True, True] elif root.xpath(bravaismatrix_film_xpath + row1_tag_name): # film calculation row1 = root.xpath(bravaismatrix_film_xpath + row1_tag_name)[0].text.split() row2 = root.xpath(bravaismatrix_film_xpath + row2_tag_name)[0].text.split() row3 = root.xpath(bravaismatrix_film_xpath + row3_tag_name)[0].text.split() for i, cor in enumerate(row1): row1[i] = float(cor) * BOHR_A for i, cor in enumerate(row2): row2[i] = float(cor) * BOHR_A for i, cor in enumerate(row3): row3[i] = float(cor) * BOHR_A # row3 = [0, 0, 0]#? TODO:what has it to be in this case? cell = [row1, row2, row3] struc = StructureData(cell=cell) struc.pbc = [True, True, False] if cell is None: print('Could not extract Bravias matrix out of inp.xml. Is the ' 'Bravias matrix explicitly given? i.e Latnam definition ' 'not supported.') return None # get species for atom kinds #species = root.xpath(species_xpath) species_name = root.xpath(species_xpath + '/@' + species_attrib_name) species_element = root.xpath(species_xpath + '/@' + species_attrib_element) # alternativ: loop over species and species.get(species_attrib_name) # save species info in a dict species_dict = {} for i, spec in enumerate(species_name): species_dict[spec] = {species_attrib_element: species_element[i]} # Now we have to get all atomgroups, look what their species is and # their positions are. # Then we append them to the new structureData all_atom_groups = root.xpath(all_atom_groups_xpath) for atom_group in all_atom_groups: current_species = atom_group.get(atom_group_attrib_species) group_atom_positions_abs = atom_group.xpath(atom_group_tag_abspos) group_atom_positions_rel = atom_group.xpath(atom_group_tag_relpos) group_atom_positions_film = atom_group.xpath( atom_group_tag_filmpos) if group_atom_positions_abs: # we have absolute positions for atom in group_atom_positions_abs: postion_a = atom.text.split() # allow for math *, / for i, pos in enumerate(postion_a): if '/' in pos: temppos = pos.split('/') postion_a[i] = float(temppos[0]) / float( temppos[1]) elif '*' in pos: temppos = pos.split('*') postion_a[i] = float(temppos[0]) * float( temppos[1]) else: postion_a[i] = float(pos) postion_a[i] = postion_a[i] * BOHR_A # append atom to StructureData struc.append_atom(position=postion_a, symbols=species_dict[current_species] [species_attrib_element]) elif group_atom_positions_rel: # we have relative positions # TODO: check if film or 1D calc, because this is not allowed! I guess for atom in group_atom_positions_rel: postion_r = atom.text.split() # allow for math * / for i, pos in enumerate(postion_r): if '/' in pos: temppos = pos.split('/') postion_r[i] = float(temppos[0]) / float( temppos[1]) elif '*' in pos: temppos = pos.split('*') postion_r[i] = float(temppos[0]) * float( temppos[1]) else: postion_r[i] = float(pos) # now transform to absolute Positions new_abs_pos = rel_to_abs(postion_r, cell) # append atom to StructureData struc.append_atom(position=new_abs_pos, symbols=species_dict[current_species] [species_attrib_element]) elif group_atom_positions_film: # Do we support mixture always, or only in film case? # either support or throw error for atom in group_atom_positions_film: # film pos are rel rel abs, therefore only transform first two coordinates postion_f = atom.text.split() # allow for math * / for i, pos in enumerate(postion_f): if '/' in pos: temppos = pos.split('/') postion_f[i] = float(temppos[0]) / float( temppos[1]) elif '*' in postion_f[i]: temppos = pos.split('*') postion_f[i] = float(temppos[0]) * float( temppos[1]) else: postion_f[i] = float(pos) # now transform to absolute Positions postion_f[2] = postion_f[2] * BOHR_A new_abs_pos = rel_to_abs_f(postion_f, cell) # append atom to StructureData struc.append_atom(position=new_abs_pos, symbols=species_dict[current_species] [species_attrib_element]) else: # TODO throw error print('I should never get here, 1D not supported yet, ' 'I only know relPos, absPos, filmPos') # TODO throw error # TODO DATA-DATA links are not wanted, you might want to use a cf instead #struc.add_link_from(self, label='self.structure', link_type=LinkType.CREATE) # label='self.structure' # return {label : struc} return struc