コード例 #1
0
ファイル: Collection.py プロジェクト: krim4ik/Organisms
    def read_collection_database(self, database_path, current_generation=None):
        """
		This method will read the clusters in the database. Furthermore, this method is also designed to repair the collection database by removing any clusters that were created after the current generation if desired.

		:param database_path: The path to the database. 
		:type  database_path: str. 
		:param current_generation: The current generation that your genetic algorithm trial is being resumed from
		:type  current_generation: int

		returns clusters: This is a list of all the clusters from the database
		rtype   clusters: list of Organisms.GA.Clusters

		"""
        # Get clusters from database
        clusters = []
        cluster_to_delete = []
        with connect(database_path) as database:
            for cluster_row in database.select():
                if current_generation == None or cluster_row.gen_made <= current_generation:
                    ase_cluster = cluster_row.toatoms()
                    cluster = Cluster(ase_cluster)
                    cluster.custom_verify_cluster(
                        cluster_row.name, cluster_row.gen_made,
                        cluster_row.cluster_energy,
                        cluster_row.ever_in_population, cluster_row.
                        excluded_because_violates_predation_operator,
                        cluster_row.initial_population)
                    clusters.append(cluster)
                else:
                    cluster_to_delete.append(cluster.id)
        database.delete(cluster_to_delete)
        return clusters
