Esempio n. 1
0
    def get_step_structure(self, index, custom_kinds=None):
        """
        Return an AiiDA :py:class:`aiida.orm.nodes.data.structure.StructureData` node
        (not stored yet!) with the coordinates of the given step, identified by
        its index. If you know only the step value, use the
        :py:meth:`.get_index_from_stepid` method to get the corresponding index.

        .. note:: The periodic boundary conditions are always set to True.

        .. versionadded:: 0.7
           Renamed from step_to_structure

        :param index: The index of the step that you want to retrieve, from
           0 to ``self.numsteps- 1``.
        :param custom_kinds: (Optional) If passed must be a list of
          :py:class:`aiida.orm.nodes.data.structure.Kind` objects. There must be one
          kind object for each different string in the ``symbols`` array, with
          ``kind.name`` set to this string.
          If this parameter is omitted, the automatic kind generation of AiiDA
          :py:class:`aiida.orm.nodes.data.structure.StructureData` nodes is used,
          meaning that the strings in the ``symbols`` array must be valid
          chemical symbols.
        """
        from aiida.orm.nodes.data.structure import StructureData, Kind, Site

        # ignore step, time, and velocities
        _, _, cell, symbols, positions, _ = self.get_step_data(index)

        if custom_kinds is not None:
            kind_names = []
            for k in custom_kinds:
                if not isinstance(k, Kind):
                    raise TypeError(
                        'Each element of the custom_kinds list must '
                        'be a aiida.orm.nodes.data.structure.Kind object')
                kind_names.append(k.name)
            if len(kind_names) != len(set(kind_names)):
                raise ValueError(
                    'Multiple kinds with the same name passed as custom_kinds')
            if set(kind_names) != set(symbols):
                raise ValueError('If you pass custom_kinds, you have to '
                                 'pass one Kind object for each symbol '
                                 'that is present in the trajectory. You '
                                 'passed {}, but the symbols are {}'.format(
                                     sorted(kind_names), sorted(symbols)))

        struc = StructureData(cell=cell)
        if custom_kinds is not None:
            for _k in custom_kinds:
                struc.append_kind(_k)
            for _s, _p in zip(symbols, positions):
                struc.append_site(Site(kind_name=_s, position=_p))
        else:
            for _s, _p in zip(symbols, positions):
                # Automatic species generation
                struc.append_atom(symbols=_s, position=_p)

        return struc
Esempio n. 2
0
def standardize_cell(structure,
                     symprec,
                     angle_tolerance,
                     to_primitive=False,
                     no_idealize=False):
    """compute the standardised cell for an AiiDA structure

    When computing symmetry, atomic sites with the same **Kind** are treated as
    symmetrically equivalent (rather than just the atomic elements).

    Parameters
    ----------
    structure: aiida.StructureData
    to_primitive: bool
        If True, the standardized primitive cell is created.
    no_idealize: bool
        If True, it is disabled to idealize lengths and angles of
        basis vectors and positions of atoms according to crystal symmetry.
    symprec: float
        Symmetry search tolerance in the unit of length.
    angle_tolerance: float or None
        Symmetry search tolerance in the unit of angle degrees.
        If the value is negative or None, an internally optimized routine
        is used to judge symmetry.

    Returns
    -------
    aiida.StructureData

    """
    from aiida.orm.nodes.data.structure import Site

    structure = convert_structure(structure, "aiida")

    cell, int2kind_map = prepare_for_spglib(structure)

    new_cell = spglib.standardize_cell(
        cell,
        to_primitive=to_primitive,
        no_idealize=no_idealize,
        symprec=symprec,
        angle_tolerance=-1 if angle_tolerance is None else angle_tolerance,
    )
    if new_cell is None:
        raise ValueError("standardization of cell failed")

    new_structure = structure.clone()
    new_structure.clear_sites()
    new_structure.cell = new_cell[0].tolist()
    positions = frac_to_cartesian(new_structure.cell, new_cell[1])
    for position, eid in zip(positions, new_cell[2].tolist()):
        new_structure.append_site(
            Site(kind_name=int2kind_map[eid], position=position))

    return new_structure
