class NthOrderElasticTensor(Tensor): """ An object representing an nth-order tensor expansion of the stress-strain constitutive equations """ GPa_to_eV_A3 = Unit("GPa").get_conversion_factor(Unit("eV ang^-3")) symbol = "C" def __new__(cls, input_array, check_rank=None, tol=1e-4): obj = super(NthOrderElasticTensor, cls).__new__(cls, input_array, check_rank=check_rank) if obj.rank % 2 != 0: raise ValueError("ElasticTensor must have even rank") if not obj.is_voigt_symmetric(tol): warnings.warn("Input elastic tensor does not satisfy " "standard voigt symmetries") return obj.view(cls) @property def order(self): """ Order of the elastic tensor """ return self.rank // 2 def calculate_stress(self, strain): """ Calculate's a given elastic tensor's contribution to the stress using Einstein summation Args: strain (3x3 array-like): matrix corresponding to strain """ strain = np.array(strain) if strain.shape == (6, ): strain = Strain.from_voigt(strain) assert strain.shape == (3, 3), "Strain must be 3x3 or voigt-notation" stress_matrix = self.einsum_sequence([strain]*(self.order - 1)) \ / factorial(self.order - 1) return Stress(stress_matrix) def energy_density(self, strain, convert_GPa_to_eV=True): """ Calculates the elastic energy density due to a strain """ e_density = np.sum(self.calculate_stress(strain) * strain) / self.order if convert_GPa_to_eV: e_density *= self.GPa_to_eV_A3 # Conversion factor for GPa to eV/A^3 return e_density @classmethod def from_diff_fit(cls, strains, stresses, eq_stress=None, order=2, tol=1e-10): return cls( diff_fit(strains, stresses, eq_stress, order, tol)[order - 2])
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 test_init(self): u1 = Unit((("m", 1), ("s", -1))) self.assertEqual(str(u1), "m s^-1") u2 = Unit("kg m ^ 2 s ^ -2") self.assertEqual(str(u2), "J") self.assertEqual(str(u1 * u2), "J m s^-1") self.assertEqual(str(u2 / u1), "J s m^-1") self.assertEqual(str(u1 / Unit("m")), "s^-1") self.assertEqual(str(u1 * Unit("s")), "m") acc = u1 / Unit("s") newton = Unit("kg") * acc self.assertEqual(str(newton * Unit("m")), "N m")
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 get_pymatgen_descriptor(composition, property_name): """ Get descriptor data for elements in a compound from pymatgen. Args: composition (str/Composition): Either pymatgen Composition object or string formula, eg: "NaCl", "Na+1Cl-1", "Fe2+3O3-2" or "Fe2 +3 O3 -2" Notes: - For 'ionic_radii' property, the Composition object must be made of oxidation state decorated Specie objects not the plain Element objects. eg. fe2o3 = Composition({Specie("Fe", 3): 2, Specie("O", -2): 3}) - For string formula, the oxidation state sign(+ or -) must be specified explicitly. eg. "Fe2+3O3-2" property_name (str): pymatgen element attribute name, as defined in the Element class at http://pymatgen.org/_modules/pymatgen/core/periodic_table.html Returns: (list) of values containing descriptor floats for each atom in the compound(sorted by the electronegativity of the contituent atoms) """ eldata = [] # what are these named tuples for? not used or returned! -KM eldata_tup_lst = [] eldata_tup = collections.namedtuple('eldata_tup', 'element propname propvalue propunit amt') oxidation_states = {} if isinstance(composition, Composition): # check whether the composition is composed of oxidation state decorates species (not just plain Elements) if hasattr(composition.elements[0], "oxi_state"): oxidation_states = dict([(str(sp.element), sp.oxi_state) for sp in composition.elements]) el_amt_dict = composition.get_el_amt_dict() # string else: comp, oxidation_states = get_composition_oxidation_state(composition) el_amt_dict = comp.get_el_amt_dict() symbols = sorted(el_amt_dict.keys(), key=lambda sym: get_el_sp(sym).X) for el_sym in symbols: element = Element(el_sym) property_value = None property_units = None try: p = getattr(element, property_name) except AttributeError: print("{} attribute missing".format(property_name)) raise if p is not None: if property_name in ['ionic_radii']: if oxidation_states: property_value = element.ionic_radii[oxidation_states[el_sym]] property_units = Unit("ang") else: raise ValueError("oxidation state not given for {}; It does not yield a unique " "number per Element".format(property_name)) else: property_value = float(p) # units are None for these pymatgen descriptors # todo: there seem to be a lot more unitless descriptors which are not listed here... -Alex D if property_name not in ['X', 'Z', 'group', 'row', 'number', 'mendeleev_no', 'ionic_radii']: property_units = p.unit # Make a named tuple out of all the available information eldata_tup_lst.append(eldata_tup(element=el_sym, propname=property_name, propvalue=property_value, propunit=property_units, amt=el_amt_dict[el_sym])) # Add descriptor values, one for each atom in the compound for i in range(int(el_amt_dict[el_sym])): eldata.append(property_value) return eldata