class DeformStructureTransformation(AbstractTransformation): """ This transformation deforms a structure by a deformation gradient matrix Args: deformation (array): deformation gradient for the transformation """ def __init__(self, deformation=((1, 0, 0), (0, 1, 0), (0, 0, 1))): self._deform = Deformation(deformation) self.deformation = self._deform.tolist() def apply_transformation(self, structure): return self._deform.apply_to_structure(structure) def __str__(self): return "DeformStructureTransformation : " + \ "Deformation = {}".format(str(self.deformation)) def __repr__(self): return self.__str__() @property def inverse(self): return DeformStructureTransformation(self._deform.inv()) @property def is_one_to_many(self): return False
class DeformStructureTransformation(AbstractTransformation): """ This transformation deforms a structure by a deformation gradient matrix Args: deformation (array): deformation gradient for the transformation """ def __init__(self, deformation): self.deformation = Deformation(deformation) def apply_transformation(self, structure): return self.deformation.apply_to_structure(structure) def __str__(self): return "DeformStructureTransformation : " + \ "Deformation = {}".format(str(self.deformation.tolist())) def __repr__(self): return self.__str__() @property def inverse(self): return DeformStructureTransformation(self.deformation.inv()) @property def is_one_to_many(self): return False def as_dict(self): return {"name": self.__class__.__name__, "version": __version__, "init_args": {"deformation": self.deformation.tolist()}, "@module": self.__class__.__module__, "@class": self.__class__.__name__}
class DeformationTest(PymatgenTest): def setUp(self): self.norm_defo = Deformation.from_index_amount((0, 0), 0.02) self.ind_defo = Deformation.from_index_amount((0, 1), 0.02) self.non_ind_defo = Deformation([[1.0, 0.02, 0.02], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]) lattice = Lattice([[3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603]]) self.structure = Structure(lattice, ["Si", "Si"], [[0, 0, 0], [0.75, 0.5, 0.75]]) def test_properties(self): # green_lagrange_strain self.assertArrayAlmostEqual( self.ind_defo.green_lagrange_strain, [[0., 0.01, 0.], [0.01, 0.0002, 0.], [0., 0., 0.]]) self.assertArrayAlmostEqual( self.non_ind_defo.green_lagrange_strain, [[0., 0.01, 0.01], [0.01, 0.0002, 0.0002], [0.01, 0.0002, 0.0002]]) def test_independence(self): self.assertFalse(self.non_ind_defo.is_independent()) self.assertEqual(self.ind_defo.get_perturbed_indices()[0], (0, 1)) def test_apply_to_structure(self): strained_norm = self.norm_defo.apply_to_structure(self.structure) strained_ind = self.ind_defo.apply_to_structure(self.structure) strained_non = self.non_ind_defo.apply_to_structure(self.structure) # Check lattices self.assertArrayAlmostEqual( strained_norm.lattice.matrix, [[3.9170018886, 0, 0], [1.958500946136, 3.32571019, 0], [0, -2.21713849, 3.13550906]]) self.assertArrayAlmostEqual( strained_ind.lattice.matrix, [[3.84019793, 0, 0], [1.9866132, 3.32571019, 0], [-0.04434277, -2.21713849, 3.13550906]]) self.assertArrayAlmostEqual( strained_non.lattice.matrix, [[3.84019793, 0, 0], [1.9866132, 3.3257102, 0], [0.0183674, -2.21713849, 3.13550906]]) # Check coordinates self.assertArrayAlmostEqual(strained_norm.sites[1].coords, [3.91700189, 1.224e-06, 2.3516318]) self.assertArrayAlmostEqual(strained_ind.sites[1].coords, [3.84019793, 1.224e-6, 2.3516318]) self.assertArrayAlmostEqual(strained_non.sites[1].coords, [3.8872306, 1.224e-6, 2.3516318]) # Check convention for applying transformation for vec, defo_vec in zip(self.structure.lattice.matrix, strained_non.lattice.matrix): new_vec = np.dot(self.non_ind_defo, np.transpose(vec)) self.assertArrayAlmostEqual(new_vec, defo_vec) for coord, defo_coord in zip(self.structure.cart_coords, strained_non.cart_coords): new_coord = np.dot(self.non_ind_defo, np.transpose(coord)) self.assertArrayAlmostEqual(new_coord, defo_coord)
class DeformationTest(PymatgenTest): def setUp(self): self.norm_defo = Deformation.from_index_amount((0, 0), 0.02) self.ind_defo = Deformation.from_index_amount((0, 1), 0.02) self.non_ind_defo = Deformation([[1.0, 0.02, 0.02], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]) lattice = Lattice([[3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603]]) self.structure = Structure(lattice, ["Si", "Si"], [[0, 0, 0], [0.75, 0.5, 0.75]]) def test_properties(self): # green_lagrange_strain self.assertArrayAlmostEqual(self.ind_defo.green_lagrange_strain, [[0., 0.01, 0.], [0.01, 0.0002, 0.], [0., 0., 0.]]) self.assertArrayAlmostEqual(self.non_ind_defo.green_lagrange_strain, [[0., 0.01, 0.01], [0.01, 0.0002, 0.0002], [0.01, 0.0002, 0.0002]]) def test_check_independent(self): self.assertRaises(ValueError, self.non_ind_defo.check_independent) self.assertEqual(self.ind_defo.check_independent(), (0, 1)) def test_apply_to_structure(self): strained_norm = self.norm_defo.apply_to_structure(self.structure) strained_ind = self.ind_defo.apply_to_structure(self.structure) strained_non = self.non_ind_defo.apply_to_structure(self.structure) # Check lattices self.assertArrayAlmostEqual(strained_norm.lattice.matrix, [[3.9170018886, 0, 0], [1.958500946136, 3.32571019, 0], [0, -2.21713849, 3.13550906]]) self.assertArrayAlmostEqual(strained_ind.lattice.matrix, [[3.84019793, 0.07680396, 0], [1.92009897, 3.36411217, 0], [0, -2.21713849, 3.13550906]]) self.assertArrayAlmostEqual(strained_non.lattice.matrix, [[3.84019793, 0.07680396, 0.07680396], [1.92009897, 3.36411217, 0.0384019794], [0, -2.21713849, 3.13550906]]) # Check coordinates self.assertArrayAlmostEqual(strained_norm.sites[1].coords, [3.91700189, 1.224e-06, 2.3516318]) self.assertArrayAlmostEqual(strained_ind.sites[1].coords, [3.84019793, 0.07680518, 2.3516318]) self.assertArrayAlmostEqual(strained_non.sites[1].coords, [3.84019793, 0.07680518, 2.42843575])
def apply(self, structure, strength_multiplier=1.): deformation = Deformation( np.eye(3) + strength_multiplier * self.deformation_matrix) new_structure = deformation.apply_to_structure(structure) # move positions if self.pos_displacement_matrices is not None: for idx, mat in self.pos_displacement_matrices: new_structure.translate_sites( indices=[idx], # use original cartesian positions vector=strength_multiplier * np.dot(mat, structure.cart_coords[idx]), frac_coords=False) return new_structure
class DeformationTest(PymatgenTest): def setUp(self): self.norm_defo = Deformation.from_index_amount((0, 0), 0.02) self.ind_defo = Deformation.from_index_amount((0, 1), 0.02) self.non_ind_defo = Deformation([[1.0, 0.02, 0.02], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]) lattice = Lattice([[3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603]]) self.structure = Structure(lattice, ["Si", "Si"], [[0, 0, 0], [0.75, 0.5, 0.75]]) def test_properties(self): # green_lagrange_strain self.assertArrayAlmostEqual( self.ind_defo.green_lagrange_strain, [[0., 0.01, 0.], [0.01, 0.0002, 0.], [0., 0., 0.]]) self.assertArrayAlmostEqual( self.non_ind_defo.green_lagrange_strain, [[0., 0.01, 0.01], [0.01, 0.0002, 0.0002], [0.01, 0.0002, 0.0002]]) def test_check_independent(self): self.assertRaises(ValueError, self.non_ind_defo.check_independent) self.assertEqual(self.ind_defo.check_independent(), (0, 1)) def test_apply_to_structure(self): strained_norm = self.norm_defo.apply_to_structure(self.structure) strained_ind = self.ind_defo.apply_to_structure(self.structure) strained_non = self.non_ind_defo.apply_to_structure(self.structure) # Check lattices self.assertArrayAlmostEqual( strained_norm.lattice.matrix, [[3.9170018886, 0, 0], [1.958500946136, 3.32571019, 0], [0, -2.21713849, 3.13550906]]) self.assertArrayAlmostEqual( strained_ind.lattice.matrix, [[3.84019793, 0.07680396, 0], [1.92009897, 3.36411217, 0], [0, -2.21713849, 3.13550906]]) self.assertArrayAlmostEqual(strained_non.lattice.matrix, [[3.84019793, 0.07680396, 0.07680396], [1.92009897, 3.36411217, 0.0384019794], [0, -2.21713849, 3.13550906]]) # Check coordinates self.assertArrayAlmostEqual(strained_norm.sites[1].coords, [3.91700189, 1.224e-06, 2.3516318]) self.assertArrayAlmostEqual(strained_ind.sites[1].coords, [3.84019793, 0.07680518, 2.3516318]) self.assertArrayAlmostEqual(strained_non.sites[1].coords, [3.84019793, 0.07680518, 2.42843575])
class DeformStructureTransformation(AbstractTransformation): """ This transformation deforms a structure by a deformation gradient matrix """ def __init__(self, deformation=((1, 0, 0), (0, 1, 0), (0, 0, 1))): """ Args: deformation (array): deformation gradient for the transformation """ self._deform = Deformation(deformation) self.deformation = self._deform.tolist() def apply_transformation(self, structure): """ Apply the transformation. Args: structure (Structure): Input Structure Returns: Deformed Structure. """ return self._deform.apply_to_structure(structure) def __str__(self): return f"DeformStructureTransformation : Deformation = {self.deformation}" def __repr__(self): return self.__str__() @property def inverse(self): """ Returns: Inverse Transformation. """ return DeformStructureTransformation(self._deform.inv) @property def is_one_to_many(self): """ Returns: False """ return False
class DeformationTest(PymatgenTest): def setUp(self): self.norm_defo = Deformation.from_index_amount((0, 0), 0.02) self.ind_defo = Deformation.from_index_amount((0, 1), 0.02) self.non_ind_defo = Deformation([[1.0, 0.02, 0.02], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]) lattice = Lattice([[3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603]]) self.structure = Structure(lattice, ["Si", "Si"], [[0, 0, 0], [0.75, 0.5, 0.75]]) def test_properties(self): # green_lagrange_strain self.assertArrayAlmostEqual(self.ind_defo.green_lagrange_strain, [[0., 0.01, 0.], [0.01, 0.0002, 0.], [0., 0., 0.]]) self.assertArrayAlmostEqual(self.non_ind_defo.green_lagrange_strain, [[0., 0.01, 0.01], [0.01, 0.0002, 0.0002], [0.01, 0.0002, 0.0002]]) def test_independence(self): self.assertFalse(self.non_ind_defo.is_independent()) self.assertEqual(self.ind_defo.get_perturbed_indices()[0], (0, 1)) def test_apply_to_structure(self): strained_norm = self.norm_defo.apply_to_structure(self.structure) strained_ind = self.ind_defo.apply_to_structure(self.structure) strained_non = self.non_ind_defo.apply_to_structure(self.structure) # Check lattices self.assertArrayAlmostEqual(strained_norm.lattice.matrix, [[3.9170018886, 0, 0], [1.958500946136, 3.32571019, 0], [0, -2.21713849, 3.13550906]]) self.assertArrayAlmostEqual(strained_ind.lattice.matrix, [[3.84019793, 0, 0], [1.9866132, 3.32571019, 0], [-0.04434277, -2.21713849, 3.13550906]]) self.assertArrayAlmostEqual(strained_non.lattice.matrix, [[3.84019793, 0, 0], [1.9866132, 3.3257102, 0], [0.0183674, -2.21713849, 3.13550906]]) # Check coordinates self.assertArrayAlmostEqual(strained_norm.sites[1].coords, [3.91700189, 1.224e-06, 2.3516318]) self.assertArrayAlmostEqual(strained_ind.sites[1].coords, [3.84019793, 1.224e-6, 2.3516318]) self.assertArrayAlmostEqual(strained_non.sites[1].coords, [3.8872306, 1.224e-6, 2.3516318]) # Check convention for applying transformation for vec, defo_vec in zip(self.structure.lattice.matrix, strained_non.lattice.matrix): new_vec = np.dot(self.non_ind_defo, np.transpose(vec)) self.assertArrayAlmostEqual(new_vec, defo_vec) for coord, defo_coord in zip(self.structure.cart_coords, strained_non.cart_coords): new_coord = np.dot(self.non_ind_defo, np.transpose(coord)) self.assertArrayAlmostEqual(new_coord, defo_coord)
def write_vasp_inputs(Str, VASPDir, functional='PBE', num_kpoints=25, additional_vasp_settings=None, strain=((1.01, 0, 0), (0, 1.05, 0), (0, 0, 1.03))): # This is a somewhat strange input set. Essentially the matgen input set (PBE+U), but with tigher # convergence. # This is also a somewhat outdated and convoluted way to generate VASP inputs but it should work fine. # These changes to the default input set give much better results. # Do not increaes the EDIFF to make it converge faster!!! # If convergence is too slow, reduce the K-points # This is still using PBE+U with matgen U values though. Need to use MITCompatibility (after the run) # to apply oxygen corrections and such. # In other expansions that rely on SCAN or HSE, the corrections are different - no O correction for example # In additional_vasp_settings, you can add to, or modify the default VASPsettings. VASPSettings = { "ALGO": 'VeryFast', "ISYM": 0, "ISMEAR": 0, "EDIFF": 1e-6, "NELM": 400, "NSW": 1000, "EDIFFG": -0.02, 'LVTOT': False, 'LWAVE': False, 'LCHARG': False, 'NELMDL': -6, 'NELMIN': 8, 'LSCALU': False, 'NPAR': 2, 'NSIM': 2, 'POTIM': 0.25, 'LDAU': True } if additional_vasp_settings: for key in additional_vasp_settings: VASPSettings[key] = additional_vasp_settings[key] print('Changed {} setting to {}.'.format( key, additional_vasp_settings[key])) if not os.path.isdir(VASPDir): os.mkdir(VASPDir) # Joggle the lattice to help symmetry broken relaxation. You may turn it off by setting strain=None if strain: deformation = Deformation(strain) defStr = deformation.apply_to_structure(Str) #Str=Structure(StrainedLatt,Species,FracCoords,to_unit_cell=False,coords_are_cartesian=False); VIO = MITRelaxSet(defStr, potcar_functional=functional) VIO.user_incar_settings = VASPSettings VIO.incar.write_file(os.path.join(VASPDir, 'INCAR')) VIO.poscar.write_file(os.path.join(VASPDir, 'POSCAR')) Kpoints.automatic(num_kpoints).write_file(os.path.join(VASPDir, 'KPOINTS')) # Use PAW_PBE pseudopotentials, cannot use PBE_52, this does not exist on ginar! # NOTE: For the POTCARs to work, you need to set up the VASP pseudopotential directory as per the # pymatgen instructions, and set the path to them in .pmgrc.yaml located in your home folder. # The pymatgen website has instuctrions for how to do this. POTSyms = VIO.potcar_symbols for i, Sym in enumerate(POTSyms): if Sym == 'Zr': POTSyms[i] = 'Zr_sv' Potcar(POTSyms, functional=functional).write_file(os.path.join(VASPDir, 'POTCAR'))
def _load_data(self): """ This function parses existing vasp calculations, does mapping check, assigns charges and writes into the calc_data file mentioned in previous functions. What we mean by mapping check here, is to see whether a deformed structure can be mapped into a supercell lattice and generates a set of correlation functions in clustersupercell.corr_from_structure. We plan to do modify corr_from_structure from using pymatgen.structurematcher to a grid matcher, which will ensure higher acceptance for DFT calculations, but does not necessarily improve CE hamitonian, since some highly dipoled and deformed structures might have poor DFT energy, and even SABOTAGE CE! """ # Every key in self.calcdata['compositions'] is a composition, and each composition contains a list of dict entrees. # relaxed_structure, input_structure, magmoms, total_energy. _is_vasp_calc = lambda fs: 'POSCAR' in fs and 'INCAR' in fs and 'KPOINTS' in fs and 'POTCAR' in fs # Load VASP runs from given directories n_matched = 0 n_inputs = 0 new_unassigned_strs = [] for root, dirs, files in os.walk(self.vaspdir): #A calculation directories has only 3 status: #accepted: calculation was successful, and already entered into calcdata.mson #falied: calculated but not successful, either aborted or can't be read into calcdata.mson #For these above two, we don't want to submit a calculation or post-process again. #not marked: calculation run not started or not finished yet. Since analyzer is always called #after runner, we don't need to worry that analyzer will find unmarked folders. if _is_vasp_calc(files) and (not 'accepted' in files) and (not 'failed' in files): print("Loading VASP run in {}".format(root)) parent_root = os.path.join(*root.split(os.sep)[0:-1]) parent_parent_root = os.path.join(*root.split(os.sep)[0:-2]) with open( os.path.join(parent_parent_root, 'composition_by_site')) as compfile: composition = json.load(compfile) compstring = json.dumps(composition) if compstring not in self.calcdata['compositions']: self.calcdata['compositions'][compstring] = [] if not os.path.isfile(os.path.join(parent_root, 'matrix')): print( 'Warning: matrix presave not found. Will autodetect supercell matrix using structure matcher,\ and will suffer from numerical errors!') matrix = None else: with open(os.path.join(parent_root, 'matrix')) as mat_file: matrix = json.load(mat_file) #Check existence of output structure try: relaxed_struct = Poscar.from_file( os.path.join(root, 'CONTCAR')).structure except: print('Entry {} CONTCAR can not be read. Skipping.'.format( root)) open(os.path.join(root, 'failed'), 'a').close() continue input_struct = Poscar.from_file( os.path.join(parent_root, 'POSCAR')).structure #Check uniqueness strict_sm = StructureMatcher(stol=0.1, ltol=0.1, angle_tol=1, comparator=ElementComparator()) _is_unique = True for entry in self.calcdata['compositions'][compstring]: entry_struct = Structure.from_dict( entry['relaxed_structure']) if strict_sm.fit(entry_struct, relaxed_struct): _is_unique = False break if not _is_unique: print('Entry {} alredy calculated before.'.format(root)) open(os.path.join(root, 'accepted'), 'a').close() continue n_inputs += 1 # Note: the input_struct here comes from the poscar in upper root, rather than fm.0, so # it is not deformed. # Rescale volume to that of unrelaxed structure, this will lead to a better mapping back. # I changed it to a rescaling tensor relaxed_lat_mat = np.matrix(relaxed_struct.lattice.matrix) input_lat_mat = np.matrix(input_struct.lattice.matrix) o2i_deformation = Deformation(input_lat_mat.T * relaxed_lat_mat.I.T) relaxed_deformed = o2i_deformation.apply_to_structure( relaxed_struct) #print(relaxed_deformed,input_struct) # Assign oxidation states to Mn based on magnetic moments in OUTCAR, first check existence of OUTCAR try: Out = Outcar(os.path.join(root, 'OUTCAR')) except: print('Entry {} OUTCAR can not be read. Skipping.'.format( root)) open(os.path.join(root, 'failed'), 'a').close() continue # Get final energy from OSZICAR or Vasprun. Vasprun is better but OSZICAR is much # faster and works fine is you separately check for convergence, sanity of # magnetic moments, structure geometry with open(os.path.join(root, 'OUTCAR')) as outfile: outcar_string = outfile.read() if 'reached required accuracy' not in outcar_string: print( 'Entry {} did not converge to required accuracy. Skipping.' .format(root)) open(os.path.join(root, 'failed'), 'a').close() continue TotE = Oszicar(os.path.join(root, 'OSZICAR')).final_energy # Checking convergence Mag = [] for SiteInd, Site in enumerate(relaxed_struct.sites): Mag.append(np.abs(Out.magnetization[SiteInd]['tot'])) new_entry = {} new_entry['input_structure'] = input_struct.as_dict() new_entry['relaxed_structure'] = relaxed_struct.as_dict() new_entry['relaxed_deformed'] = relaxed_deformed.as_dict() new_entry['total_energy'] = TotE new_entry['magmoms'] = Mag new_entry['matrix'] = matrix if os.path.isfile(os.path.join(parent_parent_root, 'axis')): with open(os.path.join(parent_parent_root, 'axis')) as axisfile: axis = json.load(axisfile) if 'axis' not in new_entry: new_entry['axis'] = axis new_unassigned_strs.append((compstring, root, new_entry)) if len(new_unassigned_strs) == 0: print('No new structures appeared. Calcdata will not be updated.') return #Charge assignment if self.is_charged_ce: relaxed_deformed_pool = [] relaxed_strs_pool = [] mags = [] roots = [] energies = [] comps = [] inputs = [] mats = [] if 'axis' in new_unassigned_strs[0][2]: axis = [] for compstring, root, new_entry in new_unassigned_strs: # Out=Outcar(os.path.join(root,'OUTCAR')) Mag = new_entry['magmoms'] relaxed_struct = Structure.from_dict( new_entry['relaxed_structure']) relaxed_deformed = Structure.from_dict( new_entry['relaxed_deformed']) # Throw out structures where oxidation states don't make charge balanced. mags.append(Mag) roots.append(root) relaxed_strs_pool.append(relaxed_struct) relaxed_deformed_pool.append(relaxed_deformed) comps.append(compstring) inputs.append(Structure.from_dict( new_entry['input_structure'])) energies.append(new_entry['total_energy']) mats.append(new_entry['matrix']) if 'axis' in new_entry: axis.append(new_entry['axis']) CA = ChargeAssign(relaxed_strs_pool, mags, algo=self.assign_algo) relaxed_strs_assigned = CA.assigned_structures relaxed_deformed_assigned = CA.extend_assignments( relaxed_deformed_pool, mags) for i in range(len(inputs)): if relaxed_strs_assigned[ i] is not None and relaxed_deformed_assigned[ i] is not None: # Checking whether structure can be mapped to corr function. # This is out deformation tolerance. try: if mats[i] is not None: cesup = self.ce.supercell_from_matrix(mats[i]) corr = cesup.corr_from_structure( relaxed_deformed_assigned[i]) else: corr = self.ce.corr_from_structure( relaxed_deformed_assigned[i]) except: print( "Entry {} too far from original lattice. Skipping." .format(roots[i])) open(os.path.join(roots[i], 'failed'), 'a').close() continue assigned_entry = {} assigned_entry['input_structure'] = inputs[i].as_dict() assigned_entry[ 'relaxed_structure'] = relaxed_strs_assigned[ i].as_dict() assigned_entry[ 'relaxed_deformed'] = relaxed_deformed_assigned[ i].as_dict() assigned_entry['matrix'] = mats[i] assigned_entry['total_energy'] = energies[i] assigned_entry['magmoms'] = mags[i] if 'axis' in new_unassigned_strs[0][2]: assigned_entry['axis'] = axis[i] self.calcdata['compositions'][comps[i]].append( assigned_entry) print('Entry {} accepted!'.format(roots[i])) open(os.path.join(roots[i], 'accepted'), 'a').close() n_matched += 1 else: print("Entry {} can not be assigned. Skipping.".format( roots[i])) open(os.path.join(roots[i], 'failed'), 'a').close() continue else: print('Doing non charged ce.') for compstring, root, new_entry in new_unassigned_strs: # Checking whether structure can be mapped to corr function. # This is out deformation tolerance. try: if new_entry['matrix'] is not None: cesup = self.ce.supercell_from_matrix( new_entry['matrix']) corr = cesup.corr_from_structure( Structure.from_dict(new_entry['relaxed_defromed'])) else: corr = self.ce.corr_from_structure( Structure.from_dict(new_entry['relaxed_defromed'])) except: print("Entry {} too far from original lattice. Skipping.". format(root)) open(os.path.join(root, 'failed'), 'a').close() continue self.calcdata['compositions'][compstring].append(new_entry) open(os.path.join(root, 'accepted'), 'a').close() n_matched += 1 # Data already deduplicated! print( '{}/{} structures matched in this run. Parsed vasp data will be saved into {}.' .format(n_matched, n_inputs, self.calc_data_file))