def test_single_atom(self): filepath = os.path.join(test_dir, "OUTCAR.Al") outcar = Outcar(filepath) expected_mag = ({"p": 0.0, "s": 0.0, "d": 0.0, "tot": 0.0},) expected_chg = ({"p": 0.343, "s": 0.425, "d": 0.0, "tot": 0.768},) self.assertAlmostEqual(outcar.magnetization, expected_mag) self.assertAlmostEqual(outcar.charge, expected_chg) self.assertFalse(outcar.is_stopped) self.assertEqual( outcar.run_stats, { "System time (sec)": 0.592, "Total CPU time used (sec)": 50.194, "Elapsed time (sec)": 52.337, "Maximum memory used (kb)": 62900.0, "Average memory used (kb)": 0.0, "User time (sec)": 49.602, "cores": "32", }, ) self.assertAlmostEqual(outcar.efermi, 8.0942) self.assertAlmostEqual(outcar.nelect, 3) self.assertAlmostEqual(outcar.total_mag, 8.2e-06) self.assertIsNotNone(outcar.as_dict())
def test_get_outcar_method(testdata): from pymatgen.io.vasp.outputs import Outcar from aiida_cusp.data.outputs.vasp_outcar import VaspOutcarData outcar = testdata / 'OUTCAR' outcar_node = VaspOutcarData(file=outcar) # create Outcar object from node and compare to the original # pymatgen class generated from the test file outcar_obj_node = outcar_node.get_outcar() outcar_obj_pmg = Outcar(outcar) node_contents = json.dumps(outcar_obj_node.as_dict(), sort_keys=True) pmg_contents = json.dumps(outcar_obj_pmg.as_dict(), sort_keys=True) assert node_contents == pmg_contents
def test_nmr_efg(self): filename = os.path.join(test_dir, "nmr_efg", "AlPO4", "OUTCAR") outcar = Outcar(filename) expected_efg = [{'eta': 0.465, 'nuclear_quadrupole_moment': 146.6, 'cq': -5.573}, {'eta': 0.465, 'nuclear_quadrupole_moment': 146.6, 'cq': -5.573}, {'eta': 0.137, 'nuclear_quadrupole_moment': 146.6, 'cq': 6.327}, {'eta': 0.137, 'nuclear_quadrupole_moment': 146.6, 'cq': 6.327}, {'eta': 0.112, 'nuclear_quadrupole_moment': 146.6, 'cq': -7.453}, {'eta': 0.112, 'nuclear_quadrupole_moment': 146.6, 'cq': -7.453}, {'eta': 0.42, 'nuclear_quadrupole_moment': 146.6, 'cq': -5.58}, {'eta': 0.42, 'nuclear_quadrupole_moment': 146.6, 'cq': -5.58}] self.assertEqual(len(outcar.efg[2:10]), len(expected_efg)) for e1, e2 in zip(outcar.efg[2:10], expected_efg): for k in e1.keys(): self.assertAlmostEqual(e1[k], e2[k], places=5) d1 = outcar.as_dict() self.assertIn("efg", d1)
def test_chemical_shifts(self): filename = os.path.join(test_dir, "nmr_chemical_shift", "hydromagnesite", "OUTCAR") outcar = Outcar(filename) expected_chemical_shifts = [[191.9974, 69.5232, 0.6342], [195.0808, 68.183, 0.833], [192.0389, 69.5762, 0.6329], [195.0844, 68.1756, 0.8336], [192.005, 69.5289, 0.6339], [195.0913, 68.1859, 0.833], [192.0237, 69.565, 0.6333], [195.0788, 68.1733, 0.8337]] self.assertAlmostEqual(len(outcar.chemical_shifts[20: 28]), len(expected_chemical_shifts)) for c1, c2 in zip(outcar.chemical_shifts[20: 28], expected_chemical_shifts): for x1, x2 in zip(list(c1.maryland_values), c2): self.assertAlmostEqual(x1, x2, places=5) d1 = outcar.as_dict() self.assertIn("chemical_shifts", d1)
def test_single_atom(self): filepath = os.path.join(test_dir, "OUTCAR.Al") outcar = Outcar(filepath) expected_mag = ({u'p': 0.0, u's': 0.0, u'd': 0.0, u'tot': 0.0},) expected_chg = ({u'p': 0.343, u's': 0.425, u'd': 0.0, u'tot': 0.768},) self.assertAlmostEqual(outcar.magnetization, expected_mag) self.assertAlmostEqual(outcar.charge, expected_chg) self.assertFalse(outcar.is_stopped) self.assertEqual(outcar.run_stats, {'System time (sec)': 0.592, 'Total CPU time used (sec)': 50.194, 'Elapsed time (sec)': 52.337, 'Maximum memory used (kb)': 62900.0, 'Average memory used (kb)': 0.0, 'User time (sec)': 49.602, 'cores': '32'}) self.assertAlmostEqual(outcar.efermi, 8.0942) self.assertAlmostEqual(outcar.nelect, 3) self.assertAlmostEqual(outcar.total_mag, 8.2e-06) self.assertIsNotNone(outcar.as_dict())
def test_init(self): for f in ['OUTCAR', 'OUTCAR.gz']: filepath = os.path.join(test_dir, f) outcar = Outcar(filepath) expected_mag = ({'d': 0.0, 'p': 0.003, 's': 0.002, 'tot': 0.005}, {'d': 0.798, 'p': 0.008, 's': 0.007, 'tot': 0.813}, {'d': 0.798, 'p': 0.008, 's': 0.007, 'tot': 0.813}, {'d': 0.0, 'p':-0.117, 's': 0.005, 'tot':-0.112}, {'d': 0.0, 'p':-0.165, 's': 0.004, 'tot':-0.162}, {'d': 0.0, 'p':-0.117, 's': 0.005, 'tot':-0.112}, {'d': 0.0, 'p':-0.165, 's': 0.004, 'tot':-0.162}) expected_chg = ({'p': 0.154, 's': 0.078, 'd': 0.0, 'tot': 0.232}, {'p': 0.707, 's': 0.463, 'd': 8.316, 'tot': 9.486}, {'p': 0.707, 's': 0.463, 'd': 8.316, 'tot': 9.486}, {'p': 3.388, 's': 1.576, 'd': 0.0, 'tot': 4.964}, {'p': 3.365, 's': 1.582, 'd': 0.0, 'tot': 4.947}, {'p': 3.388, 's': 1.576, 'd': 0.0, 'tot': 4.964}, {'p': 3.365, 's': 1.582, 'd': 0.0, 'tot': 4.947}) self.assertAlmostEqual(outcar.magnetization, expected_mag, 5, "Wrong magnetization read from Outcar") self.assertAlmostEqual(outcar.charge, expected_chg, 5, "Wrong charge read from Outcar") self.assertFalse(outcar.is_stopped) self.assertEqual(outcar.run_stats, {'System time (sec)': 0.938, 'Total CPU time used (sec)': 545.142, 'Elapsed time (sec)': 546.709, 'Maximum memory used (kb)': 0.0, 'Average memory used (kb)': 0.0, 'User time (sec)': 544.204, 'cores': '8'}) self.assertAlmostEqual(outcar.efermi, 2.0112) self.assertAlmostEqual(outcar.nelect, 44.9999991) self.assertAlmostEqual(outcar.total_mag, 0.9999998) self.assertIsNotNone(outcar.as_dict()) self.assertFalse(outcar.lepsilon) filepath = os.path.join(test_dir, 'OUTCAR.stopped') outcar = Outcar(filepath) self.assertTrue(outcar.is_stopped) for f in ['OUTCAR.lepsilon', 'OUTCAR.lepsilon.gz']: filepath = os.path.join(test_dir, f) outcar = Outcar(filepath) self.assertTrue(outcar.lepsilon) self.assertAlmostEqual(outcar.dielectric_tensor[0][0], 3.716432) self.assertAlmostEqual(outcar.dielectric_tensor[0][1], -0.20464) self.assertAlmostEqual(outcar.dielectric_tensor[1][2], -0.20464) self.assertAlmostEqual(outcar.dielectric_ionic_tensor[0][0], 0.001419) self.assertAlmostEqual(outcar.dielectric_ionic_tensor[0][2], 0.001419) self.assertAlmostEqual(outcar.dielectric_ionic_tensor[2][2], 0.001419) self.assertAlmostEqual(outcar.piezo_tensor[0][0], 0.52799) self.assertAlmostEqual(outcar.piezo_tensor[1][3], 0.35998) self.assertAlmostEqual(outcar.piezo_tensor[2][5], 0.35997) self.assertAlmostEqual(outcar.piezo_ionic_tensor[0][0], 0.05868) self.assertAlmostEqual(outcar.piezo_ionic_tensor[1][3], 0.06241) self.assertAlmostEqual(outcar.piezo_ionic_tensor[2][5], 0.06242) self.assertAlmostEqual(outcar.born[0][1][2], -0.385) self.assertAlmostEqual(outcar.born[1][2][0], 0.36465)
def assimilate(self, path, launches_coll=None): """ Parses vasp runs. Then insert the result into the db. and return the task_id or doc of the insertion. Returns: If in simulate_mode, the entire doc is returned for debugging purposes. Else, only the task_id of the inserted doc is returned. """ d = self.get_task_doc(path) if self.additional_fields: d.update(self.additional_fields) # always add additional fields, even for failed jobs try: d["dir_name_full"] = d["dir_name"].split(":")[1] d["dir_name"] = get_block_part(d["dir_name_full"]) d["stored_data"] = {} except: print 'COULD NOT GET DIR NAME' pprint.pprint(d) print traceback.format_exc() raise ValueError('IMPROPER PARSING OF {}'.format(path)) if not self.simulate: # Perform actual insertion into db. Because db connections cannot # be pickled, every insertion needs to create a new connection # to the db. conn = MongoClient(self.host, self.port) db = conn[self.database] if self.user: db.authenticate(self.user, self.password) coll = db[self.collection] # Insert dos data into gridfs and then remove it from the dict. # DOS data tends to be above the 4Mb limit for mongo docs. A ref # to the dos file is in the dos_fs_id. result = coll.find_one({"dir_name": d["dir_name"]}) if result is None or self.update_duplicates: if self.parse_dos and "calculations" in d: for calc in d["calculations"]: if "dos" in calc: dos = json.dumps(calc["dos"], cls=MontyEncoder) fs = gridfs.GridFS(db, "dos_fs") dosid = fs.put(dos) calc["dos_fs_id"] = dosid del calc["dos"] d["last_updated"] = datetime.datetime.today() if result is None: if ("task_id" not in d) or (not d["task_id"]): d["task_id"] = "mp-{}".format( db.counter.find_one_and_update( {"_id": "taskid"}, {"$inc": {"c": 1}} )["c"]) logger.info("Inserting {} with taskid = {}" .format(d["dir_name"], d["task_id"])) elif self.update_duplicates: d["task_id"] = result["task_id"] logger.info("Updating {} with taskid = {}" .format(d["dir_name"], d["task_id"])) #Fireworks processing self.process_fw(path, d) try: #Add oxide_type struct=Structure.from_dict(d["output"]["crystal"]) d["oxide_type"]=oxide_type(struct) except: logger.error("can't get oxide_type for {}".format(d["task_id"])) d["oxide_type"] = None #Override incorrect outcar subdocs for two step relaxations if "optimize structure" in d['task_type'] and \ os.path.exists(os.path.join(path, "relax2")): try: run_stats = {} for i in [1,2]: o_path = os.path.join(path,"relax"+str(i),"OUTCAR") o_path = o_path if os.path.exists(o_path) else o_path+".gz" outcar = Outcar(o_path) d["calculations"][i-1]["output"]["outcar"] = outcar.as_dict() run_stats["relax"+str(i)] = outcar.run_stats except: logger.error("Bad OUTCAR for {}.".format(path)) try: overall_run_stats = {} for key in ["Total CPU time used (sec)", "User time (sec)", "System time (sec)", "Elapsed time (sec)"]: overall_run_stats[key] = sum([v[key] for v in run_stats.values()]) run_stats["overall"] = overall_run_stats except: logger.error("Bad run stats for {}.".format(path)) d["run_stats"] = run_stats # add is_compatible mpc = MaterialsProjectCompatibility("Advanced") try: func = d["pseudo_potential"]["functional"] labels = d["pseudo_potential"]["labels"] symbols = ["{} {}".format(func, label) for label in labels] parameters = {"run_type": d["run_type"], "is_hubbard": d["is_hubbard"], "hubbards": d["hubbards"], "potcar_symbols": symbols} entry = ComputedEntry(Composition(d["unit_cell_formula"]), 0.0, 0.0, parameters=parameters, entry_id=d["task_id"]) d['is_compatible'] = bool(mpc.process_entry(entry)) except: traceback.print_exc() print 'ERROR in getting compatibility' d['is_compatible'] = None #task_type dependent processing if 'static' in d['task_type']: launch_doc = launches_coll.find_one({"fw_id": d['fw_id'], "launch_dir": {"$regex": d["dir_name"]}}, {"action.stored_data": 1}) for i in ["conventional_standard_structure", "symmetry_operations", "symmetry_dataset", "refined_structure"]: try: d['stored_data'][i] = launch_doc['action']['stored_data'][i] except: pass #parse band structure if necessary if ('band structure' in d['task_type'] or "Uniform" in d['task_type'])\ and d['state'] == 'successful': launch_doc = launches_coll.find_one({"fw_id": d['fw_id'], "launch_dir": {"$regex": d["dir_name"]}}, {"action.stored_data": 1}) vasp_run = Vasprun(zpath(os.path.join(path, "vasprun.xml")), parse_projected_eigen=False) if 'band structure' in d['task_type']: def string_to_numlist(stringlist): g=re.search('([0-9\-\.eE]+)\s+([0-9\-\.eE]+)\s+([0-9\-\.eE]+)', stringlist) return [float(g.group(i)) for i in range(1,4)] for i in ["kpath_name", "kpath"]: d['stored_data'][i] = launch_doc['action']['stored_data'][i] kpoints_doc = d['stored_data']['kpath']['kpoints'] for i in kpoints_doc: kpoints_doc[i]=string_to_numlist(kpoints_doc[i]) bs=vasp_run.get_band_structure(efermi=d['calculations'][0]['output']['outcar']['efermi'], line_mode=True) else: bs=vasp_run.get_band_structure(efermi=d['calculations'][0]['output']['outcar']['efermi'], line_mode=False) bs_json = json.dumps(bs.as_dict(), cls=MontyEncoder) fs = gridfs.GridFS(db, "band_structure_fs") bs_id = fs.put(bs_json) d['calculations'][0]["band_structure_fs_id"] = bs_id # also override band gap in task doc gap = bs.get_band_gap() vbm = bs.get_vbm() cbm = bs.get_cbm() update_doc = {'bandgap': gap['energy'], 'vbm': vbm['energy'], 'cbm': cbm['energy'], 'is_gap_direct': gap['direct']} d['analysis'].update(update_doc) d['calculations'][0]['output'].update(update_doc) coll.update_one({"dir_name": d["dir_name"]}, {'$set': d}, upsert=True) return d["task_id"], d else: logger.info("Skipping duplicate {}".format(d["dir_name"])) return result["task_id"], result else: d["task_id"] = 0 logger.info("Simulated insert into database for {} with task_id {}" .format(d["dir_name"], d["task_id"])) return 0, d
def assimilate(self, path, launches_coll=None): """ Parses vasp runs. Then insert the result into the db. and return the task_id or doc of the insertion. Returns: If in simulate_mode, the entire doc is returned for debugging purposes. Else, only the task_id of the inserted doc is returned. """ d = self.get_task_doc(path) if self.additional_fields: d.update(self.additional_fields ) # always add additional fields, even for failed jobs try: d["dir_name_full"] = d["dir_name"].split(":")[1] d["dir_name"] = get_block_part(d["dir_name_full"]) d["stored_data"] = {} except: print 'COULD NOT GET DIR NAME' pprint.pprint(d) print traceback.format_exc() raise ValueError('IMPROPER PARSING OF {}'.format(path)) if not self.simulate: # Perform actual insertion into db. Because db connections cannot # be pickled, every insertion needs to create a new connection # to the db. conn = MongoClient(self.host, self.port) db = conn[self.database] if self.user: db.authenticate(self.user, self.password) coll = db[self.collection] # Insert dos data into gridfs and then remove it from the dict. # DOS data tends to be above the 4Mb limit for mongo docs. A ref # to the dos file is in the dos_fs_id. result = coll.find_one({"dir_name": d["dir_name"]}) if result is None or self.update_duplicates: if self.parse_dos and "calculations" in d: for calc in d["calculations"]: if "dos" in calc: dos = json.dumps(calc["dos"], cls=MontyEncoder) fs = gridfs.GridFS(db, "dos_fs") dosid = fs.put(dos) calc["dos_fs_id"] = dosid del calc["dos"] d["last_updated"] = datetime.datetime.today() if result is None: if ("task_id" not in d) or (not d["task_id"]): d["task_id"] = "mp-{}".format( db.counter.find_one_and_update({"_id": "taskid"}, {"$inc": { "c": 1 }})["c"]) logger.info("Inserting {} with taskid = {}".format( d["dir_name"], d["task_id"])) elif self.update_duplicates: d["task_id"] = result["task_id"] logger.info("Updating {} with taskid = {}".format( d["dir_name"], d["task_id"])) #Fireworks processing self.process_fw(path, d) try: #Add oxide_type struct = Structure.from_dict(d["output"]["crystal"]) d["oxide_type"] = oxide_type(struct) except: logger.error("can't get oxide_type for {}".format( d["task_id"])) d["oxide_type"] = None #Override incorrect outcar subdocs for two step relaxations if "optimize structure" in d['task_type'] and \ os.path.exists(os.path.join(path, "relax2")): try: run_stats = {} for i in [1, 2]: o_path = os.path.join(path, "relax" + str(i), "OUTCAR") o_path = o_path if os.path.exists( o_path) else o_path + ".gz" outcar = Outcar(o_path) d["calculations"][ i - 1]["output"]["outcar"] = outcar.as_dict() run_stats["relax" + str(i)] = outcar.run_stats except: logger.error("Bad OUTCAR for {}.".format(path)) try: overall_run_stats = {} for key in [ "Total CPU time used (sec)", "User time (sec)", "System time (sec)", "Elapsed time (sec)" ]: overall_run_stats[key] = sum( [v[key] for v in run_stats.values()]) run_stats["overall"] = overall_run_stats except: logger.error("Bad run stats for {}.".format(path)) d["run_stats"] = run_stats # add is_compatible mpc = MaterialsProjectCompatibility("Advanced") try: func = d["pseudo_potential"]["functional"] labels = d["pseudo_potential"]["labels"] symbols = ["{} {}".format(func, label) for label in labels] parameters = { "run_type": d["run_type"], "is_hubbard": d["is_hubbard"], "hubbards": d["hubbards"], "potcar_symbols": symbols } entry = ComputedEntry(Composition(d["unit_cell_formula"]), 0.0, 0.0, parameters=parameters, entry_id=d["task_id"]) d['is_compatible'] = bool(mpc.process_entry(entry)) except: traceback.print_exc() print 'ERROR in getting compatibility' d['is_compatible'] = None #task_type dependent processing if 'static' in d['task_type']: launch_doc = launches_coll.find_one( { "fw_id": d['fw_id'], "launch_dir": { "$regex": d["dir_name"] } }, {"action.stored_data": 1}) for i in [ "conventional_standard_structure", "symmetry_operations", "symmetry_dataset", "refined_structure" ]: try: d['stored_data'][i] = launch_doc['action'][ 'stored_data'][i] except: pass #parse band structure if necessary if ('band structure' in d['task_type'] or "Uniform" in d['task_type'])\ and d['state'] == 'successful': launch_doc = launches_coll.find_one( { "fw_id": d['fw_id'], "launch_dir": { "$regex": d["dir_name"] } }, {"action.stored_data": 1}) vasp_run = Vasprun(zpath(os.path.join(path, "vasprun.xml")), parse_projected_eigen=True) if 'band structure' in d['task_type']: def string_to_numlist(stringlist): g = re.search( '([0-9\-\.eE]+)\s+([0-9\-\.eE]+)\s+([0-9\-\.eE]+)', stringlist) return [float(g.group(i)) for i in range(1, 4)] for i in ["kpath_name", "kpath"]: d['stored_data'][i] = launch_doc['action'][ 'stored_data'][i] kpoints_doc = d['stored_data']['kpath']['kpoints'] for i in kpoints_doc: if isinstance(kpoints_doc[i], six.string_types): kpoints_doc[i] = string_to_numlist( kpoints_doc[i]) bs = vasp_run.get_band_structure( efermi=d['calculations'][0]['output']['outcar'] ['efermi'], line_mode=True) else: bs = vasp_run.get_band_structure( efermi=d['calculations'][0]['output']['outcar'] ['efermi'], line_mode=False) bs_json = json.dumps(bs.as_dict(), cls=MontyEncoder) fs = gridfs.GridFS(db, "band_structure_fs") bs_id = fs.put(bs_json) d['calculations'][0]["band_structure_fs_id"] = bs_id # also override band gap in task doc gap = bs.get_band_gap() vbm = bs.get_vbm() cbm = bs.get_cbm() update_doc = { 'bandgap': gap['energy'], 'vbm': vbm['energy'], 'cbm': cbm['energy'], 'is_gap_direct': gap['direct'] } d['analysis'].update(update_doc) d['calculations'][0]['output'].update(update_doc) coll.update_one({"dir_name": d["dir_name"]}, {'$set': d}, upsert=True) return d["task_id"], d else: logger.info("Skipping duplicate {}".format(d["dir_name"])) return result["task_id"], result else: d["task_id"] = 0 logger.info( "Simulated insert into database for {} with task_id {}".format( d["dir_name"], d["task_id"])) return 0, d
def test_init(self): for f in ["OUTCAR", "OUTCAR.gz"]: filepath = os.path.join(test_dir, f) outcar = Outcar(filepath) expected_mag = ( {"d": 0.0, "p": 0.003, "s": 0.002, "tot": 0.005}, {"d": 0.798, "p": 0.008, "s": 0.007, "tot": 0.813}, {"d": 0.798, "p": 0.008, "s": 0.007, "tot": 0.813}, {"d": 0.0, "p": -0.117, "s": 0.005, "tot": -0.112}, {"d": 0.0, "p": -0.165, "s": 0.004, "tot": -0.162}, {"d": 0.0, "p": -0.117, "s": 0.005, "tot": -0.112}, {"d": 0.0, "p": -0.165, "s": 0.004, "tot": -0.162}, ) expected_chg = ( {"p": 0.154, "s": 0.078, "d": 0.0, "tot": 0.232}, {"p": 0.707, "s": 0.463, "d": 8.316, "tot": 9.486}, {"p": 0.707, "s": 0.463, "d": 8.316, "tot": 9.486}, {"p": 3.388, "s": 1.576, "d": 0.0, "tot": 4.964}, {"p": 3.365, "s": 1.582, "d": 0.0, "tot": 4.947}, {"p": 3.388, "s": 1.576, "d": 0.0, "tot": 4.964}, {"p": 3.365, "s": 1.582, "d": 0.0, "tot": 4.947}, ) self.assertAlmostEqual(outcar.magnetization, expected_mag, 5, "Wrong magnetization read from Outcar") self.assertAlmostEqual(outcar.charge, expected_chg, 5, "Wrong charge read from Outcar") self.assertFalse(outcar.is_stopped) self.assertEqual( outcar.run_stats, { "System time (sec)": 0.938, "Total CPU time used (sec)": 545.142, "Elapsed time (sec)": 546.709, "Maximum memory used (kb)": 0.0, "Average memory used (kb)": 0.0, "User time (sec)": 544.204, "cores": "8", }, ) self.assertAlmostEqual(outcar.efermi, 2.0112) self.assertAlmostEqual(outcar.nelect, 44.9999991) self.assertAlmostEqual(outcar.total_mag, 0.9999998) self.assertIsNotNone(outcar.as_dict()) self.assertFalse(outcar.lepsilon) filepath = os.path.join(test_dir, "OUTCAR.stopped") outcar = Outcar(filepath) self.assertTrue(outcar.is_stopped) for f in ["OUTCAR.lepsilon", "OUTCAR.lepsilon.gz"]: filepath = os.path.join(test_dir, f) outcar = Outcar(filepath) self.assertTrue(outcar.lepsilon) self.assertAlmostEqual(outcar.dielectric_tensor[0][0], 3.716432) self.assertAlmostEqual(outcar.dielectric_tensor[0][1], -0.20464) self.assertAlmostEqual(outcar.dielectric_tensor[1][2], -0.20464) self.assertAlmostEqual(outcar.dielectric_ionic_tensor[0][0], 0.001419) self.assertAlmostEqual(outcar.dielectric_ionic_tensor[0][2], 0.001419) self.assertAlmostEqual(outcar.dielectric_ionic_tensor[2][2], 0.001419) self.assertAlmostEqual(outcar.piezo_tensor[0][0], 0.52799) self.assertAlmostEqual(outcar.piezo_tensor[1][3], 0.35998) self.assertAlmostEqual(outcar.piezo_tensor[2][5], 0.35997) self.assertAlmostEqual(outcar.piezo_ionic_tensor[0][0], 0.05868) self.assertAlmostEqual(outcar.piezo_ionic_tensor[1][3], 0.06241) self.assertAlmostEqual(outcar.piezo_ionic_tensor[2][5], 0.06242) self.assertAlmostEqual(outcar.born[0][1][2], -0.385) self.assertAlmostEqual(outcar.born[1][2][0], 0.36465)