Esempio n. 3
0
    def get_structuredata(self):
        """Return a StructureData object based on the data in the input file.

        All of the names corresponding of the ``Kind`` objects composing the ``StructureData`` object will match those
        found in the ``ATOMIC_SPECIES`` block, so the pseudo potentials 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: structure data node of the structure defined in the input file.
        :rtype: :class:`~aiida.orm.nodes.data.structure.StructureData`
        """
        from aiida.orm.nodes.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)

        data = self.get_structure_from_qeinput()
        species = self.atomic_species

        structure = StructureData()
        structure.set_attribute('cell', data['cell'])

        for mass, name, pseudo in zip(species['masses'], species['names'],
                                      species['pseudo_file_names']):
            try:
                symbols = valid_elements_regex.search(pseudo).group(
                    'ele').capitalize()
            except Exception:
                raise InputValidationError(
                    'could not determine element name from pseudo name: {}'.
                    format(pseudo))
            structure.append_kind(Kind(name=name, symbols=symbols, mass=mass))

        for symbol, position in zip(data['atom_names'], data['positions']):
            structure.append_site(Site(kind_name=symbol, position=position))

        return structure
Esempio n. 4
0
def reset_kind_names(structure, kind_names):
    """reset the kind names (per site) of a StructureData node

    Parameters
    ----------
    structure : aiida.StructureData
    kind_names : list[str]
        a name for each site of the structure

    Returns
    -------
    aiida.StructureData
        a cloned node

    Raises
    ------
    AssertionError
        if the kind_names are not compatible with the current sites

    """
    from aiida.orm.nodes.data.structure import Kind, Site

    if len(structure.sites) != len(kind_names):
        raise AssertionError("lengths of sites & names not equal")
    sites = structure.sites
    kinds = {k.name: k for k in structure.kinds}
    structure = structure.clone()
    structure.clear_sites()
    structure.clear_kinds()

    new_kinds = {}
    for site, name in zip(sites, kind_names):
        if name not in new_kinds:
            kind_dict = kinds[site.kind_name].get_raw()
            kind_dict["name"] = name
            new_kind = Kind(raw=kind_dict)
            structure.append_kind(new_kind)
            new_kinds[name] = new_kind
        old_symbols = kinds[site.kind_name].symbols
        new_symbols = new_kinds[name].symbols
        if old_symbols != new_symbols:
            raise AssertionError("inconsistent symbols: {} != {}".format(
                old_symbols, new_symbols))
        new_site = Site(kind_name=name, position=site.position)
        structure.append_site(new_site)

    return structure
Esempio n. 5
0
def get_aiida_structure_data(
        optimade_structure: OptimadeStructure) -> StructureData:
    """Get AiiDA `StructureData` from OPTIMADE structure.

    Parameters:
        optimade_structure: OPTIMADE structure.

    Returns:
        AiiDA `StructureData` Node.

    """
    if "optimade.adapters" in repr(globals().get("StructureData")):
        warn(AIIDA_NOT_FOUND, AdapterPackageNotFound)
        return None

    attributes = optimade_structure.attributes

    # Convert null/None values to float("nan")
    lattice_vectors, adjust_cell = pad_cell(attributes.lattice_vectors)
    structure = StructureData(cell=lattice_vectors)

    # Add Kinds
    for kind in attributes.species:
        symbols = []
        concentration = []
        mass = 0.0
        for index, chemical_symbol in enumerate(kind.chemical_symbols):
            # NOTE: The non-chemical element identifier "X" is identical to how AiiDA handles this,
            # so it will be treated the same as any other true chemical identifier.
            if chemical_symbol == "vacancy":
                # Skip. This is how AiiDA handles vacancies;
                # to not include them, while keeping the concentration in a site less than 1.
                continue
            else:
                symbols.append(chemical_symbol)
                concentration.append(kind.concentration[index])

                # AiiDA needs a definition for the mass, and for it to be > 0
                # mass is OPTIONAL for OPTIMADE structures
                if kind.mass:
                    mass += kind.concentration[index] * kind.mass[index]

        if not mass:
            warn(
                f"No mass defined for <species(name={kind.name!r})>, will default to setting mass to 1.0.",
                ConversionWarning,
            )

        structure.append_kind(
            Kind(symbols=symbols,
                 weights=concentration,
                 mass=mass or 1.0,
                 name=kind.name))

    # Add Sites
    for index in range(attributes.nsites):
        # range() to ensure 1-to-1 between kind and site
        structure.append_site(
            Site(
                kind_name=attributes.species_at_sites[index],
                position=attributes.cartesian_site_positions[index],
            ))

    if adjust_cell:
        structure._adjust_default_cell(
            pbc=[bool(dim.value) for dim in attributes.dimension_types])

    return structure