コード例 #2
0
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
コード例 #3
0
def randomMutate(boxtoplaceinlength,
                 vacuumAdd,
                 cluster_makeup=None,
                 cluster_to_mutate=None,
                 percentage_of_cluster_to_randomise=None):
    """
	This definition provides the random method for the mutation proceedure. In this method, a cluster is
	designed by randomly placing the designed atoms of elements into a predefined unit cell.
	
	:param boxtoplaceinlength: This is the length of the box you would like to place atoms in to make a randomly constructed cluster.
	:type  boxtoplaceinlength: float
	:param vacuumAdd: The length of vacuum added around the cluster. Written in A.
	:type  vacuumAdd: float
	:param cluster_makeup: check this
	:type  cluster_makeup: {'Element': int(number of that 'element' in this cluster),...}
	:param cluster_to_mutate: If the user desired, they can tell this definition what cluster they want to mutate. Default: None.
	:type  cluster_to_mutate: ASE.Atoms
	:param percentage_of_cluster_to_randomise: This is the percentage of the number of atoms in the cluster to randomise
	:type  percentage_of_cluster_to_randomise: float
	
	:returns mutant: The description of the mutant cluster.
	:rtypes: ase.Atoms or GA.Cluster

	"""
    # Prepare the method for performing a fully ramdomly generated cluster
    if not cluster_makeup == None and (cluster_to_mutate == None
                                       or percentage_of_cluster_to_randomise
                                       == None):
        #Turn cluster_makeup into a chemical formula string
        cluster_chemical_formula = ''
        for element, no_of_element in cluster_makeup.items():
            cluster_chemical_formula += str(element + str(no_of_element))
        # set up the cluster for randomizing
        mutant = Cluster(cluster_chemical_formula)
        nAtoms = len(mutant)
        atoms_to_randomise = range(
            nAtoms)  # randomise all atoms in the cluster.
    # preparing this method for only randomly changing the position of only a percentage (of atoms) of a cluster.
    elif cluster_makeup == None and not (cluster_to_mutate == None
                                         or percentage_of_cluster_to_randomise
                                         == None):
        mutant = copy.deepcopy(cluster_to_mutate)
        nAtoms = len(mutant)
        # The following will pick random atoms in the cluster to randomise
        no_of_atoms_to_randomise = int(
            ceil(
                float(nAtoms) *
                (float(percentage_of_cluster_to_randomise) / 100.0)))
        all_atoms_in_cluster = range(nAtoms)
        atoms_to_randomise = []
        for NOTUSED in range(no_of_atoms_to_randomise):
            index_to_randomise = randint(0, len(all_atoms_in_cluster)) - 1
            atoms_to_randomise.append(all_atoms_in_cluster.pop(index))
    else:  # Error.
        print('Error in MutationProcedure: def randomMutate.')
        print('cluster_makeup = ' + str(cluster_makeup))
        print('cluster_to_mutate = ' + str(cluster_to_mutate))
        print('percentage_of_cluster_to_randomise = ' +
              str(percentage_of_cluster_to_randomise))
        import pdb
        pdb.set_trace()
        exit()
    # At this point, the method needs:
    #	* mutant: class ASE.Atoms. A framework of the cluster.
    #	* atoms_to_randomise: the index of atoms that need to be randomised, list of ints
    # setup the Atoms class to define the atoms in the cluster of mutant and define a unit cell
    # for atoms to be randomly placed in.
    # Get initial size of cell\
    mutant.set_cell(
        [boxtoplaceinlength, boxtoplaceinlength, boxtoplaceinlength])
    mutant.center()
    # For each element, place a set number of atoms of that element into the cluster in a random position
    # within the defined cube with sides = r_ij*scale
    for index in atoms_to_randomise:
        while True:
            x_position = boxtoplaceinlength * uniform(0, 1)
            y_position = boxtoplaceinlength * uniform(0, 1)
            z_position = boxtoplaceinlength * uniform(0, 1)
            position = [x_position, y_position, z_position]
            # check the cluster to see that this new random position will not cause this atom
            # to be in the same place as any atom in the cluster currently
            if not is_position_already_occupied_by_an_atom_in_Cluster(
                    position,
                    mutant,
                    atom_indices_to_exclude_from_comparison=[index]):
                # Found that one of the atoms in this cluster has the same position as this randomly generated one.
                # Will need to make a new randomly generated position list
                break
        mutant[index].position = position
    # Add a unit cell to the cluster. Mainly needed for VASP.
    lengthOfCell = 2.0 * InclusionRadiusOfCluster(mutant) + vacuumAdd
    cell = [lengthOfCell, lengthOfCell, lengthOfCell]
    mutant.set_cell(cell)
    mutant.center()  # centre the cluster within this unit cell
    # return the mutant cluster.
    #print('cluster_makeup: '+str(cluster_makeup)+'; cluster_to_mutate: '+str(cluster_to_mutate))
    if cluster_makeup == None:
        if not len(mutant) == len(cluster_to_mutate):
            print('Error in def randomMutate, in Types_Of_Mutations.py')
            print('The offspring contains ' + str(len(mutant)) +
                  ' atoms, but should contain ' + str(len(cluster_to_mutate)))
            print('Check this')
            import pdb
            pdb.set_trace()
            print('This program will finish without completing')
            exit()
    elif cluster_to_mutate == None:
        number_of_atoms = sum(Counter(cluster_makeup).values())
        #print('Atoms in mutant: '+str(len(mutant))+'; number_of_atoms = '+str(number_of_atoms))
        #import pdb; pdb.set_trace()
        if not len(mutant) == number_of_atoms:
            print('Error in def randomMutate, in Types_Of_Mutations.py')
            print('The offspring contains ' + str(len(mutant)) +
                  ' atoms, but should contain ' + str(len(cluster_to_mutate)))
            print('Check this')
            import pdb
            pdb.set_trace()
            print('This program will finish without completing')
            exit()
    return mutant
コード例 #4
0
import copy
import re
from random import randrange, uniform, randint, random
from ase import Atoms

from Organisms.GA.Types_Of_Mutations import moveMutate, homotopMutate, randomMutate
from Organisms.GA.Population import Population
from Organisms.GA.Cluster import Cluster
from Organisms.GA.ExternalDefinitions import InclusionRadiusOfCluster

population_type_example = Population('example',
                                     -1,
                                     user_initialised_population_folder=None,
                                     write_data=False)
cluster_type_example = Cluster()
atoms_type_example = Atoms()


def isfloat(element):
    """
	Is the input a float.

	:param element: some input
	:type  element: Any

	:returns True if the element can be considered a float, False if not. 
	:rtype   ebool.
	"""
    if re.match(r'^-?\d+(?:\.\d+)?$', element) is None:
        return False
    else:
