def get_Poisson_Ratio(vasp_db, tag, volume): try: volumes_c = vasp_db.db['elasticity'].find({'metadata.tag': tag}, \ {'_id':0, 'elastic_tensor':1, 'initial_structure':1, 'fitting_data':1}) except: return None VCij = [] Poisson_Ratio = [] for i in volumes_c: vol = float(i['initial_structure']['lattice']['volume']) if vol in VCij: continue Cij = np.array(i['elastic_tensor']['ieee_format']) A = (Cij[0, 0] + Cij[1, 1] + Cij[2, 2]) / 3. B = (Cij[0, 1] + Cij[0, 2] + Cij[1, 2]) / 3. C = (Cij[3, 3] + Cij[4, 4] + Cij[5, 5]) / 3. Bv = (A + 2. * B) / 3. Gv = (A - B + 3. * C) / 5. Ev = 9. * Bv * Gv / (Gv + 3. * Bv) p_r = 0.5 * Ev / Gv - 1.0 VCij.append(vol) Poisson_Ratio.append(p_r) if len(VCij) > 1: if vol < VCij[0] or vol > VCij[-1]: return None else: Poisson_Ratio = np.array(sort_x_by_y(Poisson_Ratio, VCij)) VCij = sort_x_by_y(VCij, VCij) f2 = interp1d(VCij, Poisson_Ratio) pratio = float(f2(volume)) print("Calculated Poisson ratio based on Cij =", pratio) return pratio
def get_static_structure_by_metadata(metadata, db_file=None): ''' Get the static structure by metadata Parameters ---------- metadata: dict The metadata use for searching the database db_file: filepath-like The file path of db.json(the settings for mongodb file) if it is None or >>db_file<<, then using the db_file of fireworks configurations Returns ------- structure_list: list The list of different structures. The structures are sorted by volume ''' if (db_file is None) or (db_file == '>>db_file<<'): db_file = loadfn(config_to_dict()["FWORKER_LOC"])["env"]["db_file"] vasp_db = VaspCalcDb.from_db_file(db_file, admin=True) static_items = list( vasp_db.collection.find( {'$and': [{ 'metadata.tag': metadata['tag'] }, { 'adopted': True }]})) #static_items = list(vasp_db.db['tasks'].find({'metadata.tag': metadata})) structure_list = [ Structure.from_dict(itemi['output']['structure']) for itemi in static_items ] volumes = [ itemi['output']['structure']['lattice']['volume'] for itemi in static_items ] energies = [itemi['output']['energy_per_atom'] for itemi in static_items] band_gap = [] static_settings = {} for itemi in static_items: if itemi['output']['is_gap_direct']: band_gap.append(itemi['output']['bandgap']) else: band_gap.append(itemi['output']['direct_gap']) if not static_settings: pot = itemi['input']['pseudo_potential']['functional'].upper() if pot == "": pot = itemi['orig_inputs']['potcar']['functional'].upper() if pot == 'Perdew-Zunger81'.upper(): pot = "LDA" static_settings['user_potcar_functional'] = pot static_settings['user_incar_settings'] = itemi['input']['incar'] structure_list = sort_x_by_y(structure_list, volumes) band_gap = sort_x_by_y(band_gap, volumes) energies = sort_x_by_y(energies, volumes) volumes = sorted(volumes) return (structure_list, energies, band_gap, static_settings, volumes)
def get_qha_from_static(self): self.head = ['Temperature', 'Optimum_volume', 'Gibbs_energy'] self.unit = ['K', 'A^3', 'eV'] (volumes, energies, dos_objs, structure) = self.get_vol_energy_dos_from_static() self.structure = structure f_vib = None # phonon properties if self.phonon: # get the vibrational properties from the FW spec phonon_calculations = list(self.vaspdb.db['phonon'].find({ 'metadata': self.metadata }).sort('_id', 1)) vol_vol = [ calc['volume'] for calc in phonon_calculations ] # these are just used for sorting and will be thrown away vol_f_vib = [calc['F_vib'] for calc in phonon_calculations] # sort them order of the unit cell volumes vol_f_vib = sort_x_by_y(vol_f_vib, vol_vol) vol_vol = sorted(vol_vol) f_vib = np.vstack(vol_f_vib) if len(volumes) > len(vol_vol): for i, voli in enumerate(vol_vol): if abs(volumes[i] - voli) > 1e-3: break if i == len(vol_vol) - 1: i = -1 volumes.pop(index=i) energies.pop(index=i) qha_result = Quasiharmonic(energies, volumes, structure, dos_objects=dos_objs, F_vib=f_vib, t_min=self.t_min, t_max=self.t_max, t_step=self.t_step, poisson=self.poisson, bp2gru=self.bp2gru) eos = Vinet(volumes, energies) eos.fit() self.volume = float(eos.v0) qha = qha_result.get_summary_dict() qha['temperatures'] = qha['temperatures'].tolist() if not self.phonon: qha['poisson'] = self.poisson qha['bp2gru'] = self.bp2gru self.data = np.vstack( (qha.pop('temperatures'), qha.pop('optimum_volumes'), qha.pop('gibbs_free_energy'))).T self.parameter = qha
def get_vol_energy_dos_from_static(self): static_calculations = self.vaspdb.db['tasks'].find( {'$and': [{ 'metadata': metadata }, { 'adopted': True }]}) energies, volumes, dos_objs = [], [], [] structure = None # single Structure for QHA calculation for calc in static_calculations: energies.append(calc['output']['energy']) volumes.append(calc['output']['structure']['lattice']['volume']) dos_objs.append(self.vaspdb.get_dos(calc['task_id'])) # get a Structure. We only need one for the masses and number of atoms in the unit cell. if structure is None: structure = Structure.from_dict(calc['output']['structure']) energies = sort_x_by_y(energies, volumes) dos_objs = sort_x_by_y(dos_objs, volumes) #structure = sort_x_by_y(structure) volumes = sorted(volumes) return (volumes, energies, dos_objs, structure)
def get_orig_EV(self, db_file, tag): vasp_db = VaspCalcDb.from_db_file(db_file, admin = True) energies = [] volumes = [] dos_objs = [] # pymatgen.electronic_structure.dos.Dos objects if vasp_db.collection.count_documents({'$and':[ {'metadata.tag': tag}, {'adopted': True}, {'output.structure.lattice.volume': {'$exists': True} }]}) <= 4: static_calculations = vasp_db.collection.find({'$and':[ {'metadata.tag': tag}, {'output.structure.lattice.volume': {'$exists': True} }]}) else: static_calculations = vasp_db.collection.find({'$and':[ {'metadata.tag': tag}, {'adopted': True}, {'output.structure.lattice.volume': {'$exists': True} }]}) vol_last = 0 for calc in static_calculations: vol = calc['output']['structure']['lattice']['volume'] if abs((vol - vol_last) / vol) > 1e-8: energies.append(calc['output']['energy']) volumes.append(vol) dos_objs.append(vasp_db.get_dos(calc['task_id'])) else: energies[-1] = calc['output']['energy'] volumes[-1] = vol dos_objs[-1] = vasp_db.get_dos(calc['task_id']) vol_last = vol energies = sort_x_by_y(energies, volumes) dos_objs = sort_x_by_y(dos_objs, volumes) volumes = sorted(volumes) n = len(volumes) - 1 # Delete duplicated while n > 0: if abs((volumes[n] - volumes[n - 1]) / volumes[n]) < 1e-8: volumes.pop(n) energies.pop(n) dos_objs.pop(n) n -= 1 print('%s Volumes = %s' %(len(volumes), volumes)) print('%s Energies = %s' %(len(energies), energies)) return(volumes, energies, dos_objs)
def canonicalize_config(configuration, occupancies): """ Return canonicalized (sorted) configurations and occupancies. Parameters ---------- configuration : list of lists DFTTK-style configuration occupancies : DFFTK-style occupancies Returns ------- tuple Tuple of canonical (configuration, occupancies) Notes ----- The sublattice ordering is preserved, but the species within a sublattice are sorted. Examples -------- >>> canonicalize_config([['Fe', 'Ni'], ['Fe']], [[0.25, 0.75], [1.0]]) # already canonical ([['Fe', 'Ni'], ['Fe']], [[0.25, 0.75], [1.0]]) >>> canonicalize_config([['Cu'], ['Mg']], [[1.0], [1.0]]) # already canonical ([['Cu'], ['Mg']], [[1.0], [1.0]]) >>> canonicalize_config([['Cu', 'Mg']], [[0.9, 0.1]]) # already canonical ([['Cu', 'Mg']], [[0.9, 0.1]]) >>> canonicalize_config([['Cu', 'Mg']], [[0.1, 0.9]]) # already canonical ([['Cu', 'Mg']], [[0.1, 0.9]]) >>> canonicalize_config([['Ni', 'Fe'], ['Fe']], [[0.75, 0.25], [1.0]]) ([['Fe', 'Ni'], ['Fe']], [[0.25, 0.75], [1.0]]) >>> canonicalize_config([['Ni', 'Fe'], ['Fe', 'Cr', 'Ni']], [[0.75, 0.25], [0.1, 0.2, 0.7]]) ([['Fe', 'Ni'], ['Cr', 'Fe', 'Ni']], [[0.25, 0.75], [0.2, 0.1, 0.7]]) """ new_occupancies = [ sort_x_by_y(occ, config) for occ, config in zip(occupancies, configuration) ] new_configuration = [sorted(config) for config in configuration] return (new_configuration, new_occupancies)
def run_task(self, fw_spec): # handle arguments and database setup db_file = env_chk(self.get("db_file"), fw_spec) tag = self["tag"] vasp_db = VaspCalcDb.from_db_file(db_file, admin=True) # get the energies, volumes and DOS objects by searching for the tag static_calculations = vasp_db.collection.find({"metadata.tag": tag}) energies = [] volumes = [] dos_objs = [] # pymatgen.electronic_structure.dos.Dos objects structure = None # single Structure for QHA calculation for calc in static_calculations: energies.append(calc['output']['energy']) volumes.append(calc['output']['structure']['lattice']['volume']) dos_objs.append(vasp_db.get_dos(calc['task_id'])) # get a Structure. We only need one for the masses and number of atoms in the unit cell. if structure is None: structure = Structure.from_dict(calc['output']['structure']) # sort everything in volume order # note that we are doing volume last because it is the thing we are sorting by! energies = sort_x_by_y(energies, volumes) dos_objs = sort_x_by_y(dos_objs, volumes) volumes = sorted(volumes) qha_result = {} qha_result['structure'] = structure.as_dict() qha_result['formula_pretty'] = structure.composition.reduced_formula qha_result['elements'] = sorted( [el.name for el in structure.composition.elements]) qha_result['metadata'] = self.get('metadata', {}) qha_result['has_phonon'] = self['phonon'] poisson = self.get('poisson', 0.363615) bp2gru = self.get('bp2gru', 1) # phonon properties if self['phonon']: # get the vibrational properties from the FW spec phonon_calculations = list(vasp_db.db['phonon'].find( {'metadata.tag': tag})) vol_vol = [ calc['volume'] for calc in phonon_calculations ] # these are just used for sorting and will be thrown away vol_f_vib = [calc['F_vib'] for calc in phonon_calculations] # sort them order of the unit cell volumes vol_f_vib = sort_x_by_y(vol_f_vib, vol_vol) f_vib = np.vstack(vol_f_vib) qha = Quasiharmonic(energies, volumes, structure, dos_objects=dos_objs, F_vib=f_vib, t_min=self['t_min'], t_max=self['t_max'], t_step=self['t_step'], poisson=poisson, bp2gru=bp2gru) qha_result['phonon'] = qha.get_summary_dict() qha_result['phonon']['temperatures'] = qha_result['phonon'][ 'temperatures'].tolist() # calculate the Debye model results no matter what qha_debye = Quasiharmonic(energies, volumes, structure, dos_objects=dos_objs, F_vib=None, t_min=self['t_min'], t_max=self['t_max'], t_step=self['t_step'], poisson=poisson, bp2gru=bp2gru) # fit 0 K EOS for good measure eos = Vinet(volumes, energies) eos.fit() errors = eos.func(volumes) - energies sum_square_error = float(np.sum(np.square(errors))) eos_res = {} eos_res['b0_GPa'] = float(eos.b0_GPa) eos_res['b0'] = float(eos.b0) eos_res['b1'] = float(eos.b1) eos_res['eq_volume'] = float(eos.v0) eos_res['eq_energy'] = float(eos.e0) eos_res['energies'] = energies eos_res['volumes'] = volumes eos_res['name'] = 'Vinet' eos_res['error'] = {} eos_res['error']['difference'] = errors.tolist( ) # volume by volume differences eos_res['error']['sum_square_error'] = sum_square_error qha_result['eos'] = eos_res qha_result['debye'] = qha_debye.get_summary_dict() qha_result['debye']['poisson'] = poisson qha_result['debye']['bp2gru'] = bp2gru qha_result['debye']['temperatures'] = qha_result['debye'][ 'temperatures'].tolist() qha_result['launch_dir'] = str(os.getcwd()) # write to JSON for debugging purposes import json with open('qha_summary.json', 'w') as fp: json.dump(qha_result, fp) if self['phonon']: vasp_db.db['qha_phonon'].insert_one(qha_result) else: vasp_db.db['qha'].insert_one(qha_result)
def get_rec_from_metatag(vasp_db, m, test=False): if vasp_db.collection.count_documents({'$and':[ {'metadata.tag': m}, {'adopted': True}, \ {'output.structure.lattice.volume': {'$exists': True}}]}) <= 5: static_calculations = vasp_db.collection.find({'$and':[ {'metadata.tag': m}, \ {'output.structure.lattice.volume': {'$exists': True} }]}) else: static_calculations = vasp_db.collection.\ find({'$and':[ {'metadata': {'tag':m}}, {'adopted': True} ]}) gapfound = False energies = [] volumes = [] stresses = [] lattices = [] bandgaps = [] pressures = [] magmoms = [] emin = 1.e36 kpoints = None for calc in static_calculations: vol = calc['output']['structure']['lattice']['volume'] if kpoints is None: kpoints = calc['orig_inputs']['kpoints']['kpoints'] if vol_within(vol, volumes): continue natoms = len(calc['output']['structure']['sites']) try: sites = calc['output']['structure']['sites'] magmoms.append([{ s['label']: s['properties']['magmom'] } for s in sites]) except: pass lat = calc['output']['structure']['lattice']['matrix'] sts = calc['output']['stress'] ene = calc['output']['energy'] if ene < emin: emin = ene structure = Structure.from_dict(calc['input']['structure']) MagState = get_Magnetic_State(calc) POSCAR = structure.to(fmt="poscar") INCAR = calc['input']['incar'] gap = calc['output']['bandgap'] volumes.append(vol) energies.append(ene) stresses.append(sts) lattices.append(lat) bandgaps.append(gap) if sts != None: pressures.append((sts[0][0] + sts[1][1] + sts[2][2]) / 3.) else: pressures.append(None) if not gapfound: gapfound = float(gap) > 0.0 tvolumes = np.array(sorted(volumes)) if len(tvolumes) >= 3: dvolumes = tvolumes[1:] - tvolumes[0:-1] dvolumes = sorted(dvolumes) if abs(dvolumes[-1] - dvolumes[-2]) > 0.01 * dvolumes[-1]: all_static_calculations = vasp_db.collection.\ find({'$and':[ {'metadata.tag': m}, {'adopted': True} ]}) for calc in all_static_calculations: if len(calc['metadata']) <= 1: continue # only check constrained calculation vol = calc['output']['structure']['lattice']['volume'] if vol_within(vol, volumes): continue natoms = len(calc['output']['structure']['sites']) try: sites = calc['output']['structure']['sites'] magmoms.append([{ s['label']: s['properties']['magmom'] } for s in sites]) except: pass lat = calc['output']['structure']['lattice']['matrix'] sts = calc['output']['stress'] ene = calc['output']['energy'] if test: structure = Structure.from_dict(calc['input']['structure']) POSCAR = structure.to(fmt="poscar") INCAR = calc['input']['incar'] break if ene < emin: emin = ene structure = Structure.from_dict(calc['input']['structure']) MagState = get_Magnetic_State(calc) POSCAR = structure.to(fmt="poscar") INCAR = calc['input']['incar'] gap = calc['output']['bandgap'] volumes.append(vol) energies.append(ene) stresses.append(sts) lattices.append(lat) bandgaps.append(gap) if sts != None: pressures.append((sts[0][0] + sts[1][1] + sts[2][2]) / 3.) else: pressures.append(None) if not gapfound: gapfound = float(gap) > 0.0 energies = sort_x_by_y(energies, volumes) pressures = sort_x_by_y(pressures, volumes) stresses = sort_x_by_y(stresses, volumes) lattices = sort_x_by_y(lattices, volumes) bandgaps = sort_x_by_y(bandgaps, volumes) try: magmoms = sort_x_by_y(magmoms, volumes) except: pass volumes = sort_x_by_y(volumes, volumes) EV = {} EV['metatag'] = m EV['natoms'] = natoms EV['volumes'] = volumes EV['stresses'] = stresses EV['energies'] = energies EV['pressures'] = pressures EV['bandgaps'] = bandgaps EV['lattices'] = lattices EV['magmoms'] = magmoms EV['kpoints'] = kpoints EV['MagState'] = MagState return EV, POSCAR, INCAR
def get_rec_from_metatag(vasp_db, m): static_calculations = vasp_db.collection.\ find({'$and':[ {'metadata.tag': m}, {'adopted': True} ]}) gapfound = False energies = [] volumes = [] stresses = [] lattices = [] bandgaps = [] pressures = [] magmoms = [] emin = 1.e36 for calc in static_calculations: vol = calc['output']['structure']['lattice']['volume'] if vol_within(vol, volumes): continue natoms = len(calc['output']['structure']['sites']) try: sites = calc['output']['structure']['sites'] magmoms.append([{ s['label']: s['properties']['magmom'] } for s in sites]) except: pass lat = calc['output']['structure']['lattice']['matrix'] sts = calc['output']['stress'] ene = calc['output']['energy'] if ene < emin: structure = Structure.from_dict(calc['input']['structure']) POSCAR = structure.to(fmt="poscar") INCAR = calc['input']['incar'] gap = calc['output']['bandgap'] volumes.append(vol) energies.append(ene) stresses.append(sts) lattices.append(lat) bandgaps.append(gap) if sts != None: pressures.append((sts[0][0] + sts[1][1] + sts[2][2]) / 3.) else: pressures.append(None) if not gapfound: gapfound = float(gap) > 0.0 energies = sort_x_by_y(energies, volumes) pressures = sort_x_by_y(pressures, volumes) stresses = sort_x_by_y(stresses, volumes) lattices = sort_x_by_y(lattices, volumes) bandgaps = sort_x_by_y(bandgaps, volumes) try: magmoms = sort_x_by_y(magmoms, volumes) except: pass volumes = sort_x_by_y(volumes, volumes) EV = {} EV['metatag'] = m EV['natoms'] = natoms EV['volumes'] = volumes EV['stresses'] = stresses EV['energies'] = energies EV['pressures'] = pressures EV['bandgaps'] = bandgaps EV['lattices'] = lattices try: for volume in magmoms: for magmom in volume.values(): if magmom != 0.: EV['magmoms'] = magmoms break except: pass return EV, POSCAR, INCAR
def check_points(self, db_file, metadata, tolerance, threshold, del_limited, volumes, energies, verbose=False): self.correct = False self.error = 1e11 self.minE_value = -1 error = 1e10 num = np.arange(len(volumes)) comb = num limit = len(volumes) * del_limited # For len(num) > threshold case, do a whole number fitting to pass numbers delete if met tolerance for i in range(1): # To avoid the codes after except running if (len(num) > threshold): try: self.check_fit(volumes, energies) except: if verbose: print('Fitting error in: ', comb) break temperror = eosfit_stderr(self.eos_fit, volumes, energies) error = update_err(temperror=temperror, error=error, verbose=verbose, ind=comb) # Decrease the quantity of large results while (error > tolerance) and (len(num) > limit) and (len(num) > threshold): volume, energy = gen_volenergdos(num, volumes, energies) try: self.check_fit(volume, energy) except: if verbose: print('Fetal error in Fitting : ', num) # Seldom break fit_value = self.eos_fit.func(volume) errors = abs(fit_value - energy) num = sort_x_by_y(num, errors) errors = sorted(errors) for m in range(min(len(errors) - threshold, 1)): errors.pop(-1) num.pop(-1) temperror = cal_stderr(errors) if verbose: print( 'Absolutely average offest is: %.4f in %s numbers combination.' % (temperror, len(num))) if temperror < error: error = temperror comb = num self.minE_value = self.eos_fit.v0 # combinations len_comb = len(comb) if (error > tolerance) and (len_comb <= threshold): comb_source = comb while (error > tolerance) and (len_comb >= 4): print('Combinations in "%s"...' % len_comb) combination = combinations(comb_source, len_comb) for combs in combination: volume, energy = gen_volenergdos(combs, volumes, energies) try: self.check_fit(volume, energy) except: if verbose: print('Fitting error in: ', comb) continue temperror = eosfit_stderr(self.eos_fit, volume, energy) if verbose: print('error = %.4f in %s ' % (temperror, combs)) if temperror < error: error = temperror comb = combs self.minE_value = self.eos_fit.v0 len_comb -= 1 print('Minimum error = %s' % error, comb) if error <= tolerance: self.correct = True print('The volume ratio of minmum energy in PreStatic is %.3f.' % self.minE_value) comb = list(comb) comb.sort() self.points = comb self.error = error
def check_vol_coverage(self, volume, vol_spacing, vol_orig, run_num, energy, structure, dos_objects, phonon, db_file, tag, t_min, t_max, t_step, EVcheck_result): result = [] volumer = volume.copy() # Check minimum spacing volumer = [vol_i / vol_orig for vol_i in volumer] """ decimals = 4 for m in range(len(volumer) - 1): vol_space_m = volumer[m + 1] - volumer[m] if np.around(vol_space_m, decimals) > np.around(vol_spacing, decimals): vol = np.linspace(volumer[m], volumer[m + 1], math.ceil(vol_space_m / vol_spacing) + 1).tolist() print('Additional volume({}) is appended for the volume space({}) is larger than specified({})'.format(vol[1:-1], vol_space_m, vol_spacing)) result.append(vol[1:-1]) #result.extend(vol[1:-1]) """ # To check (and extend) deformation coverage # To make sure that coverage extension smaller than interpolation spacing vol_spacing = vol_spacing * 0.98 qha = Quasiharmonic(energy, volume, structure, dos_objects=dos_objects, F_vib=None, t_min=t_min, t_max=t_max, t_step=t_step, poisson=0.363615, bp2gru=2. / 3.) vol_max = np.nanmax(qha.optimum_volumes) vol_min = np.nanmin(qha.optimum_volumes) EVcheck_result['debye'] = qha.get_summary_dict() EVcheck_result['debye']['temperatures'] = EVcheck_result['debye'][ 'temperatures'].tolist() if phonon: # get the vibrational properties from the FW spec # TODO: add a stable check in Quasiharmonic vasp_db = VaspCalcDb.from_db_file(db_file=db_file, admin=True) phonon_calculations = list(vasp_db.db['phonon'].find( {'$and': [{ 'metadata.tag': tag }, { 'adopted': True }]})) #vol_vol = [calc['volume'] for calc in phonon_calculations] # these are just used for sorting and will be thrown away #vol_f_vib = [calc['F_vib'] for calc in phonon_calculations] vol_vol = [] vol_f_vib = [] for calc in phonon_calculations: if calc['volume'] in vol_vol: continue if calc['volume'] not in volume: continue vol_vol.append(calc['volume']) vol_f_vib.append(calc['F_vib']) # sort them order of the unit cell volumes vol_f_vib = sort_x_by_y(vol_f_vib, vol_vol) f_vib = np.vstack(vol_f_vib) qha_phonon = Quasiharmonic(energy, volume, structure, dos_objects=dos_objects, F_vib=f_vib, t_min=t_min, t_max=t_max, t_step=t_step, poisson=0.363615, bp2gru=2. / 3.) vol_max = max(np.nanmax(qha_phonon.optimum_volumes), vol_max) vol_min = min(np.nanmax(qha_phonon.optimum_volumes), vol_min) EVcheck_result['phonon'] = qha_phonon.get_summary_dict() EVcheck_result['phonon']['temperatures'] = EVcheck_result[ 'phonon']['temperatures'].tolist() EVcheck_result['MIN_volume_Evaluated'] = '%.3f' % vol_min EVcheck_result['MAX_volume_Evaluated'] = '%.3f' % vol_max print('Evaluated MIN volume is %.3f;' % vol_min) print('Evaluated MAX volume is %.3f;' % vol_max) """ vol_max = vol_max / vol_orig vol_min = vol_min / vol_orig counter = 1 # Using max_append for reducing unnecessary calculations because of rough fitting max_append = 1 if phonon else 2 # Over coverage ratio set to 1.01 as following if volumer[-1] * 1.01 < vol_max: print('The current maximum volume is smaller than maximum volume evaluated by quasiharmonic analysis') result.append(volumer[-1] + vol_spacing) # counter is set to limit calculation times when exception occurs while (counter < max_append) and (result[-1] < vol_max): result.append(result[-1] + vol_spacing) counter += 1 counter = 1 if volumer[0] * 0.99 > vol_min: print('The current minimum volume is larger than minimum volume evaluated by quasiharmonic analysis') result.append(volumer[0] - vol_spacing) while (counter < max_append) and (result[-1] > vol_min): result.append(result[-1] - vol_spacing) counter += 1 """ val, idx = min((val, idx) for (idx, val) in enumerate(energy)) nV = len(energy) nV_addL = 3 if nV_addL - idx > 0: vol_spacing = volumer[1] - volumer[0] for i in range(nV_addL - idx): result.append(volumer[0] - (nV_addL - idx - i) * vol_spacing) nV_addR = 4 if idx + 1 + nV_addR - nV > 0: vol_spacing = volumer[-1] - volumer[-2] for i in range(idx + 1 + nV_addR - nV): result.append(volumer[-1] + (i + 1) * vol_spacing) return (np.array(result))
def check_points(self, db_file, metadata, eos_tolerance, threshold, del_limited, volumes, energies, verbose=False): """ Check the existing points if they reached the tolerance, if reached, update self.correct as True if not, reduce the point to 4 """ self.correct = False self.error = 1e11 error = 1e10 num = np.arange(len(volumes)) comb = num limit = len(volumes) * del_limited # For len(num) > threshold case, do a whole number fitting to pass numbers delete if met tolerance for i in range(1): # To avoid the codes after except running, ? if (len(num) > threshold): try: self.check_fit(volumes, energies) except: if verbose: print( 'Fitting error in: ', comb, '. If you can not achieve QHA result, try to run far negative deformations.' ) break temperror = eosfit_stderr(self.eos_fit, volumes, energies) error = update_err(temperror=temperror, error=error, verbose=verbose, ind=comb) # Decrease the quantity of large results while (error > eos_tolerance) and (len(num) > limit) and (len(num) > threshold): volume, energy = gen_volenergdos(num, volumes, energies) try: self.check_fit(volume, energy) except: if verbose: print('Fetal error in Fitting : ', num) # Seldom break fit_value = self.eos_fit.func(volume) errors = abs(fit_value - energy) num = sort_x_by_y(num, errors) errors = sorted(errors) for m in range(min(len(errors) - threshold, 1)): errors.pop(-1) num.pop(-1) temperror = cal_stderr(errors) error, comb = update_err(temperror=temperror, error=error, verbose=verbose, ind=comb, temp_ind=num) # combinations len_comb = len(comb) if (error > eos_tolerance) and (len_comb <= threshold): comb_source = comb while (error > eos_tolerance) and (len_comb >= 4): print('Combinations in "%s"...' % len_comb) combination = combinations(comb_source, len_comb) for combs in combination: print(volumes) print(energies) volume, energy = gen_volenergdos(combs, volumes, energies) try: self.check_fit(volume, energy) except: if verbose: print( 'Fitting error in: ', combs, '. If you can not achieve QHA result, try to run far negative deformations.' ) continue temperror = eosfit_stderr(self.eos_fit, volume, energy) error, comb = update_err(temperror=temperror, error=error, verbose=verbose, ind=comb, temp_ind=combs) len_comb -= 1 print('Minimum error = %s' % error, comb) if error <= eos_tolerance: self.correct = True comb = list(comb) comb.sort() self.points = comb self.error = error
def run_task(self): tag = self["tag"] admin = self.get('admin', False) _db_file = self.get('db_file', db_file) vasp_db = VaspCalcDb.from_db_file(db_file=_db_file, admin=admin) # get the energies, volumes and DOS objects by searching for the tag static_calculations = vasp_db.collection.find( {'$and': [{ 'metadata.tag': tag }, { 'adopted': True }]}) energies = [] volumes = [] dos_objs = [] # pymatgen.electronic_structure.dos.Dos objects structure = None # single Structure for QHA calculation for calc in static_calculations: energies.append(calc['output']['energy']) volumes.append(calc['output']['structure']['lattice']['volume']) dos_objs.append(vasp_db.get_dos(calc['task_id'])) # get a Structure. We only need one for the masses and number of atoms in the unit cell. if structure is None: structure = Structure.from_dict(calc['output']['structure']) # sort everything in volume order # note that we are doing volume last because it is the thing we are sorting by! energies = sort_x_by_y(energies, volumes) dos_objs = sort_x_by_y(dos_objs, volumes) volumes = sorted(volumes) qha_result = {} qha_result['structure'] = structure.as_dict() qha_result['formula_pretty'] = structure.composition.reduced_formula qha_result['elements'] = sorted( [el.name for el in structure.composition.elements]) qha_result['metadata'] = self.get('metadata', {'tag': tag}) #qha_result['has_phonon'] = self['phonon'] poisson = self.get('poisson', 0.363615) bp2gru = self.get('bp2gru', 1) # phonon properties # check if phonon calculations existed #always perform phonon calculations when when enough phonon calculations found num_phonons = len( list(vasp_db.db['phonon'].find( {'$and': [{ 'metadata.tag': tag }, { 'adopted': True }]}))) qha_result['has_phonon'] = num_phonons >= 5 #if self['phonon']: if qha_result['has_phonon']: # get the vibrational properties from the FW spec phonon_calculations = list(vasp_db.db['phonon'].find( {'$and': [{ 'metadata.tag': tag }, { 'adopted': True }]})) vol_vol = [] vol_f_vib = [] vol_s_vib = [] vol_c_vib = [] for calc in phonon_calculations: if calc['volume'] in vol_vol: continue vol_vol.append(calc['volume']) vol_f_vib.append(calc['F_vib']) vol_s_vib.append(calc['S_vib']) vol_c_vib.append(calc['CV_vib']) # sort them order of the unit cell volumes vol_f_vib = sort_x_by_y(vol_f_vib, vol_vol) vol_s_vib = sort_x_by_y(vol_s_vib, vol_vol) vol_c_vib = sort_x_by_y(vol_c_vib, vol_vol) f_vib = np.vstack(vol_f_vib) # by Yi Wang, after a long day debug, finally I fixex the bug below # i.e, sometimes, the number of phonon volumes is less than that of static! _volumes = [] _energies = [] _dos_objs = [] for iv, vol in enumerate(volumes): if vol not in vol_vol: continue _volumes.append(vol) _energies.append(energies[iv]) _dos_objs.append(dos_objs[iv]) volumes = _volumes energies = _energies dos_objs = _dos_objs qha = Quasiharmonic(energies, volumes, structure, dos_objects=dos_objs, F_vib=f_vib, t_min=self['t_min'], t_max=self['t_max'], t_step=self['t_step'], poisson=poisson, bp2gru=bp2gru) qha_result['phonon'] = qha.get_summary_dict() qha_result['phonon']['entropies'] = vol_s_vib qha_result['phonon']['heat_capacities'] = vol_c_vib qha_result['phonon']['temperatures'] = qha_result['phonon'][ 'temperatures'].tolist() # calculate the Debye model results no matter what qha_debye = Quasiharmonic(energies, volumes, structure, dos_objects=dos_objs, F_vib=None, t_min=self['t_min'], t_max=self['t_max'], t_step=self['t_step'], poisson=poisson, bp2gru=bp2gru) # fit 0 K EOS for good measure eos = Vinet(volumes, energies) eos.fit() errors = eos.func(volumes) - energies sum_square_error = float(np.sum(np.square(errors))) eos_res = {} eos_res['b0_GPa'] = float(eos.b0_GPa) eos_res['b0'] = float(eos.b0) eos_res['b1'] = float(eos.b1) eos_res['eq_volume'] = float(eos.v0) eos_res['eq_energy'] = float(eos.e0) eos_res['energies'] = energies eos_res['volumes'] = volumes eos_res['name'] = 'Vinet' eos_res['error'] = {} eos_res['error']['difference'] = errors.tolist( ) # volume by volume differences eos_res['error']['sum_square_error'] = sum_square_error qha_result['eos'] = eos_res qha_result['debye'] = qha_debye.get_summary_dict() qha_result['debye']['poisson'] = poisson qha_result['debye']['bp2gru'] = bp2gru qha_result['debye']['temperatures'] = qha_result['debye'][ 'temperatures'].tolist() qha_result['version_atomate'] = atomate_ver qha_result['version_dfttk'] = dfttk_ver volumes_false = [] energies_false = [] static_falses = vasp_db.collection.find( {'$and': [{ 'metadata.tag': tag }, { 'adopted': False }]}) for static_false in static_falses: volumes_false.append( static_false['output']['structure']['lattice']['volume']) energies_false.append(static_false['output']['energy']) qha_result['Volumes_fitting_false'] = volumes_false qha_result['Energies_fitting_false'] = energies_false print('Volumes_fitting_false : %s' % volumes_false) print('Energies_fitting_false: %s' % energies_false) print('number of phonon calculations found : %s' % num_phonons) """ # write to JSON for debugging purposes import json with open('qha_summary.json', 'w') as fp: json.dump(qha_result, fp, indent=4) """ if self.get("test_failure", False): return #if self['phonon']: if qha_result['has_phonon']: vasp_db.db['qha_phonon'].insert_one(qha_result) else: vasp_db.db['qha'].insert_one(qha_result)
def check_vol_coverage(self, volume, vol_spacing, vol_orig, run_num, energy, structure, dos_objects, phonon, db_file, tag, t_min, t_max, t_step, EVcheck_result): result = [] volumer = volume.copy() # Check minimum spacing for m in range(len(volumer)): volumer[m] = volumer[m] / vol_orig for m in range(len(volumer) - 1): if (volumer[m + 1] - volumer[m]) > vol_spacing: step = (volumer[m + 1] - volumer[m]) / (int((volumer[m + 1] - volumer[m]) / vol_spacing) + 1 - 0.0002 * run_num) vol = volumer[m] + step while vol < volumer[m + 1]: result.append(vol) vol += step # To check (and extend) deformation coverage # To make sure that coverage extension smaller than interpolation spacing vol_spacing = vol_spacing * 0.98 qha = Quasiharmonic(energy, volume, structure, dos_objects=dos_objects, F_vib=None, t_min=t_min, t_max=t_max, t_step=t_step, poisson=0.363615, bp2gru=1) vol_max = np.nanmax(qha.optimum_volumes) vol_min = np.nanmin(qha.optimum_volumes) EVcheck_result['debye'] = qha.get_summary_dict() EVcheck_result['debye']['temperatures'] = EVcheck_result['debye']['temperatures'].tolist() if phonon: # get the vibrational properties from the FW spec vasp_db = VaspCalcDb.from_db_file(db_file, admin=True) phonon_calculations = list(vasp_db.db['phonon'].find({'$and':[ {'metadata.tag': tag}, {'adopted': True} ]})) vol_vol = [calc['volume'] for calc in phonon_calculations] # these are just used for sorting and will be thrown away vol_f_vib = [calc['F_vib'] for calc in phonon_calculations] # sort them order of the unit cell volumes vol_f_vib = sort_x_by_y(vol_f_vib, vol_vol) f_vib = np.vstack(vol_f_vib) qha_phonon = Quasiharmonic(energy, volume, structure, dos_objects=dos_objects, F_vib=f_vib, t_min=t_min, t_max=t_max, t_step=t_step, poisson=0.363615, bp2gru=1) vol_max = max(np.nanmax(qha_phonon.optimum_volumes), vol_max) vol_min = min(np.nanmax(qha_phonon.optimum_volumes), vol_min) EVcheck_result['phonon'] = qha_phonon.get_summary_dict() EVcheck_result['phonon']['temperatures'] = EVcheck_result['phonon']['temperatures'].tolist() EVcheck_result['MIN_volume_Evaluated'] = '%.3f' %vol_min EVcheck_result['MAX_volume_Evaluated'] = '%.3f' %vol_max print('Evaluated MIN volume is %.3f;' %vol_min) print('Evaluated MAX volume is %.3f;' %vol_max) vol_max = vol_max / vol_orig vol_min = vol_min / vol_orig counter = 1 # Using max_append for reducing unnecessary calculations because of rough fitting max_append = 1 if phonon else 2 # Over coverage ratio set to 1.01 as following if volumer[-1] * 1.01 < vol_max: result.append(volumer[-1] + vol_spacing) # counter is set to limit calculation times when exception occurs while (counter < max_append) and (result[-1] < vol_max): result.append(result[-1] + vol_spacing) counter += 1 counter = 1 if volumer[0] * 0.99 > vol_min: result.append(volumer[0] - vol_spacing) while (counter < max_append) and (result[-1] > vol_min): result.append(result[-1] - vol_spacing) counter += 1 return(np.array(result))
def check_points(self, db_file, metadata, tolerance, threshold, del_limited, volumes, energies, verbose = False): self.correct = False self.error = 1e11 error = 1e10 num = np.arange(len(volumes)) comb = num limit = len(volumes) * del_limited # For len(num) > threshold case, do a whole number fitting to pass numbers delete if met tolerance for i in range(1): # To avoid the codes after except running if (len(num) > threshold): try: self.check_fit(volumes, energies) except: if verbose: print('Fitting error in: ', comb, '. If you can not achieve QHA result, try to run far negative deformations.') break fit_value = self.eos_fit.func(volumes) temperror = 0 for m in range(len(volumes)): temperror += math.pow((fit_value[m] - energies[m]), 2) temperror = math.sqrt(temperror) / len(volumes) if verbose: print('error = %.4f in %s ' %(temperror, comb)) if temperror < error: error = temperror # Decrease the quantity of large results while (error > tolerance) and (len(num) > limit) and (len(num) > threshold): volume, energy = self.gen_volenerg(num, volumes, energies) try: self.check_fit(volume, energy) except: if verbose: print('Fetal error in Fitting : ', num) # Seldom break fit_value = self.eos_fit.func(volume) errors = abs(fit_value - energy) num = sort_x_by_y(num, errors) errors = sorted(errors) for m in range(min(len(errors) - threshold, 1)): errors.pop(-1) num.pop(-1) temperror = 0 for m in range(len(num)): temperror += math.pow(errors[m], 2) temperror = math.sqrt(temperror) / len(num) if verbose: print('Absolutely average offest is: %.4f in %s numbers combination.' %(temperror, len(num))) if temperror < error: error = temperror comb = num # combinations len_comb = len(comb) if (error > tolerance) and (len_comb <= threshold): comb_source = comb while (error > tolerance) and (len_comb >= 4): print('Combinations in "%s"...' %len_comb) combination = combinations(comb_source, len_comb) for combs in combination: volume, energy = self.gen_volenerg(combs, volumes, energies) try: self.check_fit(volume, energy) except: if verbose: print('Fitting error in: ', comb, '. If you can not achieve QHA result, try to run far negative deformations.') continue fit_value = self.eos_fit.func(volume) temperror = 0 for m in range(len(volume)): temperror += math.pow((fit_value[m] - energy[m]), 2) temperror = math.sqrt(temperror) / len(volume) if verbose: print('error = %.4f in %s ' %(temperror, combs)) if temperror < error: error = temperror comb = combs len_comb -= 1 print('Minimum error = %s' %error, comb) if error <= tolerance: self.correct = True comb = list(comb) comb.sort() self.points = comb self.error = error
def qha_renew(self): hit = [] phases = [] static_collection = (self.vasp_db).collection.find({'$and':[{'metadata': { "$exists": True }}, \ {'adopted': True} ]}) for i in static_collection: mm = i['metadata'] if mm in hit: continue if len(mm) > 1: continue else: hit.append(mm) structure = Structure.from_dict(i['output']['structure']) formula_pretty = structure.composition.reduced_formula try: formula2composition(formula_pretty) except: formula_pretty = reduced_formula( structure.composition.alphabetical_formula) sa = SpacegroupAnalyzer(structure) phasename = formula_pretty+'_'\ + sa.get_space_group_symbol().replace('/','.')+'_'+str(sa.get_space_group_number()) if phasename in phases: for jj in range(10000): nphasename = phasename + "#" + str(jj) if nphasename in phases: continue phasename = nphasename break phases.append(phasename) print("\nfound complete calculations in the task collection:\n") total = 0 total_qha = 0 total_qha_phonon = 0 all_static_calculations = list((self.vasp_db).db['tasks'].\ find({'$and':[{'metadata': { "$exists": True }}, {'adopted': True} ]},\ {'metadata':1, 'output':1, 'input':1, 'orig_inputs':1})) all_qha_calculations = list((self.vasp_db).db['qha'].\ find({'$and':[{'metadata': { "$exists": True }},{'has_phonon':True}]}, {'metadata':1, 'temperatures':1})) all_qha_phonon_calculations = list((self.vasp_db).db['qha_phonon'].\ find({'$and':[{'metadata': { "$exists": True }},{'has_phonon':True}]}, {'metadata':1, 'temperatures':1})) for i, m in enumerate(hit): if self.skipby(phases[i], m['tag']): continue total += 1 static_calculations = [ f for f in all_static_calculations if f['metadata']['tag'] == m['tag'] ] qha_calculations = [ f for f in all_qha_calculations if f['metadata']['tag'] == m['tag'] ] qha_phonon_calculations = [ f for f in all_qha_phonon_calculations if f['metadata']['tag'] == m['tag'] ] qha_phonon_success = len(qha_phonon_calculations) > 0 if qha_phonon_success: total_qha_phonon += 1 if len(qha_calculations) > 0 or qha_phonon_success: total_qha += 1 potsoc = None volumes = [] energies = [] for ii, calc in enumerate(static_calculations): vol = calc['output']['structure']['lattice']['volume'] if vol in volumes: if len(calc['metadata']) > 1: continue else: ix = volumes.index(vol) volumes.pop(ix) energies.pop(ix) volumes.append( calc['output']['structure']['lattice']['volume']) energies.append(calc['output']['energy']) if potsoc is None: potsoc = get_used_pot(calc) pname = phases[i].split('#') if len(pname) > 1: phases[i] = pname[0] + potsoc + '#' + pname[1] else: phases[i] = pname[0] + potsoc nS = len(volumes) if nS < 6: continue if qha_phonon_success and not self.db_renew: continue energies = sort_x_by_y(energies, volumes) volumes = sorted(volumes) volumes = np.array(volumes) energies = np.array(energies) val, idx = min((val, idx) for (idx, val) in enumerate(energies)) if idx < 2 or idx > nS - 2: continue jobpath = findjobdir(self.jobpath, m['tag']) if jobpath == None: sys.stdout.write('{}, static: {:>2}, qha_phonon: {:<1.1s}, {}\n'\ .format(m, nS, str(qha_phonon_success), phases[i])) else: sys.stdout.write('{}, static: {:>2}, qha_phonon: {:<1.1s}, {},{}\n'\ .format(m, nS, str(qha_phonon_success), phases[i],jobpath)) self.tags.append({'tag': m['tag'], 'phasename': phases[i]}) sys.stdout.write ('\n({},{})/{} (qha, qha_phonon) entries returned under the given searching conditions.\n'\ .format(total_qha, total_qha_phonon, total))