Esempio n. 6
0
def spglib_tuple_to_structure(structure_tuple, kind_info=None, kinds=None):  # pylint: disable=too-many-locals
    """
    Convert a tuple of the format (cell, scaled_positions, element_numbers) to an AiiDA structure.

    Unless the element_numbers are identical to the Z number of the atoms,
    you should pass both kind_info and kinds, with the same format as returned
    by get_tuple_from_aiida_structure.

    :param structure_tuple: the structure in format (structure_tuple, kind_info)
    :param kind_info: a dictionary mapping the kind_names to
       the numbers used in element_numbers. If not provided, assumes {element_name: element_Z}
    :param kinds: a list of the kinds of the structure.
    """
    if kind_info is None and kinds is not None:
        raise ValueError('If you pass kind_info, you should also pass kinds')
    if kinds is None and kind_info is not None:
        raise ValueError('If you pass kinds, you should also pass kind_info')

    #Z = {v['symbol']: k for k, v in elements.items()}
    cell, rel_pos, numbers = structure_tuple
    if kind_info:
        _kind_info = copy.copy(kind_info)
        _kinds = copy.copy(kinds)
    else:
        try:
            # For each site
            symbols = [elements[num]['symbol'] for num in numbers]
        except KeyError as exc:
            raise ValueError(
                'You did not pass kind_info, but at least one number '
                'is not a valid Z number: {}'.format(exc.args[0]))

        _kind_info = {elements[num]['symbol']: num for num in set(numbers)}
        # Get the default kinds
        _kinds = [Kind(symbols=sym) for sym in set(symbols)]

    _kinds_dict = {k.name: k for k in _kinds}
    # Now I will use in any case _kinds and _kind_info
    if len(_kind_info) != len(set(_kind_info.values())):
        raise ValueError(
            'There is at least a number repeated twice in kind_info!')
    # Invert the mapping
    mapping_num_kindname = {v: k for k, v in _kind_info.items()}
    # Create the actual mapping
    try:
        mapping_to_kinds = {
            num: _kinds_dict[kindname]
            for num, kindname in mapping_num_kindname.items()
        }
    except KeyError as exc:
        raise ValueError("Unable to find '{}' in the kinds list".format(
            exc.args[0]))

    try:
        site_kinds = [mapping_to_kinds[num] for num in numbers]
    except KeyError as exc:
        raise ValueError(
            'Unable to find kind in kind_info for number {}'.format(
                exc.args[0]))

    structure = StructureData(cell=cell)
    for k in _kinds:
        structure.append_kind(k)
    abs_pos = np.dot(rel_pos, cell)
    if len(abs_pos) != len(site_kinds):
        raise ValueError(
            'The length of the positions array is different from the length of the element numbers'
        )

    for kind, pos in zip(site_kinds, abs_pos):
        structure.append_site(Site(kind_name=kind.name, position=pos))

    return structure