コード例 #5
0
def randomMutate(boxtoplaceinlength,
                 vacuumAdd,
                 composition_constrained,
                 cluster_makeup=None,
                 cluster_to_mutate=None,
                 percentage_of_cluster_to_randomise=None):
    """
	This definition provides the random method for the mutation proceedure. In this method, a cluster is
	designed by randomly placing the designed atoms of elements into a predefined unit cell.
	
	:param boxtoplaceinlength: This is the length of the box you would like to place atoms in to make a randomly constructed cluster.
	:type  boxtoplaceinlength: float
	:param vacuumAdd: The length of vacuum added around the cluster. Written in A.
	:type  vacuumAdd: float
	:param cluster_makeup: check this
	:type  cluster_makeup: {'Element': int(number of that 'element' in this cluster),...}
	:param cluster_to_mutate: If the user desired, they can tell this definition what cluster they want to mutate. Default: None.
	:type  cluster_to_mutate: ASE.Atoms
	:param percentage_of_cluster_to_randomise: This is the percentage of the number of atoms in the cluster to randomise
	:type  percentage_of_cluster_to_randomise: float
	
	:returns mutant: The description of the mutant cluster.
	:rtypes: ase.Atoms or GA.Cluster

	"""
    # Prepare the method for performing a fully ramdomly generated cluster
    mutant = None
    atoms_to_randomise = []

    np.random.seed(int(time.time()))
    if cluster_makeup is not None and (
            cluster_to_mutate is None
            or percentage_of_cluster_to_randomise is None):
        # Turn cluster_makeup into a chemical formula string
        print('Mutant transformed', file=sys.stdout)
        cluster_chemical_formula = ''
        for element, no_of_element in cluster_makeup.items():
            if not composition_constrained:
                max_atom_change = int(np.floor(np.minimum(no_of_element, 5)))
                if max_atom_change == no_of_element:
                    max_atom_change -= 1
                if max_atom_change == 0:
                    actual_change = 0
                else:
                    actual_change = np.random.randint(low=-max_atom_change,
                                                      high=max_atom_change,
                                                      size=1,
                                                      dtype=int)[0]
                # print(element, actual_change, file=sys.stderr)
                cluster_chemical_formula += str(element) + str(
                    int(no_of_element + actual_change))
            else:
                cluster_chemical_formula += str(element) + str(
                    int(no_of_element))

        # print('genning new mutant: ', cluster_chemical_formula)
        # set up the cluster for randomizing
        mutant = Cluster(cluster_chemical_formula)
        nAtoms = len(mutant)
        atoms_to_randomise = range(
            nAtoms)  # randomise all atoms in the cluster.
    # preparing this method for only randomly changing the position of only a percentage (of atoms) of a cluster.
    elif cluster_makeup is None and not (
            cluster_to_mutate is None
            or percentage_of_cluster_to_randomise is None):
        mutant = copy.deepcopy(cluster_to_mutate)
        swapping_prob = 0.75

        # my additional idea of swapping single atoms
        # at this point we could also swap multiple atoms but this is not composition_constrained -> use else here TODO
        if composition_constrained:
            distinct_species = list()
            for s in mutant.get_chemical_symbols():
                if s not in distinct_species:
                    distinct_species.append(s)
            if len(distinct_species) > 1:
                if np.random.random() < swapping_prob:
                    symbols = dict()
                    for idx, species in enumerate(
                            mutant.get_chemical_symbols()):
                        if species not in symbols:
                            symbols[species] = []
                            symbols[species].append(idx)
                        else:
                            symbols[species].append(idx)

                    chosen = sample([s for s in symbols], 2)

                    exchange = dict()
                    for species in chosen:
                        exchange[species] = sample(symbols[species], 1)

                    pairs = list(exchange.items())

                    swapped = dict({
                        pairs[1][0]: exchange[pairs[0][0]],
                        pairs[0][0]: exchange[pairs[1][0]]
                    })
                    for species in swapped:
                        for idx in swapped[species]:
                            mutant[idx].symbol = species

                    nAtoms = len(mutant)
                    atoms_to_randomise = range(nAtoms)

                    print('Mutant swapped', file=sys.stdout)
        else:
            # The following will pick random atoms in the cluster to randomise, original code
            print('Mutant randomized', file=sys.stdout)
            nAtoms = len(mutant)
            no_of_atoms_to_randomise = int(
                np.ceil(
                    float(nAtoms) *
                    (float(percentage_of_cluster_to_randomise) / 100.0)))
            all_atoms_in_cluster = [int(ia) for ia in range(nAtoms)]
            atoms_to_randomise = []
            for NOTUSED in range(no_of_atoms_to_randomise):
                index_to_randomise = randint(0, len(all_atoms_in_cluster)) - 1
                atoms_to_randomise.append(
                    all_atoms_in_cluster.pop(index_to_randomise))

            cluster_chemical_formula = ''
            for element, no_of_element in cluster_to_mutate.get_elemental_makeup(
            ).items():
                cluster_chemical_formula += str(element) + str(
                    int(no_of_element))
            # print('randomize mutant: ', cluster_chemical_formula)

    else:  # Error
        print('Error in MutationProcedure: def randomMutate.')
        print('cluster_makeup = ' + str(cluster_makeup))
        print('cluster_to_mutate = ' + str(cluster_to_mutate))
        print('percentage_of_cluster_to_randomise = ' +
              str(percentage_of_cluster_to_randomise))
        import pdb
        pdb.set_trace()
        exit()
    # At this point, the method needs:
    #	* mutant: class ASE.Atoms. A framework of the cluster.
    #	* atoms_to_randomise: the index of atoms that need to be randomised, list of ints
    # setup the Atoms class to define the atoms in the cluster of mutant and define a unit cell
    # for atoms to be randomly placed in.
    # Get initial size of cell\
    mutant.set_cell(
        [boxtoplaceinlength, boxtoplaceinlength, boxtoplaceinlength])
    mutant.center()
    # For each element, place a set number of atoms of that element into the cluster in a random position
    # within the defined cube with sides = r_ij*scale
    for index in atoms_to_randomise:
        while True:
            x_position = boxtoplaceinlength * uniform(
                0.25, 0.75)  # janK: change from (0, 1)
            y_position = boxtoplaceinlength * uniform(0.25, 0.75)
            z_position = boxtoplaceinlength * uniform(0.25, 0.75)
            position = [x_position, y_position, z_position]
            # check the cluster to see that this new random position will not cause this atom
            # to be in the same place as any atom in the cluster currently
            if not is_position_already_occupied_by_an_atom_in_Cluster(
                    position,
                    mutant,
                    atom_indices_to_exclude_from_comparison=[index]):
                # Found that one of the atoms in this cluster has the same position as this randomly generated one.
                # Will need to make a new randomly generated position list
                break
        mutant[index].position = position
    # Add a unit cell to the cluster. Mainly needed for VASP.
    #lengthOfCell = 2.0*InclusionRadiusOfCluster(mutant) + vacuumAdd
    #cell = [lengthOfCell,lengthOfCell,lengthOfCell]
    #mutant.set_cell(cell)
    mutant.center(vacuum=vacuumAdd)  # centre the cluster within this unit cell
    # return the mutant cluster.
    #print('cluster_makeup: '+str(cluster_makeup)+'; cluster_to_mutate: '+str(cluster_to_mutate))
    if cluster_makeup is None:
        if not len(mutant) == len(cluster_to_mutate):
            print('Error in def randomMutate, in Types_Of_Mutations.py')
            print('The offspring contains ' + str(len(mutant)) +
                  ' atoms, but should contain ' + str(len(cluster_to_mutate)))
            print('Check this')
            import pdb
            pdb.set_trace()
            print('This program will finish without completing')
            exit()
    # elif cluster_to_mutate is None:
    # 	number_of_atoms = sum(Counter(cluster_makeup).values())
    # 	pass
    #print('Atoms in mutant: '+str(len(mutant))+'; number_of_atoms = '+str(number_of_atoms))
    #import pdb; pdb.set_trace()
    # if not len(mutant) == number_of_atoms:
    # 	print('Error in def randomMutate, in Types_Of_Mutations.py')
    # 	print('The offspring contains '+str(len(mutant))+' atoms, but should contain '+str(len(cluster_to_mutate)))
    # 	print('Check this')
    # 	import pdb; pdb.set_trace()
    # 	print('This program will finish without completing')
    # 	exit()
    return mutant