def test_get_compliance_expansion(self): ce_exp = self.exp_cu.get_compliance_expansion() et_comp = ElasticTensorExpansion(ce_exp) strain_orig = Strain.from_voigt([0.01, 0, 0, 0, 0, 0]) stress = self.exp_cu.calculate_stress(strain_orig) strain_revert = et_comp.calculate_stress(stress) self.assertArrayAlmostEqual(strain_orig, strain_revert, decimal=4)
def test_get_compliance_expansion(self): ce_exp = self.exp_cu.get_compliance_expansion() et_comp = ElasticTensorExpansion(ce_exp) strain_orig = Strain.from_voigt([0.01, 0, 0, 0, 0, 0]) stress = self.exp_cu.calculate_stress(strain_orig) strain_revert = et_comp.calculate_stress(stress) self.assertArrayAlmostEqual(strain_orig, strain_revert, decimal=4)
def setUp(self): with open(os.path.join(test_dir, 'test_toec_data.json')) as f: self.data_dict = json.load(f) self.strains = [Strain(sm) for sm in self.data_dict['strains']] self.pk_stresses = [Stress(d) for d in self.data_dict['pk_stresses']] self.c2 = self.data_dict["C2_raw"] self.c3 = self.data_dict["C3_raw"] self.exp = ElasticTensorExpansion.from_voigt([self.c2, self.c3]) self.cu = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3.623), ["Cu"], [[0] * 3]) indices = [(0, 0), (0, 1), (3, 3)] values = [167.8, 113.5, 74.5] cu_c2 = ElasticTensor.from_values_indices(values, indices, structure=self.cu, populate=True) indices = [(0, 0, 0), (0, 0, 1), (0, 1, 2), (0, 3, 3), (0, 5, 5), (3, 4, 5)] values = [-1507., -965., -71., -7., -901., 45.] cu_c3 = Tensor.from_values_indices(values, indices, structure=self.cu, populate=True) self.exp_cu = ElasticTensorExpansion([cu_c2, cu_c3]) cu_c4 = Tensor.from_voigt(self.data_dict["Cu_fourth_order"]) self.exp_cu_4 = ElasticTensorExpansion([cu_c2, cu_c3, cu_c4]) warnings.simplefilter("ignore")
def setUp(self): with open(os.path.join(test_dir, 'test_toec_data.json')) as f: self.data_dict = json.load(f) self.strains = [Strain(sm) for sm in self.data_dict['strains']] self.pk_stresses = [Stress(d) for d in self.data_dict['pk_stresses']] self.c2 = self.data_dict["C2_raw"] self.c3 = self.data_dict["C3_raw"] self.exp = ElasticTensorExpansion.from_voigt([self.c2, self.c3]) self.cu = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3.623), ["Cu"], [[0]*3]) indices = [(0, 0), (0, 1), (3, 3)] values = [167.8, 113.5, 74.5] cu_c2 = ElasticTensor.from_values_indices(values, indices, structure=self.cu, populate=True) indices = [(0, 0, 0), (0, 0, 1), (0, 1, 2), (0, 3, 3), (0, 5, 5), (3, 4, 5)] values = [-1507., -965., -71., -7., -901., 45.] cu_c3 = Tensor.from_values_indices(values, indices, structure=self.cu, populate=True) self.exp_cu = ElasticTensorExpansion([cu_c2, cu_c3]) cu_c4 = Tensor.from_voigt(self.data_dict["Cu_fourth_order"]) self.exp_cu_4 = ElasticTensorExpansion([cu_c2, cu_c3, cu_c4]) warnings.simplefilter("ignore")
def test_from_diff_fit(self): exp = ElasticTensorExpansion.from_diff_fit(self.strains, self.pk_stresses)
def test_init(self): cijkl = Tensor.from_voigt(self.c2) cijklmn = Tensor.from_voigt(self.c3) exp = ElasticTensorExpansion([cijkl, cijklmn]) from_voigt = ElasticTensorExpansion.from_voigt([self.c2, self.c3]) self.assertEqual(exp.order, 3)
class ElasticTensorExpansionTest(PymatgenTest): def setUp(self): with open(os.path.join(test_dir, 'test_toec_data.json')) as f: self.data_dict = json.load(f) self.strains = [Strain(sm) for sm in self.data_dict['strains']] self.pk_stresses = [Stress(d) for d in self.data_dict['pk_stresses']] self.c2 = self.data_dict["C2_raw"] self.c3 = self.data_dict["C3_raw"] self.exp = ElasticTensorExpansion.from_voigt([self.c2, self.c3]) self.cu = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3.623), ["Cu"], [[0] * 3]) indices = [(0, 0), (0, 1), (3, 3)] values = [167.8, 113.5, 74.5] cu_c2 = ElasticTensor.from_values_indices(values, indices, structure=self.cu, populate=True) indices = [(0, 0, 0), (0, 0, 1), (0, 1, 2), (0, 3, 3), (0, 5, 5), (3, 4, 5)] values = [-1507., -965., -71., -7., -901., 45.] cu_c3 = Tensor.from_values_indices(values, indices, structure=self.cu, populate=True) self.exp_cu = ElasticTensorExpansion([cu_c2, cu_c3]) cu_c4 = Tensor.from_voigt(self.data_dict["Cu_fourth_order"]) self.exp_cu_4 = ElasticTensorExpansion([cu_c2, cu_c3, cu_c4]) warnings.simplefilter("ignore") def tearDown(self): warnings.simplefilter("default") def test_init(self): cijkl = Tensor.from_voigt(self.c2) cijklmn = Tensor.from_voigt(self.c3) exp = ElasticTensorExpansion([cijkl, cijklmn]) from_voigt = ElasticTensorExpansion.from_voigt([self.c2, self.c3]) self.assertEqual(exp.order, 3) def test_from_diff_fit(self): exp = ElasticTensorExpansion.from_diff_fit(self.strains, self.pk_stresses) def test_calculate_stress(self): calc_stress = self.exp.calculate_stress(self.strains[0]) self.assertArrayAlmostEqual(self.pk_stresses[0], calc_stress, decimal=2) def test_energy_density(self): edensity = self.exp.energy_density(self.strains[0]) self.assertAlmostEqual(edensity, 1.36363099e-4) def test_gruneisen(self): # Get GGT ggt = self.exp_cu.get_ggt([1, 0, 0], [0, 1, 0]) self.assertArrayAlmostEqual( np.eye(3) * np.array([4.92080537, 4.2852349, -0.7147651]), ggt) # Get TGT tgt = self.exp_cu.get_tgt() self.assertArrayAlmostEqual(tgt, np.eye(3) * 2.59631832) # Get heat capacity c0 = self.exp_cu.get_heat_capacity(0, self.cu, [1, 0, 0], [0, 1, 0]) self.assertEqual(c0, 0.0) c = self.exp_cu.get_heat_capacity(300, self.cu, [1, 0, 0], [0, 1, 0]) self.assertAlmostEqual(c, 8.285611958) # Get Gruneisen parameter gp = self.exp_cu.get_gruneisen_parameter() self.assertAlmostEqual(gp, 2.59631832) gpt = self.exp_cu.get_gruneisen_parameter(temperature=200, structure=self.cu) def test_thermal_expansion_coeff(self): #TODO get rid of duplicates alpha_dp = self.exp_cu.thermal_expansion_coeff(self.cu, 300, mode="dulong-petit") alpha_debye = self.exp_cu.thermal_expansion_coeff(self.cu, 300, mode="debye") alpha_comp = 5.9435148e-7 * np.ones((3, 3)) alpha_comp[np.diag_indices(3)] = 21.4533472e-06 self.assertArrayAlmostEqual(alpha_comp, alpha_debye) def test_get_compliance_expansion(self): ce_exp = self.exp_cu.get_compliance_expansion() et_comp = ElasticTensorExpansion(ce_exp) strain_orig = Strain.from_voigt([0.01, 0, 0, 0, 0, 0]) stress = self.exp_cu.calculate_stress(strain_orig) strain_revert = et_comp.calculate_stress(stress) self.assertArrayAlmostEqual(strain_orig, strain_revert, decimal=4) def test_get_effective_ecs(self): # Ensure zero strain is same as SOEC test_zero = self.exp_cu.get_effective_ecs(np.zeros((3, 3))) self.assertArrayAlmostEqual(test_zero, self.exp_cu[0]) s = np.zeros((3, 3)) s[0, 0] = 0.02 test_2percent = self.exp_cu.get_effective_ecs(s) diff = test_2percent - test_zero self.assertArrayAlmostEqual(self.exp_cu[1].einsum_sequence([s]), diff) def test_get_strain_from_stress(self): strain = Strain.from_voigt([0.05, 0, 0, 0, 0, 0]) stress3 = self.exp_cu.calculate_stress(strain) strain_revert3 = self.exp_cu.get_strain_from_stress(stress3) self.assertArrayAlmostEqual(strain, strain_revert3, decimal=2) # fourth order stress4 = self.exp_cu_4.calculate_stress(strain) strain_revert4 = self.exp_cu_4.get_strain_from_stress(stress4) self.assertArrayAlmostEqual(strain, strain_revert4, decimal=2) def test_get_yield_stress(self): ys = self.exp_cu_4.get_yield_stress([1, 0, 0])
def run_task(self, fw_spec): ref_struct = self['structure'] d = {"analysis": {}, "initial_structure": self['structure'].as_dict()} # Get optimized structure calc_locs_opt = [ cl for cl in fw_spec.get('calc_locs', []) if 'optimiz' in cl['name'] ] if calc_locs_opt: optimize_loc = calc_locs_opt[-1]['path'] logger.info("Parsing initial optimization directory: {}".format( optimize_loc)) drone = VaspDrone() optimize_doc = drone.assimilate(optimize_loc) opt_struct = Structure.from_dict( optimize_doc["calcs_reversed"][0]["output"]["structure"]) d.update({"optimized_structure": opt_struct.as_dict()}) ref_struct = opt_struct eq_stress = -0.1 * Stress(optimize_doc["calcs_reversed"][0] ["output"]["ionic_steps"][-1]["stress"]) else: eq_stress = None if self.get("fw_spec_field"): d.update({ self.get("fw_spec_field"): fw_spec.get(self.get("fw_spec_field")) }) # Get the stresses, strains, deformations from deformation tasks defo_dicts = fw_spec["deformation_tasks"].values() stresses, strains, deformations = [], [], [] for defo_dict in defo_dicts: stresses.append(Stress(defo_dict["stress"])) strains.append(Strain(defo_dict["strain"])) deformations.append(Deformation(defo_dict["deformation_matrix"])) # Add derived stresses and strains if symmops is present for symmop in defo_dict.get("symmops", []): stresses.append(Stress(defo_dict["stress"]).transform(symmop)) strains.append(Strain(defo_dict["strain"]).transform(symmop)) deformations.append( Deformation( defo_dict["deformation_matrix"]).transform(symmop)) stresses = [-0.1 * s for s in stresses] pk_stresses = [ stress.piola_kirchoff_2(deformation) for stress, deformation in zip(stresses, deformations) ] d['fitting_data'] = { 'cauchy_stresses': stresses, 'eq_stress': eq_stress, 'strains': strains, 'pk_stresses': pk_stresses, 'deformations': deformations } logger.info("Analyzing stress/strain data") # TODO: @montoyjh: what if it's a cubic system? don't need 6. -computron # TODO: Can add population method but want to think about how it should # be done. -montoyjh order = self.get('order', 2) if order > 2: method = 'finite_difference' else: method = self.get('fitting_method', 'finite_difference') if method == 'finite_difference': result = ElasticTensorExpansion.from_diff_fit(strains, pk_stresses, eq_stress=eq_stress, order=order) if order == 2: result = ElasticTensor(result[0]) elif method == 'pseudoinverse': result = ElasticTensor.from_pseudoinverse(strains, pk_stresses) elif method == 'independent': result = ElasticTensor.from_independent_strains( strains, pk_stresses, eq_stress=eq_stress) else: raise ValueError( "Unsupported method, method must be finite_difference, " "pseudoinverse, or independent") ieee = result.convert_to_ieee(ref_struct) d.update({ "elastic_tensor": { "raw": result.voigt, "ieee_format": ieee.voigt } }) if order == 2: d.update({ "derived_properties": ieee.get_structure_property_dict(ref_struct) }) else: soec = ElasticTensor(ieee[0]) d.update({ "derived_properties": soec.get_structure_property_dict(ref_struct) }) d["formula_pretty"] = ref_struct.composition.reduced_formula d["fitting_method"] = method d["order"] = order d = jsanitize(d) # Save analysis results in json or db db_file = env_chk(self.get('db_file'), fw_spec) if not db_file: with open("elasticity.json", "w") as f: f.write(json.dumps(d, default=DATETIME_HANDLER)) else: db = VaspCalcDb.from_db_file(db_file, admin=True) db.collection = db.db["elasticity"] db.collection.insert_one(d) logger.info("Elastic analysis complete.") return FWAction()
def get_elastic_analysis(opt_task, defo_tasks): """ Performs the analysis of opt_tasks and defo_tasks necessary for an elastic analysis Args: opt_task: task doc corresponding to optimization defo_tasks: task_doc corresponding to deformations Returns: elastic document with fitted elastic tensor and analysis """ elastic_doc = {"warnings": []} opt_struct = Structure.from_dict(opt_task['output']['structure']) input_struct = Structure.from_dict(opt_task['input']['structure']) # For now, discern order (i.e. TOEC) using parameters from optimization # TODO: figure this out more intelligently diff = get(opt_task, "input.incar.EDIFFG", 0) order = 3 if np.isclose(diff, -0.001) else 2 explicit, derived = process_elastic_calcs(opt_task, defo_tasks) all_calcs = explicit + derived stresses = [c.get("cauchy_stress") for c in all_calcs] pk_stresses = [c.get("pk_stress") for c in all_calcs] strains = [c.get("strain") for c in all_calcs] elastic_doc['calculations'] = all_calcs vstrains = [s.zeroed(0.002).voigt for s in strains] if np.linalg.matrix_rank(vstrains) == 6: if order == 2: et_fit = legacy_fit(strains, stresses) elif order == 3: # Test for TOEC if len(strains) < 70: logger.info("insufficient valid strains for {} TOEC".format( opt_task['formula_pretty'])) return None eq_stress = -0.1 * Stress(opt_task['output']['stress']) # strains = [s.zeroed(0.0001) for s in strains] # et_expansion = pdb_function(ElasticTensorExpansion.from_diff_fit, # strains, pk_stresses, eq_stress=eq_stress, tol=1e-5) et_exp_raw = ElasticTensorExpansion.from_diff_fit( strains, pk_stresses, eq_stress=eq_stress, tol=1e-6) et_exp = et_exp_raw.voigt_symmetrized.convert_to_ieee(opt_struct) et_exp = et_exp.round(1) et_fit = ElasticTensor(et_exp[0]) # Update elastic doc with TOEC stuff tec = et_exp.thermal_expansion_coeff(opt_struct, 300) elastic_doc.update({ "elastic_tensor_expansion": elastic_sanitize(et_exp), "elastic_tensor_expansion_original": elastic_sanitize(et_exp_raw), "thermal_expansion_tensor": tec, "average_linear_thermal_expansion": np.trace(tec) / 3 }) et = et_fit.voigt_symmetrized.convert_to_ieee(opt_struct) vasp_input = opt_task['input'] if 'structure' in vasp_input: vasp_input.pop('structure') completed_at = max([d['completed_at'] for d in defo_tasks]) elastic_doc.update({ "optimization_task_id": opt_task['task_id'], "optimization_dir_name": opt_task['dir_name'], "cauchy_stresses": stresses, "strains": strains, "elastic_tensor": elastic_sanitize(et.zeroed(0.01).round(0)), # Convert compliance to 10^-12 Pa "compliance_tensor": elastic_sanitize(et.compliance_tensor * 1000), "elastic_tensor_original": elastic_sanitize(et_fit), "optimized_structure": opt_struct, "spacegroup": input_struct.get_space_group_info()[0], "input_structure": input_struct, "completed_at": completed_at, "optimization_input": vasp_input, "order": order, "pretty_formula": opt_struct.composition.reduced_formula }) # Add magnetic type mag = CollinearMagneticStructureAnalyzer(opt_struct).ordering.value elastic_doc['magnetic_type'] = mag_types[mag] try: prop_dict = et.get_structure_property_dict(opt_struct) prop_dict.pop('structure') except ValueError: logger.debug("Negative K or G found, structure property " "dict not computed") prop_dict = et.property_dict for k, v in prop_dict.items(): if k in ['homogeneous_poisson', 'universal_anisotropy']: prop_dict[k] = np.round(v, 2) else: prop_dict[k] = np.round(v, 0) elastic_doc.update(prop_dict) # Update with state and warnings state, warnings = get_state_and_warnings(elastic_doc) elastic_doc.update({"state": state, "warnings": warnings}) # TODO: add kpoints params? return elastic_doc else: logger.info("insufficient valid strains for {}".format( opt_task['formula_pretty'])) return None
def process_item(self, item): docs, material_dict = item grouped = group_by_material_id(material_dict, docs, 'input_structure') formula = docs[0]['pretty_formula'] if not grouped: formula = Structure.from_dict(list( material_dict.values())[0]).composition.reduced_formula logger.debug("No material match for {}".format(formula)) # For now just do the most recent one that's not failed # TODO: better sorting of docs all_docs = [] for task_id, elastic_docs in grouped.items(): elastic_docs = sorted(elastic_docs, key=lambda x: (x['order'], x['state'], x['completed_at'])) grouped_by_order = { k: list(v) for k, v in groupby(elastic_docs, key=lambda x: x['order']) } soec_docs = grouped_by_order.get(2) toec_docs = grouped_by_order.get(3) if soec_docs: final_doc = soec_docs[-1] else: final_doc = toec_docs[-1] structure = Structure.from_dict(final_doc['optimized_structure']) formula = structure.composition.reduced_formula elements = [s.symbol for s in structure.composition.elements] chemsys = '-'.join(elements) # Issue warning if relaxed structure differs warnings = final_doc.get('warnings') or [] opt = Structure.from_dict(final_doc['optimized_structure']) init = Structure.from_dict(final_doc['input_structure']) # TODO: are these the right params? if not StructureMatcher().fit(init, opt): warnings.append("Inequivalent optimization structure") material_mag = CollinearMagneticStructureAnalyzer( opt).ordering.value material_mag = mag_types[material_mag] if final_doc['magnetic_type'] != material_mag: warnings.append("Elastic magnetic phase is {}".format( final_doc['magnetic_type'])) warnings = warnings or None # Filter for failure and warnings k_vrh = final_doc['k_vrh'] if k_vrh < 0 or k_vrh > 600: state = 'failed' elif warnings is not None: state = 'warning' else: state = 'successful' final_doc.update({"warnings": warnings}) elastic_summary = { 'task_id': task_id, 'all_elastic_fits': elastic_docs, 'elasticity': final_doc, 'spacegroup': init.get_space_group_info()[0], 'magnetic_type': final_doc['magnetic_type'], 'pretty_formula': formula, 'chemsys': chemsys, 'elements': elements, 'last_updated': self.elasticity.lu_field, 'state': state } if toec_docs: # TODO: this should be a bit more refined final_toec_doc = deepcopy(toec_docs[-1]) et_exp = ElasticTensorExpansion.from_voigt( final_toec_doc['elastic_tensor_expansion']) symbol_dict = et_exp[1].zeroed(1e-2).get_symbol_dict() final_toec_doc.update({"symbol_dict": symbol_dict}) set_(elastic_summary, "elasticity.third_order", final_toec_doc) all_docs.append(jsanitize(elastic_summary)) # elastic_summary.update(final_doc) return all_docs
def run_task(self, fw_spec): ref_struct = self['structure'] d = { "analysis": {}, "initial_structure": self['structure'].as_dict() } # Get optimized structure calc_locs_opt = [cl for cl in fw_spec.get('calc_locs', []) if 'optimiz' in cl['name']] if calc_locs_opt: optimize_loc = calc_locs_opt[-1]['path'] logger.info("Parsing initial optimization directory: {}".format(optimize_loc)) drone = VaspDrone() optimize_doc = drone.assimilate(optimize_loc) opt_struct = Structure.from_dict(optimize_doc["calcs_reversed"][0]["output"]["structure"]) d.update({"optimized_structure": opt_struct.as_dict()}) ref_struct = opt_struct eq_stress = -0.1*Stress(optimize_doc["calcs_reversed"][0]["output"]["ionic_steps"][-1]["stress"]) else: eq_stress = None if self.get("fw_spec_field"): d.update({self.get("fw_spec_field"): fw_spec.get(self.get("fw_spec_field"))}) # Get the stresses, strains, deformations from deformation tasks defo_dicts = fw_spec["deformation_tasks"].values() stresses, strains, deformations = [], [], [] for defo_dict in defo_dicts: stresses.append(Stress(defo_dict["stress"])) strains.append(Strain(defo_dict["strain"])) deformations.append(Deformation(defo_dict["deformation_matrix"])) # Add derived stresses and strains if symmops is present for symmop in defo_dict.get("symmops", []): stresses.append(Stress(defo_dict["stress"]).transform(symmop)) strains.append(Strain(defo_dict["strain"]).transform(symmop)) deformations.append(Deformation(defo_dict["deformation_matrix"]).transform(symmop)) stresses = [-0.1*s for s in stresses] pk_stresses = [stress.piola_kirchoff_2(deformation) for stress, deformation in zip(stresses, deformations)] d['fitting_data'] = {'cauchy_stresses': stresses, 'eq_stress': eq_stress, 'strains': strains, 'pk_stresses': pk_stresses, 'deformations': deformations } logger.info("Analyzing stress/strain data") # TODO: @montoyjh: what if it's a cubic system? don't need 6. -computron # TODO: Can add population method but want to think about how it should # be done. -montoyjh order = self.get('order', 2) if order > 2: method = 'finite_difference' else: method = self.get('fitting_method', 'finite_difference') if method == 'finite_difference': result = ElasticTensorExpansion.from_diff_fit( strains, pk_stresses, eq_stress=eq_stress, order=order) if order == 2: result = ElasticTensor(result[0]) elif method == 'pseudoinverse': result = ElasticTensor.from_pseudoinverse(strains, pk_stresses) elif method == 'independent': result = ElasticTensor.from_independent_strains(strains, pk_stresses, eq_stress=eq_stress) else: raise ValueError("Unsupported method, method must be finite_difference, " "pseudoinverse, or independent") ieee = result.convert_to_ieee(ref_struct) d.update({ "elastic_tensor": { "raw": result.voigt, "ieee_format": ieee.voigt } }) if order == 2: d.update({"derived_properties": ieee.get_structure_property_dict(ref_struct)}) else: soec = ElasticTensor(ieee[0]) d.update({"derived_properties": soec.get_structure_property_dict(ref_struct)}) d["formula_pretty"] = ref_struct.composition.reduced_formula d["fitting_method"] = method d["order"] = order d = jsanitize(d) # Save analysis results in json or db db_file = env_chk(self.get('db_file'), fw_spec) if not db_file: with open("elasticity.json", "w") as f: f.write(json.dumps(d, default=DATETIME_HANDLER)) else: db = VaspCalcDb.from_db_file(db_file, admin=True) db.collection = db.db["elasticity"] db.collection.insert_one(d) logger.info("Elastic analysis complete.") return FWAction()
def test_from_diff_fit(self): exp = ElasticTensorExpansion.from_diff_fit(self.strains, self.pk_stresses)
def test_init(self): cijkl = Tensor.from_voigt(self.c2) cijklmn = Tensor.from_voigt(self.c3) exp = ElasticTensorExpansion([cijkl, cijklmn]) from_voigt = ElasticTensorExpansion.from_voigt([self.c2, self.c3]) self.assertEqual(exp.order, 3)
class ElasticTensorExpansionTest(PymatgenTest): def setUp(self): with open(os.path.join(test_dir, 'test_toec_data.json')) as f: self.data_dict = json.load(f) self.strains = [Strain(sm) for sm in self.data_dict['strains']] self.pk_stresses = [Stress(d) for d in self.data_dict['pk_stresses']] self.c2 = self.data_dict["C2_raw"] self.c3 = self.data_dict["C3_raw"] self.exp = ElasticTensorExpansion.from_voigt([self.c2, self.c3]) self.cu = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3.623), ["Cu"], [[0]*3]) indices = [(0, 0), (0, 1), (3, 3)] values = [167.8, 113.5, 74.5] cu_c2 = ElasticTensor.from_values_indices(values, indices, structure=self.cu, populate=True) indices = [(0, 0, 0), (0, 0, 1), (0, 1, 2), (0, 3, 3), (0, 5, 5), (3, 4, 5)] values = [-1507., -965., -71., -7., -901., 45.] cu_c3 = Tensor.from_values_indices(values, indices, structure=self.cu, populate=True) self.exp_cu = ElasticTensorExpansion([cu_c2, cu_c3]) cu_c4 = Tensor.from_voigt(self.data_dict["Cu_fourth_order"]) self.exp_cu_4 = ElasticTensorExpansion([cu_c2, cu_c3, cu_c4]) warnings.simplefilter("ignore") def tearDown(self): warnings.resetwarnings() def test_init(self): cijkl = Tensor.from_voigt(self.c2) cijklmn = Tensor.from_voigt(self.c3) exp = ElasticTensorExpansion([cijkl, cijklmn]) from_voigt = ElasticTensorExpansion.from_voigt([self.c2, self.c3]) self.assertEqual(exp.order, 3) def test_from_diff_fit(self): exp = ElasticTensorExpansion.from_diff_fit(self.strains, self.pk_stresses) def test_calculate_stress(self): calc_stress = self.exp.calculate_stress(self.strains[0]) self.assertArrayAlmostEqual(self.pk_stresses[0], calc_stress, decimal=2) def test_energy_density(self): edensity = self.exp.energy_density(self.strains[0]) self.assertAlmostEqual(edensity, 1.36363099e-4) def test_gruneisen(self): # Get GGT ggt = self.exp_cu.get_ggt([1, 0, 0], [0, 1, 0]) self.assertArrayAlmostEqual( np.eye(3)*np.array([4.92080537, 4.2852349, -0.7147651]), ggt) # Get TGT tgt = self.exp_cu.get_tgt() self.assertArrayAlmostEqual(tgt, np.eye(3)*2.59631832) # Get heat capacity c0 = self.exp_cu.get_heat_capacity(0, self.cu, [1, 0, 0], [0, 1, 0]) self.assertEqual(c0, 0.0) c = self.exp_cu.get_heat_capacity(300, self.cu, [1, 0, 0], [0, 1, 0]) self.assertAlmostEqual(c, 8.285611958) # Get Gruneisen parameter gp = self.exp_cu.get_gruneisen_parameter() self.assertAlmostEqual(gp, 2.59631832) gpt = self.exp_cu.get_gruneisen_parameter(temperature=200, structure=self.cu) def test_thermal_expansion_coeff(self): #TODO get rid of duplicates alpha_dp = self.exp_cu.thermal_expansion_coeff(self.cu, 300, mode="dulong-petit") alpha_debye = self.exp_cu.thermal_expansion_coeff(self.cu, 300, mode="debye") alpha_comp = 5.9435148e-7 * np.ones((3, 3)) alpha_comp[np.diag_indices(3)] = 21.4533472e-06 self.assertArrayAlmostEqual(alpha_comp, alpha_debye) def test_get_compliance_expansion(self): ce_exp = self.exp_cu.get_compliance_expansion() et_comp = ElasticTensorExpansion(ce_exp) strain_orig = Strain.from_voigt([0.01, 0, 0, 0, 0, 0]) stress = self.exp_cu.calculate_stress(strain_orig) strain_revert = et_comp.calculate_stress(stress) self.assertArrayAlmostEqual(strain_orig, strain_revert, decimal=4) def test_get_effective_ecs(self): # Ensure zero strain is same as SOEC test_zero = self.exp_cu.get_effective_ecs(np.zeros((3, 3))) self.assertArrayAlmostEqual(test_zero, self.exp_cu[0]) s = np.zeros((3, 3)) s[0, 0] = 0.02 test_2percent = self.exp_cu.get_effective_ecs(s) diff = test_2percent - test_zero self.assertArrayAlmostEqual(self.exp_cu[1].einsum_sequence([s]), diff) def test_get_strain_from_stress(self): strain = Strain.from_voigt([0.05, 0, 0, 0, 0, 0]) stress3 = self.exp_cu.calculate_stress(strain) strain_revert3 = self.exp_cu.get_strain_from_stress(stress3) self.assertArrayAlmostEqual(strain, strain_revert3, decimal=2) # fourth order stress4 = self.exp_cu_4.calculate_stress(strain) strain_revert4 = self.exp_cu_4.get_strain_from_stress(stress4) self.assertArrayAlmostEqual(strain, strain_revert4, decimal=2) def test_get_yield_stress(self): ys = self.exp_cu_4.get_yield_stress([1, 0, 0])