Esempio n. 7
0
def get_last_structure(xmldoc, input_structure):

    itemlist = xmldoc.getElementsByTagName('module')

    # Use the last "geometry" module, and not the "Finalization" one.
    finalmodule = None
    for item in itemlist:
        # Get a "geometry" module by the criteria:
        if 'serial' in list(item.attributes.keys()):
            if 'dictRef' in list(item.attributes.keys()):
                if item.attributes['dictRef'].value != "SCF":
                    finalmodule = item

    # In case there is no appropriate data, fall back and at least return the initial structure
    # (this should not be necessary, as the initial Geometry module is opened very soon)
    if finalmodule is None:
        return False, input_structure

    # When using floating sites, Siesta associates 'atomic positions' to them, and
    # the structure (and forces) in the XML file include these fake atoms.
    # In order to return physical structures and forces, we need to remove them.
    # Recall that the input structure is the physical one, as the floating sites
    # are specified in the 'basis' input

    number_of_real_atoms = len(input_structure.sites)

    atoms = finalmodule.getElementsByTagName('atom')
    cellvectors = finalmodule.getElementsByTagName('latticeVector')

    atomlist = []

    for atm in atoms[0:number_of_real_atoms]:
        kind = atm.attributes['elementType'].value
        x = atm.attributes['x3'].value
        y = atm.attributes['y3'].value
        z = atm.attributes['z3'].value
        atomlist.append([kind, [float(x), float(y), float(z)]])

    cell = []
    for latt in cellvectors:
        data = latt.childNodes[0].data.split()
        cell.append([float(s) for s in data])

    # Generally it is better to clone the input structure and reset the data, since site
    # 'names' are not handled by the CML file (at least not in Siesta versions <= 4.0)

    import copy
    from aiida.orm.nodes.data.structure import Site
    new_structure = input_structure.clone()
    new_structure.reset_cell(cell)
    new_structure.clear_sites()
    for i in range(number_of_real_atoms):
        new_site = Site(site=input_structure.sites[i])
        new_site.position = copy.deepcopy(atomlist[i][1])
        new_structure.append_site(new_site)

    # The most obvious alternative does not work, as the reset method below does not
    # work if the numbers do not match.
    #
    ## new_pos = [atom[1] for atom in atomlist]
    ## new_structure.reset_sites_positions(new_pos)

    return True, new_structure
Esempio n. 8
0
def convert_structure(structure, out_type):
    """convert an AiiDA, ASE or dict object to another type

    Parameters
    ----------
    structure: aiida.StructureData or dict or ase.Atoms
    out_type: str
        one of: 'dict', 'ase' or 'aiida

    """
    from aiida.orm.nodes.data.structure import Kind, Site
    from aiida.plugins import DataFactory

    structure_data_cls = DataFactory("structure")

    if isinstance(structure, dict):
        if "symbols" in structure and "atomic_numbers" not in structure:
            structure["atomic_numbers"] = symbols2numbers(structure["symbols"])
        if ("fcoords" in structure and "lattice" in structure
                and "ccoords" not in structure):
            structure["ccoords"] = frac_to_cartesian(structure["lattice"],
                                                     structure["fcoords"])
        required_keys = ["pbc", "lattice", "ccoords", "atomic_numbers"]
        if not set(structure.keys()).issuperset(required_keys):
            raise AssertionError(
                "dict keys are not a superset of: {}".format(required_keys))

    if out_type == "dict":
        if isinstance(structure, dict):
            return structure
        if isinstance(structure, structure_data_cls):
            return structure_to_dict(structure)
        if isinstance(structure, Atoms):
            return {
                "pbc": structure.pbc.tolist(),
                "atomic_numbers": structure.get_atomic_numbers().tolist(),
                "ccoords": structure.positions.tolist(),
                "lattice": structure.cell.tolist(),
                "equivalent": structure.get_tags().tolist(),
            }
        raise TypeError("structure: {}".format(structure))
    elif out_type == "ase":
        if isinstance(structure, Atoms):
            return structure
        if isinstance(structure, structure_data_cls):
            return structure.get_ase()
        if isinstance(structure, dict):
            return Atoms(
                numbers=structure["atomic_numbers"],
                cell=structure["lattice"],
                positions=structure["ccoords"],
                pbc=structure["pbc"],
                tags=structure.get("equivalent", None),
            )
        raise TypeError("structure: {}".format(structure))
    elif out_type == "aiida":
        if isinstance(structure, structure_data_cls):
            return structure
        if isinstance(structure, Atoms):
            return structure_data_cls(ase=structure)
        if isinstance(structure, dict):
            if structure.get("kinds") is not None:
                struct = structure_data_cls(cell=structure["lattice"])
                struct.set_pbc(structure["pbc"])
                for kind, ccoord in zip(structure["kinds"],
                                        structure["ccoords"]):
                    if not isinstance(kind, Kind):
                        kind = Kind(raw=kind)
                    if kind.name not in struct.get_site_kindnames():
                        struct.append_kind(kind)
                    struct.append_site(
                        Site(position=ccoord, kind_name=kind.name))
                return struct
            else:
                atoms = Atoms(
                    numbers=structure["atomic_numbers"],
                    cell=structure["lattice"],
                    positions=structure["ccoords"],
                    pbc=structure["pbc"],
                    tags=structure.get("equivalent", None),
                )
                return structure_data_cls(ase=atoms)
        raise ValueError("input type: {}".format(structure))
    raise ValueError("output type: {}".format(out_type))
