def run_task(self, fw_spec): v = Vasprun('vasprun.xml.gz') stress = v.ionic_steps[-1]['stress'] defo = self['deformation'] d_ind = np.nonzero(defo - np.eye(3)) delta = Decimal((defo - np.eye(3))[d_ind][0]) # Shorthand is d_X_V, X is voigt index, V is value dtype = "_".join(["d", str(reverse_voigt_map[d_ind][0]), "{:.0e}".format(delta)]) strain = IndependentStrain(defo) defo_dict = {'deformation_matrix': defo, 'strain': strain.tolist(), 'stress': stress} return FWAction(mod_spec=[{'_set': { 'deformation_tasks->{}'.format(dtype): defo_dict}}])
def test_from_stress_dict(self): stress_dict = dict(list(zip([IndependentStrain(def_matrix) for def_matrix in self.def_stress_dict['deformations']], [Stress(stress_matrix) for stress_matrix in self.def_stress_dict['stresses']]))) with warnings.catch_warnings(record = True): et_from_sd = ElasticTensor.from_stress_dict(stress_dict) self.assertArrayAlmostEqual(et_from_sd.voigt_symmetrized.round(2), self.elastic_tensor_1)
def test_as_strain_dict(self): strain_dict = self.default_dss.as_strain_dict() for i, def_struct in enumerate(self.default_dss): test_strain = IndependentStrain(self.default_dss.deformations[i]) strain_keys = [ strain for strain in list(strain_dict.keys()) if (strain == test_strain).all() ] self.assertEqual(len(strain_keys), 1) self.assertEqual(self.default_dss.def_structs[i], strain_dict[strain_keys[0]])
def test_from_stress_dict(self): stress_dict = dict( list( zip([ IndependentStrain(def_matrix) for def_matrix in self.def_stress_dict['deformations'] ], [ Stress(stress_matrix) for stress_matrix in self.def_stress_dict['stresses'] ]))) minimal_sd = { k: v for k, v in stress_dict.items() if (abs(k[k.ij] - 0.015) < 1e-10 or abs(k[k.ij] - 0.01005) < 1e-10) } with warnings.catch_warnings(record=True): et_from_sd = ElasticTensor.from_stress_dict(stress_dict) et_from_minimal_sd = ElasticTensor.from_stress_dict(minimal_sd) self.assertArrayAlmostEqual(et_from_sd.voigt_symmetrized.round(2), self.elastic_tensor_1) self.assertAlmostEqual(50.63394169, et_from_minimal_sd[0, 0, 0, 0])
def run_task(self, fw_spec): db_dir = os.environ['DB_LOC'] db_path = os.path.join(db_dir, 'tasks_db.json') i = fw_spec['original_task_id'] with open(db_path) as f: db_creds = json.load(f) connection = MongoClient(db_creds['host'], db_creds['port']) tdb = connection[db_creds['database']] tdb.authenticate(db_creds['admin_user'], db_creds['admin_password']) tasks = tdb[db_creds['collection']] elasticity = tdb['elasticity'] ndocs = tasks.find({"original_task_id": i, "state":"successful"}).count() existing_doc = elasticity.find_one({"relaxation_task_id" : i}) if existing_doc: print "Updating: " + i else: print "New material: " + i d = {"analysis": {}, "error": [], "warning": []} d["ndocs"] = ndocs o = tasks.find_one({"task_id" : i}, {"pretty_formula" : 1, "spacegroup" : 1, "snl" : 1, "snl_final" : 1, "run_tags" : 1}) if not o: raise ValueError("Cannot find original task id") # Get stress from deformed structure d["deformation_tasks"] = {} ss_dict = {} for k in tasks.find({"original_task_id": i}, {"deformation_matrix":1, "calculations.output":1, "state":1, "task_id":1}): defo = k['deformation_matrix'] d_ind = np.nonzero(defo - np.eye(3)) delta = Decimal((defo - np.eye(3))[d_ind][0]) # Normal deformation if d_ind[0] == d_ind[1]: dtype = "_".join(["d", str(d_ind[0][0]), "{:.0e}".format(delta)]) # Shear deformation else: dtype = "_".join(["s", str(d_ind[0] + d_ind[1]), "{:.0e}".format(delta)]) sm = IndependentStrain(defo) if dtype in d["deformation_tasks"].keys(): print "old_task: {}".format(d["deformation_tasks"][dtype]["task_id"]) print "new_task: {}".format(k["task_id"]) raise ValueError("Duplicate deformation task in database.") d["deformation_tasks"][dtype] = {"state" : k["state"], "deformation_matrix" : defo, "strain" : sm.tolist(), "task_id": k["task_id"]} if k["state"] == "successful": st = Stress(k["calculations"][-1]["output"] \ ["ionic_steps"][-1]["stress"]) ss_dict[sm] = st d["snl"] = o["snl"] if "run_tags" in o.keys(): d["run_tags"] = o["run_tags"] for tag in o["run_tags"]: if isinstance(tag, dict): if "input_id" in tag.keys(): d["input_mp_id"] = tag["input_id"] d["snl_final"] = o["snl_final"] d["pretty_formula"] = o["pretty_formula"] # Old input mp-id style if o["snl"]["about"].get("_mp_id"): d["material_id"] = o["snl"]["about"]["_mp_id"] # New style elif "input_mp_id" in d: d["material_id"] = d["input_mp_id"] else: d["material_id"] = None d["relaxation_task_id"] = i calc_struct = Structure.from_dict(o["snl_final"]) # TODO: # JHM: This test is unnecessary at the moment, but should be redone """ conventional = is_conventional(calc_struct) if conventional: d["analysis"]["is_conventional"] = True else: d["analysis"]["is_conventional"] = False """ d["spacegroup"]=o.get("spacegroup", "Unknown") if ndocs >= 20: # Perform Elastic tensor fitting and analysis result = ElasticTensor.from_stress_dict(ss_dict) d["elastic_tensor"] = result.voigt.tolist() kg_average = result.kg_average d.update({"K_Voigt":kg_average[0], "G_Voigt":kg_average[1], "K_Reuss":kg_average[2], "G_Reuss":kg_average[3], "K_Voigt_Reuss_Hill":kg_average[4], "G_Voigt_Reuss_Hill":kg_average[5]}) d["universal_anisotropy"] = result.universal_anisotropy d["homogeneous_poisson"] = result.homogeneous_poisson if ndocs < 24: d["warning"].append("less than 24 tasks completed") # Perform filter checks symm_t = result.voigt_symmetrized d["symmetrized_tensor"] = symm_t.voigt.tolist() d["analysis"]["not_rare_earth"] = True for s in calc_struct.species: if s.is_rare_earth_metal: d["analysis"]["not_rare_earth"] = False eigvals = np.linalg.eigvals(symm_t.voigt) eig_positive = np.all((eigvals > 0) & np.isreal(eigvals)) d["analysis"]["eigval_positive"] = bool(eig_positive) c11 = symm_t.voigt[0][0] c12 = symm_t.voigt[0][1] c13 = symm_t.voigt[0][2] c23 = symm_t.voigt[1][2] d["analysis"]["c11_c12"]= not (abs((c11-c12)/c11) < 0.05 or c11 < c12) d["analysis"]["c11_c13"]= not (abs((c11-c13)/c11) < 0.05 or c11 < c13) d["analysis"]["c11_c23"]= not (abs((c11-c23)/c11) < 0.1 or c11 < c23) d["analysis"]["K_R"] = not (d["K_Reuss"] < 2) d["analysis"]["G_R"] = not (d["G_Reuss"] < 2) d["analysis"]["K_V"] = not (d["K_Voigt"] < 2) d["analysis"]["G_V"] = not (d["G_Voigt"] < 2) filter_state = np.all(d["analysis"].values()) d["analysis"]["filter_pass"] = bool(filter_state) d["analysis"]["eigval"] = list(eigvals) # TODO: # JHM: eventually we can reintroduce the IEEE conversion # but as of now it's not being used, and it should # be in pymatgen """ # IEEE Conversion try: ieee_tensor = IEEE_conversion.get_ieee_tensor(struct_final, result) d["elastic_tensor_IEEE"] = ieee_tensor[0].tolist() d["analysis"]["IEEE"] = True except Exception as e: d["elastic_tensor_IEEE"] = None d["analysis"]["IEEE"] = False d["error"].append("Unable to get IEEE tensor: {}".format(e)) """ # Add thermal properties nsites = calc_struct.num_sites volume = calc_struct.volume natoms = calc_struct.composition.num_atoms weight = calc_struct.composition.weight num_density = 1e30 * nsites / volume mass_density = 1.6605e3 * nsites * volume * weight / \ (natoms * volume) tot_mass = sum([e.atomic_mass for e in calc_struct.species]) avg_mass = 1.6605e-27 * tot_mass / natoms y_mod = 9e9 * result.k_vrh * result.g_vrh / \ (3. * result.k_vrh * result.g_vrh) trans_v = 1e9 * result.k_vrh / mass_density**0.5 long_v = 1e9 * result.k_vrh + \ 4./3. * result.g_vrh / mass_density**0.5 clarke = 0.87 * 1.3806e-23 * avg_mass**(-2./3.) * \ mass_density**(1./6.) * y_mod**0.5 cahill = 1.3806e-23 / 2.48 * num_density**(2./3.) * long_v + \ 2 * trans_v snyder_ac = 0.38483 * avg_mass * \ (long_v + 2./3.*trans_v)**3. / \ (300. * num_density**(-2./3.) * nsites**(1./3.)) snyder_opt = 1.66914e-23 * (long_v + 2./3.*trans_v) / \ num_density**(-2./3.) * \ (1 - nsites**(-1./3.)) snyder_total = snyder_ac + snyder_opt debye = 2.489e-11 * avg_mass**(-1./3.) * \ mass_density**(-1./6.) * y_mod**0.5 d["thermal"]={"num_density" : num_density, "mass_density" : mass_density, "avg_mass" : avg_mass, "num_atom_per_unit_formula" : natoms, "youngs_modulus" : y_mod, "trans_velocity" : trans_v, "long_velocity" : long_v, "clarke" : clarke, "cahill" : cahill, "snyder_acou_300K" : snyder_ac, "snyder_opt" : snyder_opt, "snyder_total" : snyder_total, "debye": debye } else: d['state'] = "Fewer than 20 successful tasks completed" return FWAction() if o["snl"]["about"].get("_kpoint_density"): d["kpoint_density"]= o["snl"]["about"].get("_kpoint_density") if d["error"]: raise ValueError("Elastic analysis failed: {}".format(d["error"])) elif d["analysis"]["filter_pass"]: d["state"] = "successful" else: d["state"] = "filter_failed" elasticity.update({"relaxation_task_id": d["relaxation_task_id"]}, d, upsert=True) return FWAction()
def test_new(self): with self.assertRaises(ValueError): IndependentStrain([[0.1, 0.1, 0], [0, 0, 0], [0, 0, 0]])
def setUp(self): self.ind_strain = IndependentStrain([[1, 0.1, 0], [0, 1, 0], [0, 0, 1]])
def run_task(self, fw_spec): # Get optimized structure optimize_loc = fw_spec["calc_locs"][0]["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"]) deformations = fw_spec['deformations'] d = {"analysis": {}, "deformation_tasks": {}, "initial_structure": self['structure'].as_dict(), "optimized_structure": opt_struct.as_dict()} stress_dict = {} dtypes = [] for deformation in deformations: defo = deformation['deformation'] d_ind = np.nonzero(defo - np.eye(3)) delta = Decimal((defo - np.eye(3))[d_ind][0]) # Shorthand is d_X_V, X is voigt index, V is value dtype = "_".join(["d", str(reverse_voigt_map[d_ind][0]), "{:.0e}".format(delta)]) strain = IndependentStrain(defo) stress = Stress(deformation['stress']) d["deformation_tasks"][dtype] = {'deformation_matrix': defo, 'strain': strain.tolist(), 'stress': deformation['stress']} dtypes.append(dtype) stress_dict[strain] = stress logger.info("ANALYZING STRESS/STRAIN DATA") # DETERMINE IF WE HAVE 6 "UNIQUE" deformations if len(set([de[:3] for de in dtypes])) == 6: # Perform Elastic tensor fitting and analysis result = ElasticTensor.from_stress_dict(stress_dict) d["elastic_tensor"] = result.voigt.tolist() kg_average = result.kg_average d.update({"K_Voigt": kg_average[0], "G_Voigt": kg_average[1], "K_Reuss": kg_average[2], "G_Reuss": kg_average[3], "K_Voigt_Reuss_Hill": kg_average[4], "G_Voigt_Reuss_Hill": kg_average[5]}) d["universal_anisotropy"] = result.universal_anisotropy d["homogeneous_poisson"] = result.homogeneous_poisson else: raise ValueError("Fewer than 6 unique deformations") d["state"] = "successful" # 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 = MMDb.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 run_task(self, fw_spec): d = { "analysis": {}, "deformation_tasks": fw_spec["deformation_tasks"], "initial_structure": self['structure'].as_dict() } # Get optimized structure calc_locs_opt = [ cl for cl in fw_spec['calc_locs'] if 'optimize' 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()}) # TODO: @montoyjh: does the below have anything to do with elastic tensor? If not, try # the more general fw_spec_field approach in the VaspToDb rather than hard-coding the # tags insertion here. -computron if fw_spec.get("tags", None): d["tags"] = fw_spec["tags"] results = fw_spec["deformation_tasks"].values() defos = [r["deformation_matrix"] for r in results] stresses = [r["stress"] for r in results] strains = np.array([Strain(r["strain"]).voigt for r in results]) stress_dict = { IndependentStrain(defo): Stress(stress) for defo, stress in zip(defos, stresses) } logger.info("Analyzing stress/strain data") # Determine if we have 6 unique deformations # TODO: @montoyjh: what if it's a cubic system? don't need 6. -computron if np.linalg.matrix_rank(strains) == 6: # Perform Elastic tensor fitting and analysis result = ElasticTensor.from_stress_dict(stress_dict) d["elastic_tensor"] = result.voigt.tolist() d.update(result.property_dict) else: raise ValueError("Fewer than 6 unique deformations") d["state"] = "successful" # 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 process_item(self, item): """ Process the tasks and materials into a dielectrics collection Args: item dict: a dict of material_id, structure, and tasks Returns: dict: a dieletrics dictionary """ root_mats = [ mat for mat in item["mats"] if mat.get("inputs", {}).get("structure optimization", None) ] deform_mats = [mat for mat in item["mats"] if mat not in root_mats] docs = [] # TODO: What structure matcher parameters to use? # TODO: Should SM parameters be configurable? sm = StructureMatcher(primitive_cell=True, scale=True, attempt_supercell=False, allow_subset=False, comparator=ElementComparator()) for r_mat in root_mats: # Enumerate over all deformations r_struc = Structure.from_dict(r_mat['initial_structure']) defos = [] stresses = [] strains = [] m_ids = [] for d_mat in deform_mats: # Find deformation matrix d_struc = Structure.from_dict(d_mat["initial_structure"]) transform_matrix = np.transpose( np.linalg.solve(r_struc.lattice.matrix, d_struc.lattice.matrix)) # apply deformation matrix to root_mat and check if the two structures match dfm = Deformation(transform_matrix) dfm_struc = dfm.apply_to_structure(r_struc) # if match store stress and strain matrix if sm.fit(dfm_struc, d_struc): # This is a deformtion of the root struc defos.append(dfm) stresses.append(d_mat['stress']) strains.append(dfm.green_lagrange_strain) m_ids.append(d_mat['material_id']) stress_dict = { IndependentStrain(defo): Stress(stress) for defo, stress in zip(defos, stresses) } self.__logger.info("Analyzing stress/strain data") # Determine if we have 6 unique deformations if np.linalg.matrix_rank(strains) == 6: # Perform Elastic tensor fitting and analysis result = ElasticTensor.from_stress_dict(stress_dict) d = { "material_id": r_mat["material_id"], "elasticity": { "elastic_tensor": result.voigt.tolist(), "material_ids": m_ids } } d["elasticity"].update(result.property_dict) docs.append(d) else: self.__logger.warn( "Fewer than 6 unique deformations for {}".format( r_mat["material_id"])) return docs
def run_task(self, fw_spec): # Get optimized structure # TODO: will this find the correct path if the workflow is rerun from the start? optimize_loc = fw_spec["calc_locs"][0]["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 = { "analysis": {}, "deformation_tasks": fw_spec["deformation_tasks"], "initial_structure": self['structure'].as_dict(), "optimized_structure": opt_struct.as_dict() } if fw_spec.get("tags", None): d["tags"] = fw_spec["tags"] dtypes = fw_spec["deformation_tasks"].keys() defos = [ fw_spec["deformation_tasks"][dtype]["deformation_matrix"] for dtype in dtypes ] stresses = [ fw_spec["deformation_tasks"][dtype]["stress"] for dtype in dtypes ] stress_dict = { IndependentStrain(defo): Stress(stress) for defo, stress in zip(defos, stresses) } logger.info("ANALYZING STRESS/STRAIN DATA") # DETERMINE IF WE HAVE 6 "UNIQUE" deformations if len(set([de[:3] for de in dtypes])) == 6: # Perform Elastic tensor fitting and analysis result = ElasticTensor.from_stress_dict(stress_dict) d["elastic_tensor"] = result.voigt.tolist() kg_average = result.kg_average d.update({ "K_Voigt": kg_average[0], "G_Voigt": kg_average[1], "K_Reuss": kg_average[2], "G_Reuss": kg_average[3], "K_Voigt_Reuss_Hill": kg_average[4], "G_Voigt_Reuss_Hill": kg_average[5] }) d["universal_anisotropy"] = result.universal_anisotropy d["homogeneous_poisson"] = result.homogeneous_poisson else: raise ValueError("Fewer than 6 unique deformations") d["state"] = "successful" # 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 = MMVaspDb.from_db_file(db_file, admin=True) db.collection = db.db["elasticity"] db.collection.insert_one(d) logger.info("ELASTIC ANALYSIS COMPLETE") return FWAction()