def from_abivars(cls, *args, **kwargs): """ Returns a new instance from a dictionary with the variables used in ABINIT to define the unit cell. """ kwargs.update(dict(*args)) d = kwargs rprim = d.get("rprim", None) angdeg = d.get("angdeg", None) acell = d["acell"] # Call pymatgen constructors (note that pymatgen uses Angstrom instead of Bohr). if rprim is not None: assert angdeg is None rprim = np.reshape(rprim, (3, 3)) rprimd = [float(acell[i]) * rprim[i] for i in range(3)] return cls(ArrayWithUnit(rprimd, "bohr").to("ang")) elif angdeg is not None: # angdeg(0) is the angle between the 2nd and 3rd vectors, # angdeg(1) is the angle between the 1st and 3rd vectors, # angdeg(2) is the angle between the 1st and 2nd vectors, raise NotImplementedError("angdeg convention should be tested") angles = angdeg angles[1] = -angles[1] l = ArrayWithUnit(acell, "bohr").to("ang") return cls.from_lengths_and_angles(l, angdeg) else: raise ValueError( "Don't know how to construct a Lattice from dict: %s" % str(d))
def read_cart_forces(self, unit="eV ang^-1"): """ Read and return a |numpy-array| with the cartesian forces in unit ``unit``. Shape (natom, 3) """ return ArrayWithUnit(self.read_value("cartesian_forces"), "Ha bohr^-1").to(unit)
def test_factors(self): e = EnergyArray([27.21138386, 1], "eV").to("Ha") self.assertTrue(str(e).endswith("Ha")) l = LengthArray([1.0], "ang").to("bohr") self.assertTrue(str(l).endswith(" bohr")) v = ArrayWithUnit([1, 2, 3], "bohr^3").to("ang^3") self.assertTrue(str(v).endswith(' ang^3'))
def test_factors(self): e = EnergyArray([27.21138386, 1], "eV").to("Ha") self.assertTrue(str(e) == "[ 0.99999996 0.03674932] Ha") l = LengthArray([1.0], "ang").to("bohr") self.assertTrue(str(l) == "[ 1.88972612] bohr") v = ArrayWithUnit([1, 2, 3], "bohr^3").to("ang^3") self.assertTrue(str(v) == '[ 0.14818471 0.29636942 0.44455413] ang^3')
def to_abivars(self): "Returns a dictionary with the abinit variables." types_of_specie = self.types_of_specie natom = self.num_sites znucl_type = [specie.number for specie in types_of_specie] znucl_atoms = self.atomic_numbers typat = np.zeros(natom, np.int) for (atm_idx, site) in enumerate(self): typat[atm_idx] = types_of_specie.index(site.specie) + 1 rprim = ArrayWithUnit(self.lattice.matrix, "ang").to("bohr") xred = np.reshape([site.frac_coords for site in self], (-1,3)) return { "acell" : 3 * [1.0], "rprim" : rprim, "natom" : natom, "ntypat": len(types_of_specie), "typat" : typat, "xred" : xred, "znucl" : znucl_type, }
def from_dict(cls, d, fmt=None, **kwargs): """ Create a Lattice from a dictionary containing the a, b, c, alpha, beta, and gamma parameters. """ if fmt == "abivars": kwargs.update(d) d = kwargs rprim = d.get("rprim", None) angdeg = d.get("angdeg", None) acell = d["acell"] # Call pymatgen constructors (note that pymatgen uses Angstrom instead of Bohr). if rprim is not None: assert angdeg is None rprim = np.reshape(rprim, (3, 3)) rprimd = [float(acell[i]) * rprim[i] for i in range(3)] return cls(ArrayWithUnit(rprimd, "bohr").to("ang")) elif angdeg is not None: # angdeg(0) is the angle between the 2nd and 3rd vectors, # angdeg(1) is the angle between the 1st and 3rd vectors, # angdeg(2) is the angle between the 1st and 2nd vectors, raise NotImplementedError("angdeg convention should be tested") angles = angdeg angles[1] = -angles[1] l = ArrayWithUnit(acell, "bohr").to("ang") return cls.from_lengths_and_angles(l, angdeg) else: raise ValueError( "Don't know how to construct a Lattice from dict: %s" % str(d)) if "matrix" in d: return cls(d["matrix"]) else: return cls.from_parameters(d["a"], d["b"], d["c"], d["alpha"], d["beta"], d["gamma"])
def structure_from_etsf_file(ncdata, site_properties=None): """ Reads and returns a pymatgen structure from a NetCDF file containing crystallographic data in the ETSF-IO format. Args: ncdata: filename or NetcdfReader instance. site_properties: Dictionary with site properties. """ ncdata, closeit = as_ncreader(ncdata) # TODO check whether atomic units are used lattice = ArrayWithUnit(ncdata.read_value("primitive_vectors"), "bohr").to("ang") red_coords = ncdata.read_value("reduced_atom_positions") natom = len(red_coords) znucl_type = ncdata.read_value("atomic_numbers") # type_atom[0:natom] --> index Between 1 and number of atom species type_atom = ncdata.read_value("atom_species") # Fortran to C index and float --> int conversion. species = natom * [None] for atom in range(natom): type_idx = type_atom[atom] - 1 species[atom] = int(znucl_type[type_idx]) d = {} if site_properties is not None: for prop in site_properties: d[property] = ncdata.read_value(prop) structure = Structure(lattice, species, red_coords, site_properties=d) # Quick and dirty hack. # I need an abipy structure since I need to_abivars and other methods. #from pymatgen.io.abinitio.abiobjects import AbiStructure #structure.__class__ = AbiStructure try: from abipy.core.structure import Structure as AbipyStructure structure.__class__ = AbipyStructure except ImportError: pass if closeit: ncdata.close() return structure
def structure_to_abivars(structure, **kwargs): """ Receives a structure and returns a dictionary with the ABINIT variables. """ types_of_specie = structure.types_of_specie natom = structure.num_sites znucl_type = [specie.number for specie in types_of_specie] znucl_atoms = structure.atomic_numbers typat = np.zeros(natom, np.int) for atm_idx, site in enumerate(structure): typat[atm_idx] = types_of_specie.index(site.specie) + 1 rprim = ArrayWithUnit(structure.lattice.matrix, "ang").to("bohr") xred = np.reshape([site.frac_coords for site in structure], (-1,3)) # Set small values to zero. This usually happens when the CIF file # does not give structure parameters with enough digits. rprim = np.where(np.abs(rprim) > 1e-8, rprim, 0.0) xred = np.where(np.abs(xred) > 1e-8, xred, 0.0) # Info on atoms. d = dict( natom=natom, ntypat=len(types_of_specie), typat=typat, znucl=znucl_type, xred=xred, ) # Add info on the lattice. # Should we use (rprim, acell) or (angdeg, acell) to specify the lattice? geomode = kwargs.pop("geomode", "rprim") #latt_dict = structure.lattice.to_abivars(geomode=geomode) if geomode == "rprim": d.update(dict( acell=3 * [1.0], rprim=rprim)) elif geomode == "angdeg": d.update(dict( acell=3 * [1.0], angdeg=angdeg)) else: raise ValueError("Wrong value for geomode: %s" % geomode) return d
def boxed_molecule(cls, pseudos, cart_coords, acell=3 * (10, )): """ Creates a molecule in a periodic box of lengths acell [Bohr] Args: pseudos: List of pseudopotentials cart_coords: Cartesian coordinates acell: Lengths of the box in *Bohr* """ cart_coords = np.atleast_2d(cart_coords) molecule = pymatgen.Molecule([p.symbol for p in pseudos], cart_coords) l = ArrayWithUnit(acell, "bohr").to("ang") structure = molecule.get_boxed_structure(l[0], l[1], l[2]) return cls(structure)
def test_as_base_units(self): x = ArrayWithUnit([5, 10], "MPa") self.assertArrayEqual(ArrayWithUnit([5000000, 10000000], "Pa"), x.as_base_units)
def j(): return ArrayWithUnit([5, 10], "g")
def structure_from_abivars(cls=None, *args, **kwargs): """ Build a :class:`Structure` object from a dictionary with ABINIT variables. Args: cls: Structure class to be instantiated. pymatgen.core.structure.Structure if cls is None example: al_structure = structure_from_abivars( acell=3*[7.5], rprim=[0.0, 0.5, 0.5, 0.5, 0.0, 0.5, 0.5, 0.5, 0.0], typat=1, xred=[0.0, 0.0, 0.0], ntypat=1, znucl=13, ) `xred` can be replaced with `xcart` or `xangst`. """ kwargs.update(dict(*args)) d = kwargs cls = Structure if cls is None else cls #lattice = Lattice.from_dict(d, fmt="abivars") lattice = lattice_from_abivars(**d) coords, coords_are_cartesian = d.get("xred", None), False if coords is None: coords = d.get("xcart", None) if coords is not None: if "xangst" in d: raise ValueError("xangst and xcart are mutually exclusive") coords = ArrayWithUnit(coords, "bohr").to("ang") else: coords = d.get("xangst", None) coords_are_cartesian = True if coords is None: raise ValueError("Cannot extract coordinates from:\n %s" % str(d)) coords = np.reshape(coords, (-1,3)) znucl_type, typat = d["znucl"], d["typat"] if not isinstance(znucl_type, collections.Iterable): znucl_type = [znucl_type] if not isinstance(typat, collections.Iterable): typat = [typat] if len(typat) != len(coords): raise ValueError("len(typat) != len(coords):\ntypat: %s\ncoords: %s" % (typat, coords)) # Note Fortran --> C indexing #znucl_type = np.rint(znucl_type) species = [znucl_type[typ-1] for typ in typat] return cls(lattice, species, coords, validate_proximity=False, to_unit_cell=False, coords_are_cartesian=coords_are_cartesian)
def lattice_from_abivars(cls=None, *args, **kwargs): """ Returns a `Lattice` object from a dictionary with the Abinit variables `acell` and either `rprim` in Bohr or `angdeg` If acell is not given, the Abinit default is used i.e. [1,1,1] Bohr Args: cls: Lattice class to be instantiated. pymatgen.core.lattice.Lattice if cls is None Example: lattice_from_abivars(acell=3*[10], rprim=np.eye(3)) """ cls = Lattice if cls is None else cls kwargs.update(dict(*args)) d = kwargs rprim = d.get("rprim", None) angdeg = d.get("angdeg", None) acell = d["acell"] if rprim is not None: if angdeg is not None: raise ValueError("angdeg and rprimd are mutually exclusive") rprim = np.reshape(rprim, (3,3)) rprimd = [float(acell[i]) * rprim[i] for i in range(3)] # Call pymatgen constructors (note that pymatgen uses Angstrom instead of Bohr). return cls(ArrayWithUnit(rprimd, "bohr").to("ang")) elif angdeg is not None: angdeg = np.reshape(angdeg, 3) if np.any(angdeg <= 0.): raise ValueError("Angles must be > 0 but got %s" % str(angdeg)) if angdeg.sum() >= 360.: raise ValueError("The sum of angdeg must be lower that 360, angdeg %s" % str(angdeg)) # This code follows the implementation in ingeo.F90 # See also http://www.abinit.org/doc/helpfiles/for-v7.8/input_variables/varbas.html#angdeg tol12 = 1e-12 pi, sin, cos, sqrt = np.pi, np.sin, np.cos, np.sqrt rprim = np.zeros((3,3)) if (abs(angdeg[0] -angdeg[1]) < tol12 and abs(angdeg[1] - angdeg[2]) < tol12 and abs(angdeg[0]-90.) + abs(angdeg[1]-90.) + abs(angdeg[2] -90) > tol12): # Treat the case of equal angles (except all right angles): # generates trigonal symmetry wrt third axis cosang = cos(pi * angdeg[0]/180.0) a2 = 2.0/3.0*(1.0 - cosang) aa = sqrt(a2) cc = sqrt(1.0-a2) rprim[0,0] = aa ; rprim[0,1] = 0.0 ; rprim[0,2] = cc rprim[1,0] = -0.5*aa; rprim[1,1] = sqrt(3.0)*0.5*aa ; rprim[1,2] = cc rprim[2,0] = -0.5*aa; rprim[2,1] = -sqrt(3.0)*0.5*aa; rprim[2,2] = cc else: # Treat all the other cases rprim[0,0] = 1.0 rprim[1,0] = cos(pi*angdeg[2]/180.) rprim[1,1] = sin(pi*angdeg[2]/180.) rprim[2,0] = cos(pi*angdeg[1]/180.) rprim[2,1] = (cos(pi*angdeg[0]/180.0)-rprim[1,0]*rprim[2,0])/rprim[1,1] rprim[2,2] = sqrt(1.0-rprim[2,0]**2-rprim[2,1]**2) # Call pymatgen constructors (note that pymatgen uses Angstrom instead of Bohr). rprimd = [float(acell[i]) * rprim[i] for i in range(3)] return cls(ArrayWithUnit(rprimd, "bohr").to("ang")) raise ValueError("Don't know how to construct a Lattice from dict: %s" % str(d))
def read_cart_forces(self, unit="eV ang^-1"): """Read and return a numpy array with the cartesian forces. Shape (num_steps, natom, 3)""" return ArrayWithUnit(self.read_value("fcart"), "Ha bohr^-1").to(unit)
def structure_to_abivars(structure, **kwargs): """ Receives a structure and returns a dictionary with the ABINIT variables. """ if not structure.is_ordered: raise ValueError("""\ Received disordered structure with partial occupancies that cannot be converted into an Abinit input Please use OrderDisorderedStructureTransformation or EnumerateStructureTransformation to build an appropriate supercell from partial occupancies or alternatively use the Virtual Crystal Approximation.""") types_of_specie = structure.types_of_specie natom = structure.num_sites znucl_type = [specie.number for specie in types_of_specie] znucl_atoms = structure.atomic_numbers typat = np.zeros(natom, np.int) for atm_idx, site in enumerate(structure): typat[atm_idx] = types_of_specie.index(site.specie) + 1 rprim = ArrayWithUnit(structure.lattice.matrix, "ang").to("bohr") angdeg = structure.lattice.angles xred = np.reshape([site.frac_coords for site in structure], (-1, 3)) # Set small values to zero. This usually happens when the CIF file # does not give structure parameters with enough digits. rprim = np.where(np.abs(rprim) > 1e-8, rprim, 0.0) xred = np.where(np.abs(xred) > 1e-8, xred, 0.0) # Info on atoms. d = dict( natom=natom, ntypat=len(types_of_specie), typat=typat, znucl=znucl_type, xred=xred, ) # Add info on the lattice. # Should we use (rprim, acell) or (angdeg, acell) to specify the lattice? geomode = kwargs.pop("geomode", "rprim") if geomode == "automatic": geomode = "rprim" if structure.lattice.is_hexagonal: # or structure.lattice.is_rhombohedral geomode = "angdeg" angdeg = structure.lattice.angles # Here one could polish a bit the numerical values if they are not exact. # Note that in pmg the angles are 12, 20, 01 while in Abinit 12, 02, 01 # One should make sure that the orientation is preserved (see Curtarolo's settings) if geomode == "rprim": d.update( acell=3 * [1.0], rprim=rprim, ) elif geomode == "angdeg": d.update( acell=ArrayWithUnit(structure.lattice.abc, "ang").to("bohr"), angdeg=angdeg, ) else: raise ValueError("Wrong value for geomode: %s" % geomode) return d