Esempio n. 9
0
def test_multiple_kinds_auto(cp2k_code, cp2k_basissets, cp2k_pseudos,
                             clear_database):  # pylint: disable=unused-argument
    """Testing CP2K with multiple KIND sections for the same symbol, auto-assigned"""

    # structure
    atoms = ase.build.molecule("H2O")
    atoms.center(vacuum=2.0)

    structure = StructureData(ase=atoms)

    structure = StructureData(cell=atoms.cell, pbc=atoms.pbc)
    structure.append_kind(Kind(name="O", symbols="O"))
    structure.append_kind(Kind(name="H1", symbols="H"))
    structure.append_kind(Kind(name="H2", symbols="H"))

    # ASE stores it as OHH
    assert all(atoms.numbers == [8, 1, 1]
               ), "ASE changed positions of atoms in generated structure"
    structure.append_site(Site(kind_name="O", position=atoms.positions[0]))
    structure.append_site(Site(kind_name="H1", position=atoms.positions[1]))
    structure.append_site(Site(kind_name="H2", position=atoms.positions[2]))

    # parameters
    parameters = Dict(
        dict={
            "FORCE_EVAL": {
                "METHOD": "Quickstep",
                "DFT": {
                    "QS": {
                        "EPS_DEFAULT": 1.0e-12,
                        "WF_INTERPOLATION": "ps",
                        "EXTRAPOLATION_ORDER": 3,
                    },
                    "MGRID": {
                        "NGRIDS": 4,
                        "CUTOFF": 280,
                        "REL_CUTOFF": 30
                    },
                    "XC": {
                        "XC_FUNCTIONAL": {
                            "_": "LDA"
                        }
                    },
                    "POISSON": {
                        "PERIODIC": "none",
                        "PSOLVER": "MT"
                    },
                },
                "SUBSYS": {
                    "KIND": [
                        {
                            "_": "O",
                            "POTENTIAL": "GTH " + cp2k_pseudos["O"].name,
                            "BASIS_SET": cp2k_basissets["O"].name,
                        },
                        {
                            "_": "H1",
                            "ELEMENT": "H",
                        },
                        {
                            "_": "H2",
                            # ELEMENT keyword is not even necessary since CP2K guesses from KIND section parameter
                            # these KIND sections are occasionally encountered when specifying MAGNETIZATION
                            # for different sites
                        },
                    ]
                },
            }
        })

    options = {
        "resources": {
            "num_machines": 1,
            "num_mpiprocs_per_machine": 1
        },
        "max_wallclock_seconds": 1 * 3 * 60,
    }

    inputs = {
        "structure": structure,
        "parameters": parameters,
        "code": cp2k_code,
        "metadata": {
            "options": options
        },
        "basissets": cp2k_basissets,
        "pseudos": cp2k_pseudos,
    }

    _, calc_node = run_get_node(CalculationFactory("cp2k"), **inputs)

    with calc_node.open("aiida.inp") as fhandle:
        lines = fhandle.readlines()

    for line in lines:
        print(line)
    assert calc_node.exit_status == 0

    assert any("&KIND H1" in line
               for line in lines), "&KIND H1 not found in generated input"
    assert any("&KIND H2" in line
               for line in lines), "&KIND H2 not found in generated input"
    assert len(
        [True for line in lines if '&KIND H' in line]
    ) < 3, "More than the expected 2 &KIND H sections found in generated input"