def check_relax_ML_dist(): g0 = ase_read("POSCAR") g1 = ase_read("run.final/CONTCAR") # Compute layer thickness t0 = max(g0.positions[:,2]) - min(g0.positions[:,2]) t1 = max(g1.positions[:,2]) - min(g1.positions[:,2]) # Compute distance between ML d0 = g0.cell[2,2] - t0 d1 = g1.cell[2,2] - t1 print("d0=%.3f Ang" % d0, "d1=%.3f Ang" % d1) print("Diff=%5.2g Ang reldiff=%5.2g%%" % (d0-d1, 100*(d0-d1)/d0)) return d0, d1
def Minimisation_Function(cluster, collection, cluster_name): ####################################################################################### cluster.pbc = True # make sure that the periodic boundry conditions are set off ####################################################################################### # Perform the local optimisation method on the cluster. original_path = os.getcwd() offspring_name = str(cluster_name) clusters_to_make_name = 'clusters_for_VASP' if not os.path.exists(clusters_to_make_name): os.makedir(clusters_to_make_name) copyfile('VASP_Files/INCAR', clusters_to_make_name + '/' + offspring_name + '/INCAR') copyfile('VASP_Files/POTCAR', clusters_to_make_name + '/' + offspring_name + '/POTCAR') copyfile('VASP_Files/KPOINTS', clusters_to_make_name + '/' + offspring_name + '/KPOINTS') os.chdir(clusters_to_make_name + '/' + offspring_name) ase_write(cluster, 'POSCAR', 'vasp') startTime = time.time() try: Popen(['srun', 'vasp']) except Exception: pass endTime = time.time() cluster = ase_read('OUTCAR') os.chdir(original_path) #################################################################################################################### # Write information about the algorithm Info = {} Info["INFO.txt"] = '' #Info["INFO.txt"] += ("No of Force Calls: " + str(dyn.get_number_of_steps()) + '\n') Info["INFO.txt"] += ("Time (s): " + str(endTime - startTime) + '\n') #Info["INFO.txt"] += ("Cluster converged?: " + str(dyn.converged()) + '\n') #################################################################################################################### return cluster, converged, Info
def read_chrg(genfname='geoinp.gen', chrg_fname='charges.dat', hsdfname='dftb_in.hsd', ): ''' Prerequisites: * charges.dat to be read; * correspondence ``.gen`` * input ``dftb_in.hsd`` Intermediates: max_orbital_dict: { 'Au':'d', O:'p', ... } ''' # read chem_symbol:max_orbital from HSD input hsd = HSD(hsdfname) max_orbital_dict = hsd.get_nestkeys()['Hamiltonian']['MaxAngularMomentum'] # read chem_symbol list from .gen file atoms = ase_read(filename=genfname, format='gen') chemsymbol_list = atoms.get_chemical_symbols() # read charges.dat with open(chrg_fname, 'r') as chrgf: # read header and transform strings into proper type headline = chrgf.readline() version, t_blockcharges, t_imaginaryblock, nspin, checksum = headline.split() version, t_blockcharges, t_imaginaryblock, nspin, checksum = int(version), t_blockcharges=='T', t_imaginaryblock=='T', int(nspin), float(checksum) if t_blockcharges or t_imaginaryblock: sys.exit("Reading "+chrg_fname+", not implemented case for current 'tBlockCharges tImaginaryBlock'.") chrg_list = [] for chemsymbol in chemsymbol_list: n_chrgentry = orb_chrg_entry[max_orbital_dict[chemsymbol]] chrg_str = np.fromfile(chrgf, count=n_chrgentry, sep=' ') chrg_list.append( float(chrg_str[0]) ) chrg_arry = np.array(chrg_list) return chrg_arry
def read(file_path, input_format=None): if input_format is None: input_format = os.path.splitext(file_path)[1][1:] # Read molecule file atoms = ase_read(file_path, format=input_format) # Get atom coordinates and names atom_coors = atoms.get_positions() atom_numbers = atoms.numbers atom_names = atoms.get_chemical_symbols() all_atoms = [] for c, n in zip(atom_coors, atom_numbers): all_atoms.append((c[0], c[1], c[2], n)) # Get unit cell parameters (converting from unit cell vectors) uc = ase_cellpar(atoms.cell) molecule = { 'uc_size': [uc[0], uc[1], uc[2]], 'uc_angle': [uc[3], uc[4], uc[5]], 'atom_names': atom_names, 'atom_coors': atom_coors, 'atoms': all_atoms } return atoms, molecule
def __init__(self,surface_details): # Details about the surface if surface_details in [None,'none','None', {}]: self.surface = None self.place_cluster_where = None elif isinstance(surface_details['surface'], Atoms): self.surface = surface_details['surface'] elif isinstance(surface_details['surface'], str): self.surface = ase_read(surface_details['surface']) else: exit('Error') if not self.surface == None: self.surface.center() self.place_cluster_where = surface_details['place_cluster_where']
def import_surface(surface): """ This method is designed to import the surface into the genetic algorithm :param surface: This is the surface that the cluster is optimised upon. :type surface: str/ASE.Atoms. """ if isinstance(surface, Atoms): pass elif isinstance(surface, str): surface = ase_read(surface) elif surface is None: surface = None else: print('Surface error in Cluster') exit('Error') for index in range(len(surface)): surface[index].type = 'surface' surface.centre() return surface
def read(filename: str): """ Reads cluster space from filename. Parameters --------- filename name of file from which to read cluster space """ if isinstance(filename, str): tar_file = tarfile.open(mode='r', name=filename) else: tar_file = tarfile.open(mode='r', fileobj=filename) # read items items = pickle.load(tar_file.extractfile('items')) # read structure temp_file = tempfile.NamedTemporaryFile() temp_file.write(tar_file.extractfile('atoms').read()) temp_file.seek(0) structure = ase_read(temp_file.name, format='json') tar_file.close() # ensure backward compatibility if 'symprec' not in items: # pragma: no cover items['symprec'] = 1e-5 if 'position_tolerance' not in items: # pragma: no cover items['position_tolerance'] = items['symprec'] cs = ClusterSpace(structure=structure, cutoffs=items['cutoffs'], chemical_symbols=items['chemical_symbols'], symprec=items['symprec'], position_tolerance=items['position_tolerance']) for indices in items['pruning_history']: cs._prune_orbit_list(indices) return cs
def read(file_path, input_format=None): if input_format is None: input_format = os.path.splitext(file_path)[1][1:] # Read molecule file atoms = ase_read(file_path, format=input_format) # Get atom coordinates and names atom_coors = atoms.get_positions() atom_names = atoms.get_chemical_symbols() # Get unit cell parameters (converting from unit cell vectors) uc = ase_cellpar(atoms.cell) # Additional unit cell information (currently not used) sg = atoms.info['spacegroup'].symbol volume = atoms.get_volume() molecule = {'uc_size': [uc[0], uc[1], uc[2]], 'uc_angle': [uc[3], uc[4], uc[5]], 'atom_names': atom_names, 'atom_coors': atom_coors} return atoms, molecule
def Place_Already_Created_Clusters_In_Population( population, cluster_makeup, Minimisation_Function, vacuum_to_add_length, r_ij, rounding_criteria, surface, memory_operator, predation_operator, fitness_operator, previous_cluster_name): """ This method will place any user created clusters into the population. :param population: The population :type population: Organisms.GA.Population :param cluster_makeup: The makeup of the cluster :type cluster_makeup: dict. :param Minimisation_Function: The minimisation function :type Minimisation_Function: __func__ :param vacuum_to_add_length: The amount of vacuum to place around the cluster :type vacuum_to_add_length: float :param r_ij: The maximum distance that should be between atoms to be considered bonded. This value should be as large a possible, to reflected the longest bond possible between atoms in the cluster. :type r_ij: float :param rounding_criteria: The number of decimal places to round the energy of clusters to. :type rounding_criteria: int :param surface: This is the surface that the cluster is placed on. None means there is no surface. :type surface: Organisms.GA.Surface :param memory_operator: The memory operator :type memory_operator: Organisms.GA.Memory_Operator :param predation_operator: This is the predation operator :type predation_operator: Organisms.GA.Predation_Operator :param fitness_operator: This is the fitness operator :type fitness_operator: Organisms.GA.Fitness_Operator :param previous_cluster_name: This is the name of the last cluster created in the genetic algorithm. :type previous_cluster_name: int :returns The name of the most recently created cluster by this method in the population. :rtype int """ print( 'Checking to see if the user has any clusters in the folder ' + str(population.user_initialised_population_folder) + ' that the user would like to have inputted into the initalise population.' ) # First, the definition will check the clusters if there is a population folder. If not, the user does not want to add any clusters to the initial population. if population.user_initialised_population_folder == None or population.user_initialised_population_folder == '': return 0 if not os.path.exists(population.user_initialised_population_folder): print( 'Error in def Place_Already_Created_Clusters_In_Population of class Population, in Population.py.' ) print( 'Yo, The path to the Intialised Population folder that you have specified does not exist.' ) print('Check the path to this folder exists: ' + str(population.user_initialised_population_folder)) print( 'Note: This variable should include the folder itself as well in the path.' ) exit('This program will end without doing anything.') ############################################################################################################################################## print( '################################################################################################################' ) print( '################################################################################################################' ) print('Will now initalise the population with clusters from:') print(str(population.user_initialised_population_folder)) print( '################################################################################################################' ) print( '################################################################################################################' ) ############################################################################################################################################## # The folder nammed by the variable self.user_initialised_population_folder exists. Lets get whatever clusters are inside this folder. cluster_names = [ ] # holds the cluster dir values, which are equivalent to the number of the folder for cluster_name in os.listdir( population.user_initialised_population_folder): if cluster_name.startswith('.'): continue # If the cluster folder exists and the folder is numbered properly, then it is a cluster to be added into the GA program if os.path.isdir( os.path.join(population.user_initialised_population_folder, cluster_name)) and cluster_name.isdigit(): cluster_names.append(int(cluster_name)) else: print( 'Error in def Place_Already_Created_Clusters_In_Population of class Population, in Population.py.' ) print('Check the Initialised Population folder ' + str(population.user_initialised_population_folder) + '. There is a folder which is not an integer') print( 'All folders must be called by an integer as this is how the program names each cluster.' ) print( 'Check the folder in ' + str(population.user_initialised_population_folder) + ' to make sure all folders are clusters with integer labels.') print( 'The folder that the GA is specifically having problems with is called ' + str(cluster_name)) print('This program will end without doing anything.') exit() cluster_names.sort() ############################################################################################################################################## # The clusters that the user has put into the population currently should be in sequential order starting from 1. def is_list_sequential(mylist): for index in range(0, len(mylist) - 1): if not mylist[index + 1] == mylist[index] + 1: return False return True if cluster_names == []: print( 'Error in def Place_Already_Created_Clusters_In_Population of class Population, in Population.py.' ) print( 'There are no clusters in the Initialised Population in the path: ' + str(population.user_initialised_population_folder)) print('Check the Initialised Population folder ' + str(population.user_initialised_population_folder)) print('This program will end without doing anything.') exit() elif not 1 in cluster_names: print( 'Error in def Place_Already_Created_Clusters_In_Population of class Population, in Population.py.' ) print( 'One of the clusters in your already created population must have a dir/folder name that is the name "1"' ) print('Check the cluster folders that are in the directory ' + str(population.user_initialised_population_folder)) print('Folder names in ' + str(population.user_initialised_population_folder) + ' folder: ' + str(cluster_names)) print('This program will end without doing anything.') exit() elif not is_list_sequential(cluster_names): print( 'Error in def Place_Already_Created_Clusters_In_Population of class Population, in Population.py.' ) print( 'The names of the clusters/folders in the population folder "' + str(population.user_initialised_population_folder) + '" must be named by numbers in sequential order starting from "1"') print('Check the cluster folders that are in the directory ' + str(population.user_initialised_population_folder)) print('Folder names in ' + str(population.user_initialised_population_folder) + ' folder: ' + str(cluster_names)) print('This program will end without doing anything.') exit() ############################################################################################################################################## # The clusters will now be locally minimised as described by the def Minimisation_Function for cluster_name in cluster_names: ########################################## # Should be ok, but lets just double check at least that the clusters dir is between 1 and self.size if not cluster_name > 0: print( 'Error in def Place_Already_Created_Clusters_In_Population of class Population, in Population.py.' ) print('The dir name of this cluster is less than 0.') print('Dir of this cluster: ' + str(cluster_name)) print('Cluster is located in ' + str(population.user_initialised_population_folder + '/' + str(cluster_name))) print('Check this out') import pdb pdb.set_trace() exit() elif not cluster_name < population.size + 1: toStringError = '******************************************************************\n' toStringError += 'There are more clusters in ' + str( population.user_initialised_population_folder ) + ' than allowed in this population.\n' toStringError += 'The population size for this genetic algorithm run is: ' + str( population.size) + '\n' toStringError += 'What we are going to do is not import any of the other clusters in the population, only keeping the following clusters\n' included_clusters_in_pop = population.get_cluster_names() toStringError += str(included_clusters_in_pop) + '\n' toStringError += 'We will exclude the following clusters from the population.\n' excluded_clusters_in_pop = [] for cluster_name in cluster_names: if not cluster_name in included_clusters_in_pop: excluded_clusters_in_pop.append(cluster_name) toStringError += str(excluded_clusters_in_pop) + '\n' toStringError += 'The genetic algorithm will continue. I hope you know what you are doing.\n' toStringError += '******************************************************************\n' print(toStringError) print(toStringError, file=sys.stderr) break ########################################## print('*****************************') xyz_files = [ file for file in os.listdir( os.path.join(population.user_initialised_population_folder, str(cluster_name))) if file.endswith('.xyz') ] # Check to find the xyz file that will be used. This xyz file will be considered unoptimsed if len(xyz_files) == 0: print( 'Check cluster ' + str(cluster_name) + '. There is no .xyz file. The prorgram requires a .xyz file of this cluster so it knows the positions of the atoms in the cluster.' ) exit() # File the cluster_name_UnOpt.xyz file. If it cant be found. Tell the user that it doesnt exist # and that this needs to be resolved by them. Note: we want to obtained the "_UnOpt.xyz" file, # NOT the _Opt.xyz. This is because we will be optimising it later, and for ease of the programming. # I know you might have already use the get_newly initialised_population.py program to get both the # unoptimised and optimised clusters, so you have already optimised the cluster, but this is just easier # overall. Take the optimised cluster obtained during the initialised_population.py program as a test to mkae sure # that the unoptimised cluster will not explode before it is used in the GA program. If you want to, you can change # this to use the "_Opt.xyz" file, and it probably wont be an issue. You may have to do some changing of the program, # but im sure it will be fine to do. for xyz_file in xyz_files: if str(cluster_name) + '_UnOpt.xyz' == xyz_file: break else: print( 'Error in def Place_Already_Created_Clusters_In_Population of class Population, in Population.py.' ) print('There is no .xyz file in Cluster (folder) ' + str(cluster_name) + ' called " ' + str(cluster_name) + '_Opt.xyz.') print( 'This is the cluster xyz file that is used by the genetic algorithm to represent this cluster, as it is assume that it is that cluster, and the "_Opt" indicates that it has been locally optimised.' ) print('Check folder ' + str(cluster_name) + ' in ' + str(population.user_initialised_population_folder) + ' for the file "' + str(str(cluster_name) + '_Opt.xyz') + '".') print('Ending the genetic algorithm without doing anything.') exit() # Performed all checks, will now add cluster print('Adding Cluster ' + str(cluster_name) + ' to the population.') UnOpt_Cluster = ase_read( population.user_initialised_population_folder + '/' + str(cluster_name) + '/' + xyz_file) print( 'Will locally optimise it as described by your def Minimisation_Function.' ) stdout = sys.stdout output = StringIO() sys.stdout = output Opt_Cluster, converged, opt_information = Minimisation_Function( UnOpt_Cluster.copy(), population.name, cluster_name) # note, self.name is the population name sys.stdout = stdout opt_information['output.txt'] = output.getvalue() if Exploded( Opt_Cluster, max_distance_between_atoms=r_ij ): # make sure the randomised cluster has not split up when optimised. print( 'Error in def Place_Already_Created_Clusters_In_Population of class Population, in Population.py.' ) print("The optimised Cluster exploded. Check out why cluster " + str(cluster_name) + ' has exploded by looking at cluster ' + str(cluster_name) + 's AfterOpt.traj file.') print( 'The program will now show the optimised and the unoptimised versions of cluster ' + str(cluster_name)) from ase.visualize import view view(UnOpt_Cluster) view(Opt_Cluster) print('This program will end without doing anything.') exit() elif not converged: print( 'Error in def Place_Already_Created_Clusters_In_Population of class Population, in Population.py.' ) print( "This Cluster did not converge when optimised. Check out why cluster " + str(cluster_name) + ' did not converge by at the following output from the stdout.' ) print('*******************************************************') print(opt_information['output.txt']) print('*******************************************************') print( 'The program will also show the optimised and the unoptimised versions of cluster ' + str(cluster_name)) from ase.visualize import view view(UnOpt_Cluster) view(Opt_Cluster) print('This program will end without doing anything.') exit() elif memory_operator.is_similar_cluster_in_memory_operator( Opt_Cluster): print( 'Error in def Place_Already_Created_Clusters_In_Population, in Initialise_Population.py.' ) print( "Cluster " + str(cluster_name) + ' in the population is too similar to one of the clusters in your memory operator database.' ) print( 'Check the clusters that you have included in your population and in the Memory Operator database' ) print( 'The program will now show the optimised version of cluster ' + str(cluster_name)) from ase.visualize import view view(Opt_Cluster) print('This program will end without doing anything.') exit() initialised_cluster = Cluster(Opt_Cluster) initialised_cluster.verify_cluster(cluster_name, 0, vacuum_to_add_length, rounding_criteria) initialised_cluster.remove_calculator() # Set some of the details about the cluster that are needed for the population collection to save data to the population database. initialised_cluster.ever_in_population = True initialised_cluster.excluded_because_violates_predation_operator = False initialised_cluster.initial_population = True initialised_cluster.removed_by_memory_operator = False # Make sure this imported cluster has the correct chemical makeup for this genetic algorithm run. if not initialised_cluster.get_elemental_makeup() == cluster_makeup: print( 'Error in def Place_Already_Created_Clusters_In_Population of class Population, in Population.py.' ) print('Cluster ' + str(cluster_name) + ' does not contain the correct chemical makeup.') print('Cluster ' + str(cluster_name) + ' makeup: ' + str(initialised_cluster.get_elemental_makeup())) print('Desired makeup: ' + str(cluster_makeup)) print('Check this out.') print('This program will end without doing anything') exit() index = cluster_name - 1 print( 'Adding Cluster ' + str(cluster_name) + ' that the user wants to placed into the population of the GA, before the generation cycles of the GA begins.' ) population.add( index, initialised_cluster ) # Place the initialised cluster into the population ## self.Add_ClusterToPopulation(initialised_cluster,index,UnOpt_Cluster=UnOpt_Cluster,Optimisation_Information=opt_information) print('*****************************') ############################################################################################################################################## # Check to make sure that the clusters that have been entered in by the user do not violate the diversity scheme. #clusters_removed_from_pop, removed_clusters_report = Fitness_Factor_Scheme.Check_Initial_Population(return_report=True) fitness_operator.assign_initial_population_fitnesses() clusters_to_create, removed_clusters_report = Check_Population_against_predation_operator( population, predation_operator) #import pdb; pdb.set_trace() if not len(clusters_to_create) == 0: #len(clusters_removed_from_pop): print( 'Error in def Place_Already_Created_Clusters_In_Population of class Population, in Population.py.' ) print( 'Some of the clusters that you have placed in the population folder violate the Diversity operator: ' + str(predation_operator.Predation_Switch)) print( 'Here is a list of the clusters that are the same as other clusters in this population:' ) for cluster_kept_dir, clusters_removed_dirs in sorted( removed_clusters_report, key=lambda x: x[0]): clusters_removed_dirs.insert(0, cluster_kept_dir) print('Clusters: ' + str(clusters_removed_dirs) + ' are considered identical by this diversity operator.') print('Check this out.') print('This program will end without doing anything') exit() ############################################################################################################################################## print('Finished importing clusters from ' + str(population.user_initialised_population_folder) + ' into the population') return population[-1].name
def plot_displ_CLI(argv): """Command Line Wrapper for plot displacement Python function.""" # ------------------------------------------------------------------------------- # Argument parser # ------------------------------------------------------------------------------- parser = argparse.ArgumentParser(description=plot_displ_CLI.__doc__) # Positional arguments # Optional args parser.add_argument('-s', '--start', dest='start', default="POSCAR", help='starting geometry (def: POSCAR);') parser.add_argument('-e', '--end', dest='end', default="CONTCAR", help='ending geometry (def: CONTCAR);') parser.add_argument( '-l', '--len', dest='v_len', type=float, default=1.0, help='scaling factor for displacement vectors visualization (def: 1);') parser.add_argument('--size', dest='atm_scale', type=float, default=1.0, help='scaling factor for atom size (def: 1);') parser.add_argument('--end_pt', action='store_true', dest='show_endpt', help='show minimization endpoint') parser.add_argument('--no_norm', action='store_true', dest='norm', help='normalize vectors to same size.') parser.add_argument('--rep', dest='replica', type=int, nargs=3, default=(1, 1, 1), help='replicate systems along unit cell.') parser.add_argument('--uc', dest='unitcell', action='store_true', help='plot unit cell.') parser.add_argument('--debug', action='store_true', dest='debug', help='show debug informations.') parser.add_argument( '--mindisp', dest='mindisp', type=float, default=0.0, help='minimum displacement (in Angstrom) for arrow to be drawn') # ------------------------------------------------------------------------------- # Initialize and check variables # ------------------------------------------------------------------------------- args = parser.parse_args(argv) # Set up LOGGER c_log = logging.getLogger(__name__) # Adopted format: level - current function name - mess. Width is fixed as visual aid std_format = '[%(levelname)5s - %(funcName)10s] %(message)s' logging.basicConfig(format=std_format) c_log.setLevel(logging.INFO) # Set debug option if args.debug: c_log.setLevel(logging.DEBUG) c_log.debug(args) # ------------------------------------------------------------------------------- # Load geometry # ------------------------------------------------------------------------------- start = ase_read(args.start) del start.constraints try: start = ase_sort(start * args.replica) except Exception as e: c_log.error("Starting geom replication went wrong. Expect errors") end = ase_read(args.end) del end.constraints try: end = ase_sort(end * args.replica) except Exception as e: c_log.error("Ending geom replication went wrong. Expect errors") # ------------------------------------------------------------------------------- # Plot # ------------------------------------------------------------------------------- fig = plt.figure() fig.set_dpi(150) ax = fig.gca(projection='3d') ax = plot_displ( ax, start, end, atm_scale=args.atm_scale, # size of atoms v_len=args.v_len, normalize=args.norm, mindisp=args.mindisp, # displacement arrows plt_uc=args.unitcell, plt_endpt=args.show_endpt) # To plot or not to plot plt.show()
def read(cls, infile: Union[str, BinaryIO, TextIO], old_format: bool = False): """Reads data container from file. Parameters ---------- infile file from which to read old_format If true use old json format to read runtime data; default to false Raises ------ FileNotFoundError if file is not found (str) ValueError if file is of incorrect type (not a tarball) """ if isinstance(infile, str): filename = infile else: filename = infile.name if not tarfile.is_tarfile(filename): raise TypeError('{} is not a tar file'.format(filename)) with tarfile.open(mode='r', name=filename) as tf: # file with structures with tempfile.NamedTemporaryFile() as fobj: fobj.write(tf.extractfile('atoms').read()) fobj.flush() structure = ase_read(fobj.name, format='json') # file with reference data with tempfile.NamedTemporaryFile() as fobj: fobj.write(tf.extractfile('reference_data').read()) fobj.flush() with open(fobj.name, encoding='utf-8') as fd: reference_data = json.load(fd) # init DataContainer dc = cls(structure=structure, ensemble_parameters=reference_data['parameters']) # overwrite metadata dc._metadata = reference_data['metadata'] for tag, value in reference_data['last_state'].items(): if tag == 'random_state': value = tuple( tuple(x) if isinstance(x, list) else x for x in value) dc._last_state[tag] = value # add runtime data from file with tempfile.NamedTemporaryFile() as fobj: fobj.write(tf.extractfile('runtime_data').read()) fobj.seek(0) if old_format: runtime_data = pd.read_json(fobj) data = runtime_data.sort_index(ascending=True) dc._data_list = data.T.apply( lambda x: x.dropna().to_dict()).tolist() else: dc._data_list = np.load( fobj, allow_pickle=True)['arr_0'].tolist() dc._observables = set([key for data in dc._data_list for key in data]) dc._observables = dc._observables - {'mctrial'} return dc
def read(self, *args, **kwargs): return ase_to_pyiron(ase_read(*args, **kwargs))
def create_structure(self, results_dict, output_folder): """ create the output structure """ opt_type = results_dict.get("opt_type", None) if opt_type == "polymer": if "final_coords" not in results_dict: return None, "ERROR_STRUCTURE_PARSING" final_coords = results_dict.pop("final_coords") if "structure" not in self.node.inputs: self.logger.error("the input structure node is not set") return None, "ERROR_MISSING_INPUT_STRUCTURE" if not set(final_coords.keys()).issuperset(["id", "x", "y", "z", "label"]): self.logger.error( 'expected final_coords to contain ["id", "x", "y", "z", "label"]' ) return None, "ERROR_STRUCTURE_PARSING" if not final_coords["id"] == list(range(1, len(final_coords["id"]) + 1)): self.logger.error("the final_coords ids were not ordered 1,2,3,...") return None, "ERROR_STRUCTURE_PARSING" if ( not final_coords["label"] == self.node.inputs.structure.get_ase().get_chemical_symbols() ): self.logger.error( "the final_coords labels are != to the input structure symbols" ) return None, "ERROR_STRUCTURE_PARSING" try: validate_1d_geometry(self.node.inputs.structure) except Exception as err: self.logger.error(str(err)) return None, "ERROR_STRUCTURE_PARSING" out_structure = self.node.inputs.structure.clone() positions = [] for x, y, z in zip(final_coords["x"], final_coords["y"], final_coords["z"]): # x are fractional and y,z are cartesian positions.append([x * out_structure.cell[0][0], y, z]) out_structure.reset_sites_positions(positions) elif opt_type == "surface": self.logger.error( "creating output structures for surface optimisations has not yet been implemented" ) return None, "ERROR_STRUCTURE_PARSING" else: cif_file = self.node.get_option("out_cif_file_name") if cif_file not in output_folder.list_object_names(): self.logger.error("the output cif file is missing") return None, "ERROR_CIF_FILE_MISSING" # We do not use this method, since currently different kinds are set for each atom # see aiidateam/aiida_core#2942 # NOTE cif files are read as binary, by default, since aiida-core v1.0.0b3 # with output_folder.open(cif_file, mode="rb") as handle: # cif = DataFactory('cif')(file=handle) # structure = cif.get_structure(converter="ase") with warnings.catch_warnings(): # ase.io.read returns a warnings that can be ignored # UserWarning: crystal system 'triclinic' is not interpreted for space group 1. # This may result in wrong setting! warnings.simplefilter("ignore", UserWarning) with output_folder.open(cif_file, mode="r") as handle: atoms = ase_read(handle, index=":", format="cif")[-1] atoms.set_tags(0) if self.node.get_option("use_input_kinds"): if "structure" not in self.node.inputs: self.logger.error("the input structure node is not set") return None, "ERROR_MISSING_INPUT_STRUCTURE" in_structure = self.node.inputs.structure in_atoms = in_structure.get_ase() if in_atoms.get_chemical_symbols() != atoms.get_chemical_symbols(): self.logger.error( "the input and cif structures have different atomic configurations" ) return None, "ERROR_CIF_INCONSISTENT" out_structure = in_structure.clone() out_structure.set_cell(atoms.cell) out_structure.reset_sites_positions(atoms.positions) else: out_structure = DataFactory("structure")(ase=atoms) return out_structure, None
def build_emitter_from_file(uc_file, filename="emitter.txt", z_axis=(0,0,1), x_axis="auto", y_axis="auto", emitter_radius=100, emitter_side_height=50, vacuum_radius=25): """ Build an emitter (set of nodes, TAPSim style) based on a unit cell structure file (`uc_file`). Any file format supported by ASE can be used. """ if x_axis == "auto" and y_axis == "auto": if 0.99 < np.dot(z_axis, (1, 0, 0)) < 1.01: x_axis = tuple(np.cross(z_axis, (1, 0, 0))) else: x_axis = tuple(np.cross(z_axis, (0, 1, 0))) y_axis = tuple(np.cross(z_axis, x_axis)) R = emitter_radius + vacuum_radius atoms = ase_read(uc_file) pts = atoms.get_positions() IDS = {} elt_id = 10 for e in set(atoms.get_chemical_symbols()): IDS[e] = str(elt_id) elt_id += 10 supercell_dimensions = ( (int(math.ceil((2*R)/max([pt[0] for pt in pts])))+1, 0, 0), (0, int(math.ceil((2*R)/max([pt[1] for pt in pts])))+1, 0), (0, 0, int(math.ceil((emitter_side_height+R)/max([pt[2] for pt in pts])))+1) ) atoms = make_supercell(atoms, supercell_dimensions) pts = atoms.get_positions() cx = np.mean([pt[0] for pt in pts]) cy = np.mean([pt[1] for pt in pts]) min_z = min([pt[2] for pt in pts]) emitter_points, vacuum_points, bottom_points = [], [], [] number = 0 for atom in atoms: pt = atom.position pt = [pt[0], pt[1], pt[2]-min_z] if (pt[2] < 1e-5 and (pt[0]-cx)**2+(pt[1]-cy)**2<R**2): pt = [pt[0], pt[1], 0.0] pt = [i*1e-10 for i in pt] pt.append(2) # bottom ID number += 1 bottom_points.append(pt) elif (pt[2]<emitter_side_height and (pt[0]-cx)**2+(pt[1]-cy)**2\ <emitter_radius**2): pt = [i*1e-10 for i in pt] pt.append(IDS[atom.symbol]) # emitter ID number += 1 emitter_points.append(pt) elif (pt[0]-cx)**2+(pt[1]-cy)**2+(pt[2]-emitter_side_height)**2\ <emitter_radius**2: pt = [i*1e-10 for i in pt] pt.append(IDS[atom.symbol]) # emitter ID number += 1 emitter_points.append(pt) elif (pt[2]<emitter_side_height and (pt[0]-cx)**2+(pt[1]-cy)**2<R**2): pt = [i*1e-10 for i in pt] pt.append(0) # vacuum ID number += 1 vacuum_points.append(pt) elif (pt[0]-cx)**2+(pt[1]-cy)**2+(pt[2]-emitter_side_height)**2<R**2: pt = [i*1e-10 for i in pt] pt.append(0) # vacuum ID number += 1 vacuum_points.append(pt) with open(filename, "w") as e: n_nodes = number e.write("ASCII {} 0 0\n".format(n_nodes)) for pt in emitter_points + vacuum_points + bottom_points: # It's required that the coordinates be # separated by a tab character (^I), not # by regular spaces. e.write(" ".join([str(i) for i in pt])) e.write("\n") comment = ["#"] comment += [" {}={}".format(IDS[elt], elt) for elt in IDS] e.write("{}\n".format(" ".join(comment)))
#!/usr/bin/env python3 """Get a reasonable number of nodes Nn on Iridis5 given the size N of the system. From experience, up to 6 atoms are fine on 1 node (40CPUs) for using PBE with Encut 650eV Kdens=17x17x1. Thus, reserve Nn=ceil(N/6+1/2) nodes, up to a maximum of 4 (more than 160 CPUs VASP becoms sluggish). """ import sys, math from ase.io import read as ase_read geom = ase_read(sys.argv[1], format='vasp') Nn = math.ceil(len(geom)/6) if Nn > 4: Nn = 4 print(Nn)