def add_random(self, random_probability=0.3): """ Add one random structure to the population """ structure = Structure() if self.composition is None: raise ValueError('No composition associated to this population') comp = self.composition.composition.copy() rnd = random.random() natom_limit = self.max_comp_mult * self.composition.natom / self.composition.gcd condition = { 'structure.nspecies': self.composition.nspecies, 'structure.natom': { '$lte': natom_limit } } if self.pcdb_source is None or self.pcdb_source.entries.find( condition).count() <= len(self.source_blacklist): rnd = 0 origin = None if self.pcdb_source is None or rnd < random_probability or self.composition.nspecies > 1: pcm_log.debug('Random Structure') factor = np.random.randint(self.min_comp_mult, self.max_comp_mult + 1) for i in comp: comp[i] *= factor structure = Structure.random_cell(comp, method='stretching', stabilization_number=5, nparal=5, periodic=True) else: pcm_log.debug('From source') while True: entry = None condition['properties.spacegroup'] = random.randint(1, 230) print('Trying', condition['properties.spacegroup']) for ientry in self.pcdb_source.entries.find(condition): if ientry['_id'] not in self.source_blacklist: entry = ientry break if entry is not None: origin = entry['_id'] structure = self.pcdb_source.get_structure(entry['_id']) factor = covalent_radius( self.composition.species[0]) / covalent_radius( structure.species[0]) print('From source: %s Spacegroup: %d Scaling: %7.3f' % (structure.formula, entry['properties']['spacegroup'], factor)) structure.set_cell( np.dot(factor * np.eye(3), structure.cell)) structure.symbols = structure.natom * self.composition.species self.source_blacklist.append(entry['_id']) break return self.new_entry(structure), origin
def add_random(self, random_probability=0.3): """ Add one random structure to the population """ entry_id = None structure = Structure() if self.composition is None: raise ValueError('No composition associated to this population') factor = np.random.randint(self.min_comp_mult, self.max_comp_mult + 1) comp = self.composition.composition.copy() #print("Initial composition: %s" % comp) #print(Composition(comp)) #print(Composition(comp).symbols) for i in comp: comp[i] *= factor new_comp = Composition(comp) while True: rnd = random.random() condition = { 'structure.nspecies': new_comp.nspecies, 'structure.natom': new_comp.natom } if self.pcdb_source is None: rnd = 0 elif len(self.sources[factor]) == 0: rnd = 0 if self.pcdb_source is None or rnd < random_probability: pcm_log.debug('Random Structure') structure = Structure.random_cell(new_comp, method='stretching', stabilization_number=5, nparal=5, periodic=True) break else: pcm_log.debug('From source') entry_id = self.sources[factor][np.random.randint( 0, len(self.sources[factor]))] structure = self.pcdb_source.get_structure(entry_id) print("chosen structure from database =", structure) sym = CrystalSymmetry(structure) scale_factor = float( np.max(covalent_radius(new_comp.species)) / np.max(covalent_radius(structure.species))) reduce_scale = scale_factor**(1. / 3) # WIH msg = 'Mult: %d natom: %d From source: %s Spacegroup: %d Scaling: %7.3f' print(msg % (factor, structure.natom, structure.formula, sym.number(), scale_factor)) # structure.set_cell(np.dot(scale_factor * np.eye(3), structure.cell)) # WIH structure.set_cell( np.dot(reduce_scale * np.eye(3), structure.cell)) # WIH print("symbols before change = ", structure.symbols) structure.symbols = new_comp.symbols print("symbols after change = ", structure.symbols) self.sources[factor].remove(entry_id) break return self.new_entry(structure), entry_id
def replace_failed(self): for entry in self.entries.find({'status.relaxation': 'failed'}): st = self.get_structure(entry['_id']) comp = st.composition new_structure = Structure.random_cell(comp) self.entries.update({'_id': entry['_id']}, {'$unset': {'status.relaxation': 1, 'status.target_forces': 1, 'properties.energy': 1, 'properties.forces': 1, 'properties.stress': 1}}) self.update(entry['_id'], structure=new_structure)
def add_random(self, random_probability=0.3): """ Add one random structure to the population """ entry_id = None structure = Structure() if self.composition is None: raise ValueError('No composition associated to this population') factor = np.random.randint(self.min_comp_mult, self.max_comp_mult + 1) comp = self.composition.composition.copy() # print("Initial composition: %s" % comp) # print(Composition(comp)) # print(Composition(comp).symbols) for i in comp: comp[i] *= factor new_comp = Composition(comp) while True: rnd = random.random() condition = {'structure.nspecies': new_comp.nspecies, 'structure.natom': new_comp.natom} if self.pcdb_source is None: rnd = 0 elif len(self.sources[factor]) == 0: rnd = 0 if self.pcdb_source is None or rnd < random_probability: pcm_log.debug('Random Structure') structure = Structure.random_cell(new_comp, method='stretching', stabilization_number=5, nparal=5, periodic=True) break else: pcm_log.debug('From source') entry_id = self.sources[factor][np.random.randint(0, len(self.sources[factor]))] structure = self.pcdb_source.get_structure(entry_id) print("chosen structure from database =", structure) sym = CrystalSymmetry(structure) scale_factor = float(np.max(covalent_radius(new_comp.species)) / np.max(covalent_radius(structure.species))) reduce_scale = scale_factor ** (1. / 3) # WIH msg = 'Mult: %d natom: %d From source: %s Spacegroup: %d Scaling: %7.3f' print(msg % (factor, structure.natom, structure.formula, sym.number(), scale_factor)) # structure.set_cell(np.dot(scale_factor * np.eye(3), structure.cell)) # WIH structure.set_cell(np.dot(reduce_scale * np.eye(3), structure.cell)) # WIH print("symbols before change = ", structure.symbols) structure.symbols = new_comp.symbols print("symbols after change = ", structure.symbols) self.sources[factor].remove(entry_id) break return self.new_entry(structure), entry_id
def add_random(self, random_probability=0.3): """ Add one random structure to the population """ structure = Structure() if self.composition is None: raise ValueError('No composition associated to this population') comp = self.composition.composition.copy() rnd = random.random() natom_limit = self.max_comp_mult * self.composition.natom / self.composition.gcd condition = {'structure.nspecies': self.composition.nspecies, 'structure.natom': {'$lte': natom_limit}} if self.pcdb_source is None or self.pcdb_source.entries.find(condition).count() <= len(self.source_blacklist): rnd = 0 origin = None if self.pcdb_source is None or rnd < random_probability or self.composition.nspecies > 1: pcm_log.debug('Random Structure') factor = np.random.randint(self.min_comp_mult, self.max_comp_mult + 1) for i in comp: comp[i] *= factor structure = Structure.random_cell(comp, method='stretching', stabilization_number=5, nparal=5, periodic=True) else: pcm_log.debug('From source') while True: entry = None condition['properties.spacegroup'] = random.randint(1, 230) print('Trying', condition['properties.spacegroup']) for ientry in self.pcdb_source.entries.find(condition): if ientry['_id'] not in self.source_blacklist: entry = ientry break if entry is not None: origin = entry['_id'] structure = self.pcdb_source.get_structure(entry['_id']) factor = covalent_radius(self.composition.species[0]) / covalent_radius(structure.species[0]) print('From source: %s Spacegroup: %d Scaling: %7.3f' % (structure.formula, entry['properties']['spacegroup'], factor)) structure.set_cell(np.dot(factor * np.eye(3), structure.cell)) structure.symbols = structure.natom * self.composition.species self.source_blacklist.append(entry['_id']) break return self.new_entry(structure), origin
def worker_maise(db_settings, entry_id, workdir, relaxator_params): """ Relax and return evaluate the energy of the structure stored with identifier 'entry_id' using the MAISE code :param db_settings: (dict) Dictionary of DB parameters needed to create a PyChemiaDB object :param entry_id: MongoDB identifier of one entry of the database created from db_settings :param workdir: (str) Working directory where input and output from MAISE is written :param relaxator_params: (dict) Arguments needed to control the relaxation using MAISE Arguments are store as keys and they include: 'target_forces' : Used to defined the tolerance to consider one candidate as relaxed. 'source_dir': Directory with executable maise and directory INI :return: """ max_ncalls = 6 pcdb = get_database(db_settings) target_forces = relaxator_params['target_forces'] source_dir = relaxator_params['source_dir'] pcm_log.info('[%s]: Starting relaxation. Target forces: %7.3e' % (str(entry_id), target_forces)) if pcdb.is_locked(entry_id): return else: pcdb.lock(entry_id) structure = pcdb.get_structure(entry_id) status = pcdb.get_dicts(entry_id)[2] if 'ncalls' in status and status['ncalls'] > 0: ncalls = status['ncalls'] + 1 print('ncalls = ', status['ncalls']) else: ncalls = 1 print('Verifing initial structure...') while np.min(structure.distance_matrix()+(np.eye(structure.natom)*5)) < 1.9: print('ERROR: Bad initial guess, two atoms are to close. Creating new random structure for id: %s' % str(entry_id)) write_poscar(structure, workdir + os.sep + 'Fail_initial_POSCAR') # WIH structure = Structure.random_cell(structure.composition) write_poscar(structure, workdir + os.sep + 'POSCAR') if not os.path.exists(workdir + os.sep + 'setup') and ncalls == 1: # WIH print('First run.') # WIH # print('Verifying that everything runs smoothly') # WIH print(workdir + os.sep + 'setup') shutil.copy2(source_dir + os.sep + 'setup_1', workdir + os.sep + 'setup') # WIH elif ncalls > 1: # WIH shutil.copy2(source_dir + os.sep + 'setup_2', workdir + os.sep + 'setup') # WIH if not os.path.exists(workdir + os.sep + 'INI'): os.symlink(source_dir + os.sep + 'INI', workdir + os.sep + 'INI') if not os.path.exists(workdir + os.sep + 'maise'): os.symlink(source_dir + os.sep + 'maise', workdir + os.sep + 'maise') # Get the Current Working Directory # cwd = os.getcwd() # Move to the actual directory where maise will run os.chdir(workdir) wf = open('maise.stdout', 'w') subprocess.call(['./maise'], stdout=wf) wf.close() if os.path.isfile('OSZICAR'): energies = np.loadtxt('OSZICAR') else: energies = None forces = None stress = None stress_kb = None if os.path.isfile('OUTCAR'): rf = open('OUTCAR', 'r') data = rf.read() pos_forces = re.findall(r'TOTAL-FORCE \(eV/Angst\)\s*-*\s*([-.\d\s]+)\s+-{2}', data) pos_forces = np.array([x.split() for x in pos_forces], dtype=float) if len(pos_forces) > 0 and len(pos_forces[-1]) % 7 == 0: pos_forces.shape = (len(pos_forces), -1, 7) forces = pos_forces[:, :, 3:6] # positions = pos_forces[:, :, :3] else: print('Forces and Positions could not be parsed : ', pos_forces.shape) print('pos_forces =\n%s ' % pos_forces) str_stress = re.findall('Total([.\d\s-]*)in', data) if len(str_stress) == 2: stress = np.array([[float(y) for y in x.split()] for x in str_stress]) str_stress = re.findall('in kB([.\d\s-]*)energy', data) if len(str_stress) == 2: stress_kb = np.array([[float(y) for y in x.split()] for x in str_stress]) create_new = False if not os.path.isfile('CONTCAR') or os.path.getsize("CONTCAR") == 0: create_new = True print('CONTCAR not found in entry: %s' % str(entry_id)) i = 1 while True: if not os.path.isfile('POSCAR-failed-%03s' % str(i)): os.rename('POSCAR', 'POSCAR-failed-%03s' % str(i)) break else: i += 1 else: new_structure = read_poscar('CONTCAR') # min_dist = np.min(new_structure.distance_matrix+np.ones((new_structure.natom,new_structure.natom))) min_dist = np.min(new_structure.distance_matrix()+(np.eye(new_structure.natom)*5)) # WIH print('Minimal distance= %8.7f' % min_dist) # WIH if min_dist < 2.0: print('ERROR: MAISE finished with and structure with distances too close:', entry_id) # WIH write_poscar(new_structure, workdir + os.sep + 'Collapsed_CONTCAR') # WIH create_new = True # WIH if create_new: new_structure = Structure.random_cell(structure.composition) ncalls = 0 # WIH if ncalls > max_ncalls: print('WARNING: Too many calls to MAISE and no relaxation succeeded, replacing structure: ', entry_id) # WIH new_structure = Structure.random_cell(structure.composition) pcdb.entries.update({'_id': entry_id}, {'$set': {'status.ncalls': 0}}) create_new = True else: pcdb.entries.update({'_id': entry_id}, {'$set': {'status.ncalls': ncalls}}) pcdb.update(entry_id, structure=new_structure, properties={}) # if not create_new and energies is not None and forces is not None and stress is not None: if energies is not None and forces is not None and stress is not None: te = energies[1] pcdb.entries.update({'_id': entry_id}, {'$set': {'status.relaxation': 'succeed', 'status.target_forces': target_forces, 'properties.initial_forces': generic_serializer(forces[0]), 'properties.initial_stress': generic_serializer(stress[0]), 'properties.initial_stress_kB': generic_serializer(stress_kb[0]), 'properties.forces': generic_serializer(forces[1]), 'properties.stress': generic_serializer(stress[1]), 'properties.stress_kB': generic_serializer(stress_kb[1]), 'properties.energy': te, 'properties.energy_pa': te / new_structure.natom, 'properties.energy_pf': te / new_structure.get_composition().gcd}}) for ifile in ['POSCAR', 'CONTCAR', 'setup', 'OUTCAR', 'maise.stdout', 'list.dat']: if not os.path.exists(ifile): wf = open(ifile, 'w') wf.write('') wf.close() n = 1 while True: if os.path.exists(ifile + ('_%03d' % n)): n += 1 else: break os.rename(ifile, ifile+('_%03d' % n)) pcm_log.info('[%s]: Unlocking the entry' % str(entry_id)) pcdb.unlock(entry_id)
def worker_maise(db_settings, entry_id, workdir, target_forces, relaxator_params): max_ncalls = 6 pcdb = get_database(db_settings) pcm_log.info('[%s]: Starting relaxation. Target forces: %7.3e' % (str(entry_id), target_forces)) if pcdb.is_locked(entry_id): return else: pcdb.lock(entry_id) structure = pcdb.get_structure(entry_id) status = pcdb.get_dicts(entry_id)[2] if 'ncalls' in status: ncalls = status['ncalls'] + 1 else: ncalls = 1 #print('Current directory: '+os.getcwd() ) #print('Working directory: '+workdir) write_poscar(structure,workdir+os.sep+'POSCAR') if not os.path.exists(workdir+os.sep+'setup'): shutil.copy2('setup', workdir) if not os.path.exists(workdir+os.sep+'INI'): os.symlink(os.getcwd()+os.sep+'INI', workdir+os.sep+'INI') if not os.path.exists(workdir+os.sep+'maise'): os.symlink(os.getcwd()+os.sep+'maise', workdir+os.sep+'maise') cwd=os.getcwd() os.chdir(workdir) wf=open('maise.stdout','w') subprocess.call(['./maise'], stdout=wf) wf.close() if os.path.isfile('OSZICAR'): energies=np.loadtxt('OSZICAR') else: energies=None if os.path.isfile('OUTCAR'): rf = open('OUTCAR', 'r') data = rf.read() pos_forces = re.findall(r'TOTAL-FORCE \(eV/Angst\)\s*-*\s*([-.\d\s]+)\s+-{2}', data) pos_forces = np.array([x.split() for x in pos_forces], dtype=float) if len(pos_forces) > 0 and len(pos_forces[-1]) % 7 == 0: pos_forces.shape = (len(pos_forces), -1, 7) forces = pos_forces[:, :, 3:6] positions = pos_forces[:, :, :3] else: print('Forces and Positions could not be parsed : ', pos_forces.shape) print('pos_forces =\n%s ' % pos_forces) str_stress=re.findall('Total([\.\d\s-]*)in',data) if len(str_stress)==2: stress = np.array([[float(y) for y in x.split()] for x in str_stress]) str_stress=re.findall('in kB([\.\d\s-]*)energy',data) if len(str_stress)==2: stress_kB = np.array([[float(y) for y in x.split()] for x in str_stress]) else: forces=None stress=None stress_kB=None new_structure=read_poscar('CONTCAR') if np.min(new_structure.distance_matrix()+np.eye(new_structure.natom))<0.23: print('WARNING: Structure collapse 2 atoms, creating a new random structure') new_structure=Structure.random_cell(new_structure.composition) if ncalls > max_ncalls: print('WARNING: Too many calls to MAISE and no relaxation succeeded, replacing structure') new_structure=Structure.random_cell(new_structure.composition) pcdb.entries.update({'_id': entry_id}, {'$set': {'status.ncalls': 0}}) else: pcdb.entries.update({'_id': entry_id}, {'$set': {'status.ncalls': ncalls}}) pcdb.update(entry_id, structure=new_structure) if energies is not None and forces is not None and stress is not None: te = energies[1] pcdb.entries.update({'_id': entry_id}, {'$set': {'status.relaxation': 'succeed', 'status.target_forces': target_forces, 'properties.initial_forces': generic_serializer(forces[0]), 'properties.initial_stress': generic_serializer(stress[0]), 'properties.initial_stress_kB': generic_serializer(stress_kB[0]), 'properties.forces': generic_serializer(forces[1]), 'properties.stress': generic_serializer(stress[1]), 'properties.stress_kB': generic_serializer(stress_kB[1]), 'properties.energy': te, 'properties.energy_pa': te / new_structure.natom, 'properties.energy_pf': te / new_structure.get_composition().gcd}}) for ifile in ['POSCAR', 'CONTCAR', 'setup', 'OUTCAR', 'maise.stdout', 'list.dat']: if not os.path.exists(ifile): wf = open(ifile, 'w') wf.write('') wf.close() n=1 while True: if os.path.exists(ifile+ ('_%03d' % n)): n+=1 else: break os.rename(ifile,ifile+('_%03d' % n)) pcm_log.info('[%s]: Unlocking the entry' % str(entry_id)) pcdb.unlock(entry_id)
def worker_maise(db_settings, entry_id, workdir, relaxator_params): """ Relax and return evaluate the energy of the structure stored with identifier 'entry_id' using the MAISE code :param db_settings: (dict) Dictionary of DB parameters needed to create a PyChemiaDB object :param entry_id: MongoDB identifier of one entry of the database created from db_settings :param workdir: (str) Working directory where input and output from MAISE is written :param relaxator_params: (dict) Arguments needed to control the relaxation using MAISE Arguments are store as keys and they include: 'target_forces' : Used to defined the tolerance to consider one candidate as relaxed. 'source_dir': Directory with executable maise and directory INI :return: """ max_ncalls = 6 pcdb = get_database(db_settings) target_forces = relaxator_params['target_forces'] source_dir = relaxator_params['source_dir'] pcm_log.info('[%s]: Starting relaxation. Target forces: %7.3e' % (str(entry_id), target_forces)) if pcdb.is_locked(entry_id): return else: pcdb.lock(entry_id) structure = pcdb.get_structure(entry_id) status = pcdb.get_dicts(entry_id)[2] if 'ncalls' in status and status['ncalls'] > 0: ncalls = status['ncalls'] + 1 print('ncalls = ', status['ncalls']) else: ncalls = 1 print('Verifing initial structure...') while np.min(structure.distance_matrix() + (np.eye(structure.natom) * 5)) < 1.9: print( 'ERROR: Bad initial guess, two atoms are to close. Creating new random structure for id: %s' % str(entry_id)) write_poscar(structure, workdir + os.sep + 'Fail_initial_POSCAR') # WIH structure = Structure.random_cell(structure.composition) write_poscar(structure, workdir + os.sep + 'POSCAR') if not os.path.exists(workdir + os.sep + 'setup') and ncalls == 1: # WIH print('First run.') # WIH # print('Verifying that everything runs smoothly') # WIH print(workdir + os.sep + 'setup') shutil.copy2(source_dir + os.sep + 'setup_1', workdir + os.sep + 'setup') # WIH elif ncalls > 1: # WIH shutil.copy2(source_dir + os.sep + 'setup_2', workdir + os.sep + 'setup') # WIH if not os.path.exists(workdir + os.sep + 'INI'): os.symlink(source_dir + os.sep + 'INI', workdir + os.sep + 'INI') if not os.path.exists(workdir + os.sep + 'maise'): os.symlink(source_dir + os.sep + 'maise', workdir + os.sep + 'maise') # Get the Current Working Directory # cwd = os.getcwd() # Move to the actual directory where maise will run os.chdir(workdir) wf = open('maise.stdout', 'w') subprocess.call(['./maise'], stdout=wf) wf.close() if os.path.isfile('OSZICAR'): energies = np.loadtxt('OSZICAR') else: energies = None forces = None stress = None stress_kb = None if os.path.isfile('OUTCAR'): rf = open('OUTCAR', 'r') data = rf.read() pos_forces = re.findall( r'TOTAL-FORCE \(eV/Angst\)\s*-*\s*([-.\d\s]+)\s+-{2}', data) pos_forces = np.array([x.split() for x in pos_forces], dtype=float) if len(pos_forces) > 0 and len(pos_forces[-1]) % 7 == 0: pos_forces.shape = (len(pos_forces), -1, 7) forces = pos_forces[:, :, 3:6] # positions = pos_forces[:, :, :3] else: print('Forces and Positions could not be parsed : ', pos_forces.shape) print('pos_forces =\n%s ' % pos_forces) str_stress = re.findall('Total([.\d\s-]*)in', data) if len(str_stress) == 2: stress = np.array([[float(y) for y in x.split()] for x in str_stress]) str_stress = re.findall('in kB([.\d\s-]*)energy', data) if len(str_stress) == 2: stress_kb = np.array([[float(y) for y in x.split()] for x in str_stress]) create_new = False if not os.path.isfile('CONTCAR') or os.path.getsize("CONTCAR") == 0: create_new = True print('CONTCAR not found in entry: %s' % str(entry_id)) i = 1 while True: if not os.path.isfile('POSCAR-failed-%03s' % str(i)): os.rename('POSCAR', 'POSCAR-failed-%03s' % str(i)) break else: i += 1 else: new_structure = read_poscar('CONTCAR') # min_dist = np.min(new_structure.distance_matrix+np.ones((new_structure.natom,new_structure.natom))) min_dist = np.min(new_structure.distance_matrix() + (np.eye(new_structure.natom) * 5)) # WIH print('Minimal distance= %8.7f' % min_dist) # WIH if min_dist < 2.0: print( 'ERROR: MAISE finished with and structure with distances too close:', entry_id) # WIH write_poscar(new_structure, workdir + os.sep + 'Collapsed_CONTCAR') # WIH create_new = True # WIH if create_new: new_structure = Structure.random_cell(structure.composition) ncalls = 0 # WIH if ncalls > max_ncalls: print( 'WARNING: Too many calls to MAISE and no relaxation succeeded, replacing structure: ', entry_id) # WIH new_structure = Structure.random_cell(structure.composition) pcdb.entries.update({'_id': entry_id}, {'$set': {'status.ncalls': 0}}) create_new = True else: pcdb.entries.update({'_id': entry_id}, {'$set': { 'status.ncalls': ncalls }}) pcdb.update(entry_id, structure=new_structure, properties={}) # if not create_new and energies is not None and forces is not None and stress is not None: if energies is not None and forces is not None and stress is not None: te = energies[1] pcdb.entries.update({'_id': entry_id}, { '$set': { 'status.relaxation': 'succeed', 'status.target_forces': target_forces, 'properties.initial_forces': generic_serializer(forces[0]), 'properties.initial_stress': generic_serializer(stress[0]), 'properties.initial_stress_kB': generic_serializer( stress_kb[0]), 'properties.forces': generic_serializer(forces[1]), 'properties.stress': generic_serializer(stress[1]), 'properties.stress_kB': generic_serializer(stress_kb[1]), 'properties.energy': te, 'properties.energy_pa': te / new_structure.natom, 'properties.energy_pf': te / new_structure.get_composition().gcd } }) for ifile in [ 'POSCAR', 'CONTCAR', 'setup', 'OUTCAR', 'maise.stdout', 'list.dat' ]: if not os.path.exists(ifile): wf = open(ifile, 'w') wf.write('') wf.close() n = 1 while True: if os.path.exists(ifile + ('_%03d' % n)): n += 1 else: break os.rename(ifile, ifile + ('_%03d' % n)) pcm_log.info('[%s]: Unlocking the entry' % str(entry_id)) pcdb.unlock(entry_id)