def __init__(self, symbol): self.symbol = "%s" % symbol d = _pt_data[symbol] # Store key variables for quick access self.Z = d["Atomic no"] self.X = d.get("X", 0) for a in [ "mendeleev_no", "electrical_resistivity", "velocity_of_sound", "reflectivity", "refractive_index", "poissons_ratio", "molar_volume", "electronic_structure", "thermal_conductivity", "boiling_point", "melting_point", "critical_temperature", "superconduction_temperature", "liquid_range", "bulk_modulus", "youngs_modulus", "brinell_hardness", "rigidity_modulus", "mineral_hardness", "vickers_hardness", "density_of_solid", "atomic_radius_calculated", "van_der_waals_radius", "coefficient_of_linear_thermal_expansion" ]: kstr = a.capitalize().replace("_", " ") val = d.get(kstr, None) if str(val).startswith("no data"): val = None else: try: val = float(val) except ValueError: toks_nobracket = re.sub(r'\(.*\)', "", val) toks = toks_nobracket.replace("about", "").strip().split(" ", 1) if len(toks) == 2: try: if "10<sup>" in toks[1]: base_power = re.findall(r'([+-]?\d+)', toks[1]) factor = "e" + base_power[1] toks[0] += factor if a == "electrical_resistivity": unit = "ohm m" elif a == "coefficient_of_linear_thermal_expansion": unit = "K^-1" else: unit = toks[1] val = FloatWithUnit(toks[0], unit) else: unit = toks[1].replace("<sup>", "^").replace( "</sup>", "").replace("Ω", "ohm") units = Unit(unit) if set(units.keys()).issubset( SUPPORTED_UNIT_NAMES): val = FloatWithUnit(toks[0], unit) except ValueError as ex: # Ignore error. val will just remain a string. pass setattr(self, a, val) if str(d.get("Atomic radius", "no data")).startswith("no data"): self.atomic_radius = None else: self.atomic_radius = Length(d["Atomic radius"], "ang") self.atomic_mass = Mass(d["Atomic mass"], "amu") self._data = d
def __getattr__(self, item): if item in ["mendeleev_no", "electrical_resistivity", "velocity_of_sound", "reflectivity", "refractive_index", "poissons_ratio", "molar_volume", "electronic_structure", "thermal_conductivity", "boiling_point", "melting_point", "critical_temperature", "superconduction_temperature", "liquid_range", "bulk_modulus", "youngs_modulus", "brinell_hardness", "rigidity_modulus", "mineral_hardness", "vickers_hardness", "density_of_solid", "atomic_radius_calculated", "van_der_waals_radius", "atomic_orbitals", "coefficient_of_linear_thermal_expansion"]: kstr = item.capitalize().replace("_", " ") val = self._data.get(kstr, None) if str(val).startswith("no data"): val = None elif type(val) == dict: pass else: try: val = float(val) except ValueError: nobracket = re.sub(r'\(.*\)', "", val) toks = nobracket.replace("about", "").strip().split(" ", 1) if len(toks) == 2: try: if "10<sup>" in toks[1]: base_power = re.findall(r'([+-]?\d+)', toks[1]) factor = "e" + base_power[1] if toks[0] in [">", "high"]: toks[0] = "1" # return the border value toks[0] += factor if item == "electrical_resistivity": unit = "ohm m" elif ( item == "coefficient_of_linear_thermal_expansion" ): unit = "K^-1" else: unit = toks[1] val = FloatWithUnit(toks[0], unit) else: unit = toks[1].replace("<sup>", "^").replace( "</sup>", "").replace("Ω", "ohm") units = Unit(unit) if set(units.keys()).issubset( SUPPORTED_UNIT_NAMES): val = FloatWithUnit(toks[0], unit) except ValueError as ex: # Ignore error. val will just remain a string. pass return val raise AttributeError
def average_anionic_radius(self) -> float: """ Average anionic radius for element (with units). The average is taken over all negative oxidation states of the element for which data is present. """ if "Ionic radii" in self._data: radii = [v for k, v in self._data["Ionic radii"].items() if int(k) < 0] if radii: return FloatWithUnit(sum(radii) / len(radii), "ang") return FloatWithUnit(0.0, "ang")
def __new__(cls, *args): """Extends the base class adding type conversion of arguments.""" new_args = len(args) * [None] for i, arg in enumerate(args[:-1]): converter = float if i == 0: converter = str new_args[i] = converter(arg) v0 = FloatWithUnit(new_args[1], "ang^3") b0 = FloatWithUnit(new_args[2], "eV ang^-3") return super(cls, DeltaFactorEntry).__new__(cls, symbol=new_args[0], v0=v0, b0=b0, b1=new_args[3], xc=args[-1])
def test_compound_operations(self): g = 10 * Length(1, "m") / (Time(1, "s") ** 2) e = Mass(1, "kg") * g * Length(1, "m") self.assertEqual(str(e), "10.0 N m") form_e = FloatWithUnit(10, unit="kJ mol^-1") self.assertEqual(str(form_e.to("eV atom^-1")), "0.103642691905 eV atom^-1") self.assertRaises(UnitError, form_e.to, "m s^-1") a = FloatWithUnit(1.0, "Ha^3") self.assertEqual(str(a.to("J^3")), "8.28672661615e-53 J^3") a = FloatWithUnit(1.0, "Ha bohr^-2") self.assertEqual(str(a.to("J m^-2")), "1556.89291457 J m^-2")
def nmr_quadrupole_moment(self): """ Get a dictionary the nuclear electric quadrupole moment in units of e*millibarns for various isotopes """ return {k: FloatWithUnit(v, "mbarn") for k, v in self.data.get("NMR Quadrupole Moment", {}).items()}
def test_compound_operations(self): g = 10 * Length(1, "m") / (Time(1, "s") ** 2) e = Mass(1, "kg") * g * Length(1, "m") self.assertEqual(str(e), "10.0 N m") form_e = FloatWithUnit(10, unit="kJ mol^-1").to("eV atom^-1") self.assertAlmostEqual(float(form_e), 0.103642691905) self.assertEqual(str(form_e.unit), "eV atom^-1") self.assertRaises(UnitError, form_e.to, "m s^-1") a = FloatWithUnit(1.0, "Ha^3") b = a.to("J^3") self.assertAlmostEqual(b, 8.28672661615e-53) self.assertEqual(str(b.unit), "J^3") a = FloatWithUnit(1.0, "Ha bohr^-2") b = a.to("J m^-2") self.assertAlmostEqual(b, 1556.893078472351) self.assertEqual(str(b.unit), "J m^-2")
def b0_GPa(self): """ Returns the bulk modulus in GPa. Note: This assumes that the energy and volumes are in eV and Ang^3 respectively """ return FloatWithUnit(self.b0, "eV ang^-3").to("GPa")
def coupling_constant(self, specie): """ Computes the couplling constant C_q as defined in: Wasylishen R E, Ashbrook S E, Wimperis S. NMR of quadrupolar nuclei in solid materials[M]. John Wiley & Sons, 2012. (Chapter 3.2) C_q for a specific atom type for this electric field tensor: C_q=e*Q*V_zz/h h: planck's constant Q: nuclear electric quadrupole moment in mb (millibarn e: elementary proton charge Args: specie: flexible input to specify the species at this site. Can take a isotope or element string, Specie object, or Site object Return: the coupling constant as a FloatWithUnit in MHz """ planks_constant = FloatWithUnit(6.62607004E-34, "m^2 kg s^-1") Vzz = FloatWithUnit(self.V_zz, "V ang^-2") e = FloatWithUnit(-1.60217662E-19, "C") # Convert from string to Specie object if isinstance(specie, str): # isotope was provided in string format if len(specie.split("-")) > 1: isotope = str(specie) specie = Specie(specie.split("-")[0]) Q = specie.get_nmr_quadrupole_moment(isotope) else: specie = Specie(specie) Q = specie.get_nmr_quadrupole_moment() elif isinstance(specie, Site): specie = specie.specie Q = specie.get_nmr_quadrupole_moment() elif isinstance(specie, Specie): Q = specie.get_nmr_quadrupole_moment() else: raise ValueError( "Invalid speciie provided for quadrupolar coupling constant calcuations" ) return (e * Q * Vzz / planks_constant).to("MHz")
def ionic_radii(self) -> Dict[int, float]: """ All ionic radii of the element as a dict of {oxidation state: ionic radii}. Radii are given in ang. """ if "Ionic radii" in self._data: return {int(k): FloatWithUnit(v, "ang") for k, v in self._data["Ionic radii"].items()} return {}
def average_ionic_radius(self): """ Average ionic radius for element (with units). The average is taken over all oxidation states of the element for which data is present. """ if "Ionic radii" in self._data: radii = self._data["Ionic radii"] radius = sum(radii.values()) / len(radii) else: radius = 0.0 return FloatWithUnit(radius, "ang")
def gruneisen_parameter(self, temperature, volume): """ Slater-gamma formulation(the default): gruneisen paramter = - d log(theta)/ d log(V) = - ( 1/6 + 0.5 d log(B)/ d log(V) ) = - (1/6 + 0.5 V/B dB/dV), where dB/dV = d^2E/dV^2 + V * d^3E/dV^3 Mie-gruneisen formulation: Eq(31) in doi.org/10.1016/j.comphy.2003.12.001 Eq(7) in Blanco et. al. Joumal of Molecular Structure (Theochem) 368 (1996) 245-255 Also se J.P. Poirier, Introduction to the Physics of the Earth’s Interior, 2nd ed. (Cambridge University Press, Cambridge, 2000) Eq(3.53) Args: temperature (float): temperature in K volume (float): in Ang^3 Returns: float: unitless """ if isinstance(self.eos, PolynomialEOS): p = np.poly1d(self.eos.eos_params) # pylint: disable=E1101 # first derivative of energy at 0K wrt volume evaluated at the # given volume, in eV/Ang^3 dEdV = np.polyder(p, 1)(volume) # second derivative of energy at 0K wrt volume evaluated at the # given volume, in eV/Ang^6 d2EdV2 = np.polyder(p, 2)(volume) # third derivative of energy at 0K wrt volume evaluated at the # given volume, in eV/Ang^9 d3EdV3 = np.polyder(p, 3)(volume) else: func = self.ev_eos_fit.func dEdV = derivative(func, volume, dx=1e-3) d2EdV2 = derivative(func, volume, dx=1e-3, n=2, order=5) d3EdV3 = derivative(func, volume, dx=1e-3, n=3, order=7) # Mie-gruneisen formulation if self.use_mie_gruneisen: p0 = dEdV return ( self.gpa_to_ev_ang * volume * (self.pressure + p0 / self.gpa_to_ev_ang) / self.vibrational_internal_energy(temperature, volume) ) # Slater-gamma formulation # first derivative of bulk modulus wrt volume, eV/Ang^6 dBdV = d2EdV2 + d3EdV3 * volume return -(1.0 / 6.0 + 0.5 * volume * dBdV / FloatWithUnit(self.ev_eos_fit.b0_GPa, "GPa").to("eV ang^-3"))
def __new__(cls, **kwargs): """Extends the base class adding type conversion of arguments.""" for k, v in kwargs.items(): if k in ["symbol", "struct_type"]: continue if v == "-": # Set missing entries to None v = None else: # Values in GBRV tables are in Angstrom. v = FloatWithUnit(v, "ang") kwargs[k] = v return super(cls, GbrvEntry).__new__(cls, **kwargs)
def test_compound_operations(self): g = 10 * Length(1, "m") / (Time(1, "s")**2) e = Mass(1, "kg") * g * Length(1, "m") self.assertEqual(str(e), "10.0 N m") form_e = FloatWithUnit(10, unit="kJ mol^-1").to("eV atom^-1") self.assertAlmostEqual(float(form_e), 0.103642691905) self.assertEqual(str(form_e.unit), "eV atom^-1") self.assertRaises(UnitError, form_e.to, "m s^-1") a = FloatWithUnit(1.0, "Ha^3") b = a.to("J^3") self.assertAlmostEqual(b, 8.28672661615e-53) self.assertEqual(str(b.unit), "J^3") a = FloatWithUnit(1.0, "Ha bohr^-2") b = a.to("J m^-2") self.assertAlmostEqual(b, 1556.893078472351) self.assertEqual(str(b.unit), "J m^-2")
def read_data_from_filepath(filepath, xc): """ Reads (v0, b0, b1) from file filename. b0 is in GPa. xc is the XcFunc associated to these data. Returns a dict of `DeltaFactorEntry` objects indexed by element symbol. """ data = collections.OrderedDict() with open(filepath, "r") as fh: for line in fh: line = line.strip() if line.startswith("#") or not line: continue tokens = line.split() symbol = tokens[0] # Conversion GPa --> eV / A**3 and pass xc info to the Entry. tokens[2] = FloatWithUnit(tokens[2], "GPa").to("eV ang^-3") tokens.append(xc) data[symbol] = DeltaFactorEntry(*tokens) return data
def test_unitized(self): @unitized("eV") def f(): return [1, 2, 3] self.assertEqual(str(f()[0]), "1.0 eV") self.assertIsInstance(f(), list) @unitized("eV") def g(): return 2, 3, 4 self.assertEqual(str(g()[0]), "2.0 eV") self.assertIsInstance(g(), tuple) @unitized("pm") def h(): d = collections.OrderedDict() for i in range(3): d[i] = i * 20 return d self.assertEqual(str(h()[1]), "20.0 pm") self.assertIsInstance(h(), collections.OrderedDict) @unitized("kg") def i(): return FloatWithUnit(5, "g") self.assertEqual(i(), FloatWithUnit(0.005, "kg")) @unitized("kg") def j(): return ArrayWithUnit([5, 10], "g") j_out = j() self.assertEqual(j_out.unit, Unit("kg")) self.assertEqual(j_out[0], 0.005) self.assertEqual(j_out[1], 0.01)
def b0_GPa(self): return FloatWithUnit(self.b0, "eV ang^-3").to("GPa")
def df_compute(v0w, b0w, b1w, v0f, b0f, b1f, b0_GPa=False, v=3, useasymm=False): """ Compute the deltafactor. Based on the code of the offical calcDelta.py script. Args: v0w, b0w, b1w: Volume, bulk-modulus and pressure derivative of b0w (reference values). v0f, b0f, b1f: Volume, bulk-modulus and pressure derivative of b0f (computed values). v: version of delta factor, current version is 3, 1 for symmetrical old version .. note:: v0 is A**3/natom, by default b0 is in eV/A**3, GPa units are used if b0_GPa is True. """ #print(v0w, b0w, b1w, v0f, b0f, b1f) if v == 1: # delta factor form version 1 if b0_GPa: # Conversion GPa --> eV/A**3 b0w = FloatWithUnit(b0w, "GPa").to("eV ang^-3") b0f = FloatWithUnit(b0f, "GPa").to("eV ang^-3") Vi = 0.94 * v0w Vf = 1.06 * v0w a3f = 9. * v0f**3. * b0f / 16. * (b1f - 4.) a2f = 9. * v0f**(7./3.) * b0f / 16. * (14. - 3. * b1f) a1f = 9. * v0f**(5./3.) * b0f / 16. * (3. * b1f - 16.) a0f = 9. * v0f * b0f / 16. * (6. - b1f) a3w = 9. * v0w**3. * b0w / 16. * (b1w - 4.) a2w = 9. * v0w**(7./3.) * b0w / 16. * (14. - 3. * b1w) a1w = 9. * v0w**(5./3.) * b0w / 16. * (3. * b1w - 16.) a0w = 9. * v0w * b0w / 16. * (6. - b1w) x = [0, 0, 0, 0, 0, 0, 0] x[0] = (a0f - a0w)**2 x[1] = 6. * (a1f - a1w) * (a0f - a0w) x[2] = -3. * (2. * (a2f - a2w) * (a0f - a0w) + (a1f - a1w)**2.) x[3] = -2. * (a3f - a3w) * (a0f - a0w) - 2. * (a2f - a2w) * (a1f - a1w) x[4] = -3./5. * (2. * (a3f - a3w) * (a1f - a1w) + (a2f - a2w)**2.) x[5] = -6./7. * (a3f - a3w) * (a2f - a2w) x[6] = -1./3. * (a3f - a3w)**2. Fi = np.zeros_like(Vi) Ff = np.zeros_like(Vf) for n in range(7): Fi = Fi + x[n] * Vi**(-(2.*n-3.)/3.) Ff = Ff + x[n] * Vf**(-(2.*n-3.)/3.) return 1000. * np.sqrt((Ff - Fi) / (0.12 * v0w)) elif v == 3: # version 3 if b0_GPa: # Conversion GPa --> eV/A**3 b0w = FloatWithUnit(b0w, "GPa").to("eV ang^-3") b0f = FloatWithUnit(b0f, "GPa").to("eV ang^-3") if useasymm: Vi = 0.94 * v0w Vf = 1.06 * v0w else: Vi = 0.94 * (v0w + v0f) / 2. Vf = 1.06 * (v0w + v0f) / 2. a3f = 9. * v0f**3. * b0f / 16. * (b1f - 4.) a2f = 9. * v0f**(7./3.) * b0f / 16. * (14. - 3. * b1f) a1f = 9. * v0f**(5./3.) * b0f / 16. * (3. * b1f - 16.) a0f = 9. * v0f * b0f / 16. * (6. - b1f) a3w = 9. * v0w**3. * b0w / 16. * (b1w - 4.) a2w = 9. * v0w**(7./3.) * b0w / 16. * (14. - 3. * b1w) a1w = 9. * v0w**(5./3.) * b0w / 16. * (3. * b1w - 16.) a0w = 9. * v0w * b0w / 16. * (6. - b1w) x = [0, 0, 0, 0, 0, 0, 0] x[0] = (a0f - a0w)**2 x[1] = 6. * (a1f - a1w) * (a0f - a0w) x[2] = -3. * (2. * (a2f - a2w) * (a0f - a0w) + (a1f - a1w)**2.) x[3] = -2. * (a3f - a3w) * (a0f - a0w) - 2. * (a2f - a2w) * (a1f - a1w) x[4] = -3./5. * (2. * (a3f - a3w) * (a1f - a1w) + (a2f - a2w)**2.) x[5] = -6./7. * (a3f - a3w) * (a2f - a2w) x[6] = -1./3. * (a3f - a3w)**2. y = [0, 0, 0, 0, 0, 0, 0] y[0] = (a0f + a0w)**2 / 4. y[1] = 3. * (a1f + a1w) * (a0f + a0w) / 2. y[2] = -3. * (2. * (a2f + a2w) * (a0f + a0w) + (a1f + a1w)**2.) / 4. y[3] = -(a3f + a3w) * (a0f + a0w) / 2. - (a2f + a2w) * (a1f + a1w) / 2. y[4] = -3./20. * (2. * (a3f + a3w) * (a1f + a1w) + (a2f + a2w)**2.) y[5] = -3./14. * (a3f + a3w) * (a2f + a2w) y[6] = -1./12. * (a3f + a3w)**2. Fi = np.zeros_like(Vi) Ff = np.zeros_like(Vf) Gi = np.zeros_like(Vi) Gf = np.zeros_like(Vf) for n in range(7): Fi += x[n] * Vi**(-(2.*n-3.)/3.) Ff += x[n] * Vf**(-(2.*n-3.)/3.) Gi += y[n] * Vi**(-(2.*n-3.)/3.) Gf += y[n] * Vf**(-(2.*n-3.)/3.) Delta = 1000. * np.sqrt((Ff - Fi) / (Vf - Vi)) #Deltarel = 100. * np.sqrt((Ff - Fi) / (Gf - Gi)) #if useasymm: # Delta1 = 1000. * np.sqrt((Ff - Fi) / (Vf - Vi)) \ # / v0w / b0w * vref * bref #else: # Delta1 = 1000. * np.sqrt((Ff - Fi) / (Vf - Vi)) \ # / (v0w + v0f) / (b0w + b0f) * 4. * vref * bref return Delta else: raise ValueError("Wrong version %s" % v)
def _parse_job(self, output): energy_patt = re.compile(r'Total \w+ energy\s+=\s+([.\-\d]+)') energy_gas_patt = re.compile(r'gas phase energy\s+=\s+([.\-\d]+)') energy_sol_patt = re.compile(r'sol phase energy\s+=\s+([.\-\d]+)') coord_patt = re.compile(r'\d+\s+(\w+)\s+[.\-\d]+\s+([.\-\d]+)\s+' r'([.\-\d]+)\s+([.\-\d]+)') lat_vector_patt = re.compile(r'a[123]=<\s+([.\-\d]+)\s+' r'([.\-\d]+)\s+([.\-\d]+)\s+>') corrections_patt = re.compile(r'([\w\-]+ correction to \w+)\s+=' r'\s+([.\-\d]+)') preamble_patt = re.compile(r'(No. of atoms|No. of electrons' r'|SCF calculation type|Charge|Spin ' r'multiplicity)\s*:\s*(\S+)') force_patt = re.compile(r'\s+(\d+)\s+(\w+)' + 6 * r'\s+([0-9\.\-]+)') time_patt = re.compile( r'\s+ Task \s+ times \s+ cpu: \s+ ([.\d]+)s .+ ', re.VERBOSE) error_defs = { "calculations not reaching convergence": "Bad convergence", "Calculation failed to converge": "Bad convergence", "geom_binvr: #indep variables incorrect": "autoz error", "dft optimize failed": "Geometry optimization failed"} fort2py = lambda x: x.replace("D", "e") isfloatstring = lambda s: s.find(".") == -1 parse_hess = False parse_proj_hess = False hessian = None projected_hessian = None parse_force = False all_forces = [] forces = [] data = {} energies = [] frequencies = None normal_frequencies = None corrections = {} molecules = [] structures = [] species = [] coords = [] lattice = [] errors = [] basis_set = {} bset_header = [] parse_geom = False parse_freq = False parse_bset = False parse_projected_freq = False job_type = "" parse_time = False time = 0 for l in output.split("\n"): for e, v in error_defs.items(): if l.find(e) != -1: errors.append(v) if parse_time: m = time_patt.search(l) if m: time = m.group(1) parse_time = False if parse_geom: if l.strip() == "Atomic Mass": if lattice: structures.append(Structure(lattice, species, coords, coords_are_cartesian=True)) else: molecules.append(Molecule(species, coords)) species = [] coords = [] lattice = [] parse_geom = False else: m = coord_patt.search(l) if m: species.append(m.group(1).capitalize()) coords.append([float(m.group(2)), float(m.group(3)), float(m.group(4))]) m = lat_vector_patt.search(l) if m: lattice.append([float(m.group(1)), float(m.group(2)), float(m.group(3))]) if parse_force: m = force_patt.search(l) if m: forces.extend(map(float, m.groups()[5:])) elif len(forces) > 0: all_forces.append(forces) forces = [] parse_force = False elif parse_freq: if len(l.strip()) == 0: if len(normal_frequencies[-1][1]) == 0: continue else: parse_freq = False else: vibs = [float(vib) for vib in l.strip().split()[1:]] num_vibs = len(vibs) for mode, dis in zip(normal_frequencies[-num_vibs:], vibs): mode[1].append(dis) elif parse_projected_freq: if len(l.strip()) == 0: if len(frequencies[-1][1]) == 0: continue else: parse_projected_freq = False else: vibs = [float(vib) for vib in l.strip().split()[1:]] num_vibs = len(vibs) for mode, dis in zip( frequencies[-num_vibs:], vibs): mode[1].append(dis) elif parse_bset: if l.strip() == "": parse_bset = False else: toks = l.split() if toks[0] != "Tag" and not re.match(r"-+", toks[0]): basis_set[toks[0]] = dict(zip(bset_header[1:], toks[1:])) elif toks[0] == "Tag": bset_header = toks bset_header.pop(4) bset_header = [h.lower() for h in bset_header] elif parse_hess: if l.strip() == "": continue if len(hessian) > 0 and l.find("----------") != -1: parse_hess = False continue toks = l.strip().split() if len(toks) > 1: try: row = int(toks[0]) except Exception: continue if isfloatstring(toks[1]): continue vals = [float(fort2py(x)) for x in toks[1:]] if len(hessian) < row: hessian.append(vals) else: hessian[row - 1].extend(vals) elif parse_proj_hess: if l.strip() == "": continue nat3 = len(hessian) toks = l.strip().split() if len(toks) > 1: try: row = int(toks[0]) except Exception: continue if isfloatstring(toks[1]): continue vals = [float(fort2py(x)) for x in toks[1:]] if len(projected_hessian) < row: projected_hessian.append(vals) else: projected_hessian[row - 1].extend(vals) if len(projected_hessian[-1]) == nat3: parse_proj_hess = False else: m = energy_patt.search(l) if m: energies.append(Energy(m.group(1), "Ha").to("eV")) parse_time = True continue m = energy_gas_patt.search(l) if m: cosmo_scf_energy = energies[-1] energies[-1] = dict() energies[-1].update({"cosmo scf": cosmo_scf_energy}) energies[-1].update({"gas phase": Energy(m.group(1), "Ha").to("eV")}) m = energy_sol_patt.search(l) if m: energies[-1].update( {"sol phase": Energy(m.group(1), "Ha").to("eV")}) m = preamble_patt.search(l) if m: try: val = int(m.group(2)) except ValueError: val = m.group(2) k = m.group(1).replace("No. of ", "n").replace(" ", "_") data[k.lower()] = val elif l.find("Geometry \"geometry\"") != -1: parse_geom = True elif l.find("Summary of \"ao basis\"") != -1: parse_bset = True elif l.find("P.Frequency") != -1: parse_projected_freq = True if frequencies is None: frequencies = [] toks = l.strip().split()[1:] frequencies.extend([(float(freq), []) for freq in toks]) elif l.find("Frequency") != -1: toks = l.strip().split() if len(toks) > 1 and toks[0] == "Frequency": parse_freq = True if normal_frequencies is None: normal_frequencies = [] normal_frequencies.extend([(float(freq), []) for freq in l.strip().split()[1:]]) elif l.find("MASS-WEIGHTED NUCLEAR HESSIAN") != -1: parse_hess = True if not hessian: hessian = [] elif l.find("MASS-WEIGHTED PROJECTED HESSIAN") != -1: parse_proj_hess = True if not projected_hessian: projected_hessian = [] elif l.find("atom coordinates gradient") != -1: parse_force = True elif job_type == "" and l.strip().startswith("NWChem"): job_type = l.strip() if job_type == "NWChem DFT Module" and \ "COSMO solvation results" in output: job_type += " COSMO" else: m = corrections_patt.search(l) if m: corrections[m.group(1)] = FloatWithUnit( m.group(2), "kJ mol^-1").to("eV atom^-1") if frequencies: for freq, mode in frequencies: mode[:] = zip(*[iter(mode)]*3) if normal_frequencies: for freq, mode in normal_frequencies: mode[:] = zip(*[iter(mode)]*3) if hessian: n = len(hessian) for i in range(n): for j in range(i + 1, n): hessian[i].append(hessian[j][i]) if projected_hessian: n = len(projected_hessian) for i in range(n): for j in range(i + 1, n): projected_hessian[i].append(projected_hessian[j][i]) data.update({"job_type": job_type, "energies": energies, "corrections": corrections, "molecules": molecules, "structures": structures, "basis_set": basis_set, "errors": errors, "has_error": len(errors) > 0, "frequencies": frequencies, "normal_frequencies": normal_frequencies, "hessian": hessian, "projected_hessian": projected_hessian, "forces": all_forces, "task_time": time}) return data
def test_as_base_units(self): x = FloatWithUnit(5, "MPa") self.assertEqual(FloatWithUnit(5000000, "Pa"), x.as_base_units)
def find_theo_redenth(compstr): """ Finds theoretical redox enthalpies from the Materials Project from perovskite to brownmillerite based partially on https://github.com/materialsproject/pymatgen/blob/b3e972e293885c5b3c69fb3e9aa55287869d4d84/ examples/Calculating%20Reaction%20Energies%20with%20the%20Materials%20API.ipynb :param compstr: composition as a string :return: red_enth: redox enthalpy in kJ/mol O """ compstr_perovskite = compstr.split("O")[0] + "O3" comp_spl = split_comp(compstr) chem_sys = "" for i in range(len(comp_spl)): if comp_spl[i] is not None: chem_sys = chem_sys + comp_spl[i][0] + "-" chem_sys = chem_sys + "O" chem_sys = chem_sys.split("-") all_entries = mpr.get_entries_in_chemsys(chem_sys) # This method simply gets the lowest energy entry for all entries with the same composition. def get_most_stable_entry(formula): relevant_entries = [entry for entry in all_entries if entry.composition.reduced_formula == Composition(formula).reduced_formula] relevant_entries = sorted(relevant_entries, key=lambda e: e.energy_per_atom) return relevant_entries[0] formula_spl = [''.join(g) for _, g in groupby(str(compstr), str.isalpha)] perov_formula = [] for k in range(len(formula_spl)): try: perov_formula += str(int(float(formula_spl[k]) * 8)) except ValueError: perov_formula += str(formula_spl[k]) perov_formula = "".join(perov_formula) perov_formula = str(perov_formula).split("O")[0] + "O24" perovskite = get_most_stable_entry(perov_formula) brownm_formula = [] for k in range(len(formula_spl)): try: brownm_formula += str(int(float(formula_spl[k]) * 32)) except ValueError: brownm_formula += str(formula_spl[k]) brownm_formula = "".join(brownm_formula) brownm_formula = str(brownm_formula).split("O")[0] + "O80" brownmillerite = get_most_stable_entry(brownm_formula) # for oxygen: do not use the most stable phase O8 but the most stable O2 phase def get_oxygen(): relevant_entries = [entry for entry in all_entries if entry.composition == Composition("O2")] relevant_entries = sorted(relevant_entries, key=lambda e: e.energy_per_atom) return relevant_entries[0] oxygen = get_oxygen() reaction = ComputedReaction([perovskite], [brownmillerite, oxygen]) energy = FloatWithUnit(reaction.calculated_reaction_energy, "eV atom^-1") # figure out the stoichiometry of O2 in the reaction equation in order to normalize the energies per mol of O try: o_stoich = float(str(str(reaction.as_dict).split(" O2")[0]).split()[-1]) except ValueError: o_stoich = 1 # energy in J/mol per mol of O2 ener = (float(energy.to("kJ mol^-1")) * 1000) / o_stoich # per mol of O ener = ener / 2 return ener
def i(): return FloatWithUnit(5, "g")
def metallic_radius(self): """ Metallic radius of the element. Radius is given in ang. """ return FloatWithUnit(self._data["Metallic radius"], "ang")
def _parse_job(self, output): energy_patt = re.compile("Total \w+ energy\s+=\s+([\.\-\d]+)") #In cosmo solvation results; gas phase energy = -152.5044774212 energy_gas_patt = re.compile("gas phase energy\s+=\s+([\.\-\d]+)") #In cosmo solvation results; sol phase energy = -152.5044774212 energy_sol_patt = re.compile("sol phase energy\s+=\s+([\.\-\d]+)") coord_patt = re.compile("\d+\s+(\w+)\s+[\.\-\d]+\s+([\.\-\d]+)\s+" "([\.\-\d]+)\s+([\.\-\d]+)") corrections_patt = re.compile("([\w\-]+ correction to \w+)\s+=" "\s+([\.\-\d]+)") preamble_patt = re.compile("(No. of atoms|No. of electrons" "|SCF calculation type|Charge|Spin " "multiplicity)\s*:\s*(\S+)") error_defs = { "calculations not reaching convergence": "Bad convergence", "Calculation failed to converge": "Bad convergence", "geom_binvr: #indep variables incorrect": "autoz error", "dft optimize failed": "Geometry optimization failed" } data = {} energies = [] frequencies = None corrections = {} molecules = [] species = [] coords = [] errors = [] basis_set = {} parse_geom = False parse_freq = False parse_bset = False job_type = "" for l in output.split("\n"): for e, v in error_defs.items(): if l.find(e) != -1: errors.append(v) if parse_geom: if l.strip() == "Atomic Mass": molecules.append(Molecule(species, coords)) species = [] coords = [] parse_geom = False else: m = coord_patt.search(l) if m: species.append(m.group(1).capitalize()) coords.append([ float(m.group(2)), float(m.group(3)), float(m.group(4)) ]) if parse_freq: if len(l.strip()) == 0: if len(frequencies[-1][1]) == 0: continue else: parse_freq = False else: vibs = [float(vib) for vib in l.strip().split()[1:]] num_vibs = len(vibs) for mode, dis in zip(frequencies[-num_vibs:], vibs): mode[1].append(dis) elif parse_bset: if l.strip() == "": parse_bset = False else: toks = l.split() if toks[0] != "Tag" and not re.match("\-+", toks[0]): basis_set[toks[0]] = dict( zip(bset_header[1:], toks[1:])) elif toks[0] == "Tag": bset_header = toks bset_header.pop(4) bset_header = [h.lower() for h in bset_header] else: m = energy_patt.search(l) if m: energies.append(Energy(m.group(1), "Ha").to("eV")) continue m = energy_gas_patt.search(l) if m: cosmo_scf_energy = energies[-1] energies[-1] = dict() energies[-1].update({"cosmo scf": cosmo_scf_energy}) energies[-1].update( {"gas phase": Energy(m.group(1), "Ha").to("eV")}) m = energy_sol_patt.search(l) if m: energies[-1].update( {"sol phase": Energy(m.group(1), "Ha").to("eV")}) m = preamble_patt.search(l) if m: try: val = int(m.group(2)) except ValueError: val = m.group(2) k = m.group(1).replace("No. of ", "n").replace(" ", "_") data[k.lower()] = val elif l.find("Geometry \"geometry\"") != -1: parse_geom = True elif l.find("Summary of \"ao basis\"") != -1: parse_bset = True elif l.find("P.Frequency") != -1: parse_freq = True if not frequencies: frequencies = [] frequencies.extend([(float(freq), []) for freq in l.strip().split()[1:]]) elif job_type == "" and l.strip().startswith("NWChem"): job_type = l.strip() if job_type == "NWChem DFT Module" and \ "COSMO solvation results" in output: job_type += " COSMO" else: m = corrections_patt.search(l) if m: corrections[m.group(1)] = FloatWithUnit( m.group(2), "kJ mol^-1").to("eV atom^-1") if frequencies: for freq, mode in frequencies: mode[:] = zip(*[iter(mode)] * 3) data.update({ "job_type": job_type, "energies": energies, "corrections": corrections, "molecules": molecules, "basis_set": basis_set, "errors": errors, "has_error": len(errors) > 0, "frequencies": frequencies }) return data
def get_most_stable_entry(formula): relevant_entries = [ entry for entry in all_entries if entry.composition.reduced_formula == Composition(formula).reduced_formula ] relevant_entries = sorted(relevant_entries, key=lambda e: e.energy_per_atom) return relevant_entries[0] CaO = get_most_stable_entry("CaO") CO2 = get_most_stable_entry("CO2") CaCO3 = get_most_stable_entry("CaCO3") reaction = ComputedReaction([CaO, CO2], [CaCO3]) #得到化学反应 energy = FloatWithUnit(reaction.calculated_reaction_energy, "eV atom^-1") #计算化学反应的能量,并且转换单位 print("Caculated") print(reaction) print("Reaction energy = {}".format(energy.to("kJ mol^-1"))) # The following portions demonstrate how to get the experimental values as well. exp_CaO = a.get_exp_entry("CaO") #获得ExpEntry对象,可用于实验数据分析 exp_CaCO3 = a.get_exp_entry("CaCO3") #Unfortunately, the Materials Project database does not have gas phase experimental entries. This is the value from NIST. We manually create the entry. #这是一个气相实验,而且数据库没有关于这个的数据条目,所以我们要自己创建 #NIST是化学信息的标准参考数据库 exp_CO2 = ComputedEntry("CO2", -393.51) #Exp entries should be in kJ/mol. exp_reaction = ComputedReaction([exp_CaO, exp_CO2], [exp_CaCO3])
def compute(self, input_data): return FloatWithUnit(input_data, "g")
stable_result = get_most_stable_entry(result) except IndexError: error["error"] = "No structure with that formula" json_dir = output_path + "/" + "output.json" with open(json_dir, "w") as outfile: json.dump(error, outfile) print(error) exit() # print stable_result.name, phase.get_form_energy(stable_result) (compo, factor) = stable_result.composition.get_reduced_composition_and_factor() form_energy[stable_result.name] = phase.get_form_energy(stable_result) / factor reaction = ComputedReaction(stable_formula, [stable_result]) energy = FloatWithUnit(reaction.calculated_reaction_energy, "eV atom^-1") json_result = dict() json_result["reaction"] = str(reaction) value1 = dict() value1["value"] = energy.to("kJ mol^-1") value1["unit"] = "kJ mol^-1" value2 = dict() value2["value"] = energy.to("eV atom^-1") value2["unit"] = "eV" json_result["value1"] = value1 json_result["value2"] = value2 json_result["formationenergy"] = form_energy
def b0_GPa(self): """ Returns the bulk modulus in GPa """ return FloatWithUnit(self.params[1], "eV ang^-3").to("GPa")