def test_subclassing(self): from aiida.orm.data.structure import StructureData from aiida.orm.data.parameter import ParameterData from aiida.orm import Node, Data from aiida.orm.querybuilder import QueryBuilder s = StructureData() s._set_attr('cat', 'miau') s.store() d = Data() d._set_attr('cat', 'miau') d.store() p = ParameterData(dict=dict(cat='miau')) p.store() n = Node() n._set_attr('cat', 'miau') n.store() # Now when asking for a node with attr.cat==miau, I want 4 esults: qb = QueryBuilder().append(Node, filters={'attributes.cat': 'miau'}) self.assertEqual(qb.count(), 4) qb = QueryBuilder().append(Data, filters={'attributes.cat': 'miau'}) self.assertEqual(qb.count(), 3) # If I'm asking for the specific lowest subclass, I want one result for cls in (StructureData, ParameterData): qb = QueryBuilder().append(cls, filters={'attributes.cat': 'miau'}) self.assertEqual(qb.count(), 1) # Now I am not allow the subclassing, which should give 1 result for each for cls in (StructureData, ParameterData, Node, Data): qb = QueryBuilder().append(cls, filters={'attributes.cat': 'miau'}, subclassing=False) self.assertEqual(qb.count(), 1)
def get_structuredata(self): """ Return a StructureData object based on the data in the input file. This uses all of the data in the input file to do the necessary unit conversion, ect. and then creates an AiiDA StructureData object. All of the names corresponding of the Kind objects composing the StructureData object will match those found in the ATOMIC_SPECIES block, so the pseudopotentials can be linked to the calculation using the kind.name for each specific type of atom (in the event that you wish to use different pseudo's for two or more of the same atom). :return: StructureData object of the structure in the input file :rtype: aiida.orm.data.structure.StructureData :raises aiida.common.exceptions.ParsingError: if there are issues parsing the input. """ from aiida.orm.data.structure import StructureData, Kind, Site valid_elements_regex = re.compile( """ (?P<ele> H | He | Li | Be | B | C | N | O | F | Ne | Na | Mg | Al | Si | P | S | Cl | Ar | K | Ca | Sc | Ti | V | Cr | Mn | Fe | Co | Ni | Cu | Zn | Ga | Ge | As | Se | Br | Kr | Rb | Sr | Y | Zr | Nb | Mo | Tc | Ru | Rh | Pd | Ag | Cd | In | Sn | Sb | Te | I | Xe | Cs | Ba | Hf | Ta | W | Re | Os | Ir | Pt | Au | Hg | Tl | Pb | Bi | Po | At | Rn | Fr | Ra | Rf | Db | Sg | Bh | Hs | Mt | La | Ce | Pr | Nd | Pm | Sm | Eu | Gd | Tb | Dy | Ho | Er | Tm | Yb | Lu | # Lanthanides Ac | Th | Pa | U | Np | Pu | Am | Cm | Bk | Cf | Es | Fm | Md | No | Lr | # Actinides ) [^a-z] # Any specification of an element is followed by some number # or capital letter or special character. """, re.X | re.I) structure_dict = self.get_structure_from_qeinput() # instance and set the cell structuredata = StructureData() structuredata._set_attr('cell', structure_dict['cell'].tolist()) ################# KINDS ########################## for mass, name, pseudo in zip( structure_dict['species']['masses'], structure_dict['species']['names'], structure_dict['species']['pseudo_file_names']): try: # IMPORTANT: The symbols is parsed from the Pseudo file name # Is this the best way?? # Should we also try from the associated kind name? symbols = valid_elements_regex.search(pseudo).group( 'ele').capitalize() except Exception as e: raise InputValidationError( 'I could not read an element name in {}'.format( match.group(0))) structuredata.append_kind( Kind( name=name, symbols=symbols, mass=mass, )) [ structuredata.append_site(Site( kind_name=sym, position=pos, )) for sym, pos in zip(structure_dict['atom_names'], structure_dict['positions']) ] return structuredata
def get_structuredata_from_qeinput(filepath=None, text=None, namelists=None, atomic_species=None, atomic_positions=None, cell_parameters=None): """ Function that receives either :param filepath: the filepath storing **or** :param text: the string of a standard QE-input file. An instance of :func:`StructureData` is initialized with kinds, positions and cell as defined in the input file. This function can deal with ibrav being set different from 0 and the cell being defined with celldm(n) or A,B,C, cosAB etc. """ from aiida.orm.data.structure import StructureData, Kind, Site #~ from aiida.common.utils import get_fortfloat valid_elements_regex = re.compile( """ (?P<ele> H | He | Li | Be | B | C | N | O | F | Ne | Na | Mg | Al | Si | P | S | Cl | Ar | K | Ca | Sc | Ti | V | Cr | Mn | Fe | Co | Ni | Cu | Zn | Ga | Ge | As | Se | Br | Kr | Rb | Sr | Y | Zr | Nb | Mo | Tc | Ru | Rh | Pd | Ag | Cd | In | Sn | Sb | Te | I | Xe | Cs | Ba | Hf | Ta | W | Re | Os | Ir | Pt | Au | Hg | Tl | Pb | Bi | Po | At | Rn | Fr | Ra | Rf | Db | Sg | Bh | Hs | Mt | La | Ce | Pr | Nd | Pm | Sm | Eu | Gd | Tb | Dy | Ho | Er | Tm | Yb | Lu | # Lanthanides Ac | Th | Pa | U | Np | Pu | Am | Cm | Bk | Cf | Es | Fm | Md | No | Lr | # Actinides ) [^a-z] # Any specification of an element is followed by some number # or capital letter or special character. """, re.X | re.I) # I need either a valid filepath or the text of the qeinput file: if filepath: with open(filepath) as f: txt = f.read() elif text: txt = text else: raise InputValidationError( 'Provide either a filepath or text to be parsed') if namelists is None: namelists = parse_namelists(text) if atomic_species is None: atomic_species = parse_atomic_species(txt) if cell_parameters is None: cell_parameters = parse_cell_parameters(txt) if atomic_positions is None: atomic_positions = parse_atomic_positions(txt) # First, I'm trying to figure out whether alat was specified: system_dict = namelists['SYSTEM'] if 'a' in system_dict and 'celldm(1)' in system_dict: # The user should define exclusively in celldm or ABC-system raise InputValidationError('Both a and celldm(1) specified') elif 'a' in system_dict: alat = system_dict['a'] using_celldm = False elif 'celldm(1)' in system_dict: alat = bohr_to_ang * system_dict['celldm(1)'] using_celldm = True else: alat = None using_celldm = None cell = get_cell_from_parameters(cell_parameters, system_dict, alat, using_celldm) # instance and set the cell structuredata = StructureData() structuredata._set_attr('cell', cell.tolist()) ################# KINDS ########################## for mass, name, pseudo in zip(atomic_species['masses'], atomic_species['names'], atomic_species['pseudo_file_names']): try: symbols = valid_elements_regex.search(pseudo).group( 'ele').capitalize() except Exception as e: raise InputValidationError( 'I could not read an element name in {}'.format( match.group(0))) structuredata.append_kind(Kind( name=name, symbols=symbols, mass=mass, )) ################## POSITIONS ####################### positions_units = atomic_positions['units'] positions = np.array(atomic_positions['positions']) if positions_units is None: raise InputValidationError( "There is no unit for positions\n" "This is deprecated behavior for QE.\n" "In addition the default values by CP and PW differ (bohr and alat)" ) elif positions_units == 'angstrom': pass elif positions_units == 'bohr': positions = bohr_to_ang * positions elif positions_units == 'crystal': positions = np.dot(positions, cell) elif positions_units == 'alat': positions = np.linalg.norm(cell[0]) * positions elif positions_units == 'crystal_sg': raise NotImplementedError('crystal_sg is not implemented') else: valid_positions_units = ('alat', 'bohr', 'angstrom', 'crystal', 'crystal_sg') raise InputValidationError('\nFound atom unit {}, which is not\n' 'among the valid units: {}'.format( positions_units, ', '.join(valid_positions_units))) ######### DEFINE SITES ###################### positions = positions.tolist() [ structuredata.append_site(Site( kind_name=sym, position=pos, )) for sym, pos in zip(atomic_positions['names'], positions) ] return structuredata