예제 #1
0
def checkSequenceOf(sequence, class_type, msg="The tested object is not a sequence of the correct type."):
    """
    Utility function to check if a parameter is a sequence of instances of a given type.

    :param sequence: The sequence to check.

    :param class_type: The class of which the elements in the sequence should be instances.

    :param msg: Non-default error message to print.
    :type msg: string

    :returns: The valid sequence.
    """
    # Check that it is a sequence.
    sequence = checkSequence(sequence, msg)

    # Check that its length is not zero.
    if len(sequence) == 0:
        raise Error(msg)

    # Check that each element is an instance of type KMCProcess.
    for element in sequence:
        if not isinstance(element, class_type):
            raise Error(msg)
    # Done.
    return sequence
예제 #2
0
def checkTypes(types, length):
    """
    Check that the types list is given as a list of strings with the correct
    length.

    :param types:  The object to check.

    :param length: The size the list should have.
    :type length:  int

    :returns: The checked list.
    """
    # Check that it is a list.
    if not isinstance(types, list):
        raise Error("The 'types' parameter must be given as a list of strings.")

    # Check eachg element.
    for t in types:
        if not isinstance(t,str):
            raise Error("The 'types' parameter must be given as a list of strings.")

    # Check the length.
    if len(types) != length:
        raise Error("The length of the 'types' parameter must match the coordinates.")

    # Done.
    return types
예제 #3
0
def checkCoordinateList(coordinates, varname="coordinates"):
    """
    Check that the given coordinates is a valid Nx3 sequence of numbers.

    :param coordinates: The object to test. To pass the test this must be
                        an Nx3 array of floating point numbers.
    :param varname: The name of the variable. Defaults to "coordinates"
    :type varname: string
    :returns:           A valid Nx3 numpy array of numbers.
    """
    # Check that it is a sequence.
    coordinates = checkSequence(coordinates, "The %s must be given as a list of lists with dimensions Nx3"%(varname))

    # Check that its length is not zero.
    if (len(coordinates) < 1):
        raise Error("The '%s' parameter may not be an empty list."%(varname))

    # Check each coordinate.
    for coord in coordinates:

        # Check that it is a sequence.
        coord = checkSequence(coord, "The %s must be given as a list of lists with dimensions Nx3"%(varname))

        # Make sure the length of the coordinate is 3.
        if len(coord) != 3:
            raise Error("Each entry in the '%s' list must have exactly three elements."%(varname))

        # Check that each element is a floating point number.
        if not all([isinstance(c,float) for c in coord]):
            raise Error("All '%s' entries must be given as floating point numbers."%(varname))

    # Convert to a numpy array and return.
    return numpy.array(coordinates)
예제 #4
0
def checkPositiveFloat(parameter, default_parameter, parameter_name):
    """
    Utility function for checking that a parameter is a positive float.

    :param parameter: The parameter to check.

    :param default_parameter: The value to use if the parameter value is None

    :param parameter_name: The name of the parameter to use in error messages.
    :type parameter_name: str

    :returns: The checked parameter.
    """
    # Set default.
    if parameter is None:
        parameter = default_parameter

    # The error message.
    msg = "The parameter '%s' must be given as a positive float."%(parameter_name)
    # Check type.
    if not isinstance(parameter, float):
        raise Error(msg)

    # Check value.
    if parameter < 0.0:
        raise Error(msg)

    # Checked.
    return parameter
예제 #5
0
    def __checkPeriodic(self, periodic):
        """ """
        """
        Private helper routine to check the periodic input.
        """
        # Handle the default case.
        if periodic is None:
            periodic = (True, True, True)

        # Check that it is a sequence.
        periodic = checkSequence(
            periodic,
            "The 'periodic' input parametr is not given as a sequence of bools."
        )

        # Check its length.
        if len(periodic) != 3:
            raise Error(
                "The 'periodic' input parameter must have length == 3.")

        # Check the values.
        for val in periodic:
            if not isinstance(val, bool):
                raise Error(
                    "The 'periodic' input paramter must be given as a list or tuple of three bools, e.g. (True,True,False)."
                )

        # Done.
        return periodic
예제 #6
0
    def __checkAndSetTypes(self, types, default_type, possible_types):
        """ """
        """
        Private helper routine to check and set the types input.
        """

        types_format = self.__detectTypesFormat(types)

        if types_format == "short":
            types_to_set = self.__checkAndSetShortTypes(
                types, default_type, possible_types)
            all_types_present = list(set(types_to_set))
        elif types_format == "long":
            types_to_set = self.__checkAndSetLongTypes(types, default_type,
                                                       possible_types)
            all_types_present = list(set(types_to_set))
        else:
            types_to_set = self.__checkAndSetBucketsTypes(
                types, default_type, possible_types)
            self.__use_buckets = True
            # Calculate all types.
            all_types_present = []
            for tt in types_to_set:
                for t in tt:
                    all_types_present.append(t[1])
            all_types_present = list(set(all_types_present))

        # Setup the list of possible types.
        if possible_types is None:
            possible_types = all_types_present
        else:
            # Check that the possible types is a list of strings.
            if not isinstance(possible_types, list) or not all(
                [isinstance(pt, str) for pt in possible_types]):
                raise Error(
                    "The possible types must be given as a list of strings.")

            # Check that each present type is in the list of possible types.
            possible_set = set(possible_types)
            present_set = set(all_types_present)
            if not present_set.issubset(possible_set):
                raise Error(
                    "There are types specified which are not in the given list of possible types."
                )

        # Check that the wildcard has not made it into the possible types allready.
        if "*" in possible_types:
            raise Error("The wildcard caracter '*' is not a valid type.")

        # Setup the possible types as a dict.
        possible_types = ["*"] + possible_types
        self.__possible_types = dict(
            zip(possible_types, range(len(possible_types))))

        # Every thing is checked - store the data on the class.
        self.__types = types_to_set
예제 #7
0
def checkAndNormaliseBucketEntry(t):
    """
    Check that the foramt of the type entry t is any of the valid
    "A" - single particle.
    (N,"A") - numbered single particle.
    ["A", (N1,"B")] - a list with a combination of the other two.

    :param t: The type entry to check.
    :return: The normalized [(N,"A")] formatted type.
    """
    # Three cases, list, tuple, str.
    if isinstance(t, list):
        # Check that each entry is a valid str or tuple.
        normalised_type = []
        for tt in t:
            if isinstance(tt, str):
                normalised_type.append((1,tt))
            elif isinstance(tt, tuple):
                if not len(tt) == 2 and isinstance(tt[0], int) and isinstance(tt[1], str):
                    raise Error("Bucket type given as a tuple must be of format (int, str).")
                normalised_type.append(tt)
        # Loop through the normalised type list and
        # add entries with the same string.
        final_type = []
        skip_list  = []

        # Add those that have the same type.
        for i,first in enumerate(normalised_type):

            if not i in skip_list:

                for j in range(len(normalised_type)-i-1):
                    second = normalised_type[i+j+1]

                    # Check the string.
                    if first[1] == second[1]:
                        # Add if same type.
                        first = (first[0] + second[0], first[1])
                        # Put this index on the skip list.
                        skip_list.append(i+j+1)

                # Append to the final list.
                final_type.append(first)

        # Done.
        return final_type

    elif isinstance(t, tuple):
        if not len(t) == 2 and isinstance(t[0], int) and isinstance(t[1], str):
            raise Error("Bucket type given as a tuple must be of format (int, str).")
        return [t]
    elif isinstance(t, str):
        return [(1,t)]
    else:
        # Raise an error if the format was incorrnect.
        raise Error("Each bucket format type entry must be (a list of) a tupe(s) of format (int,str) or string(s).")
예제 #8
0
    def __checkValidMoveVectors(self, move_vectors):
        """
        Private helper function to check if a set of move vectors
        is compatible with the elements before and after the move.

        :param move_vectors: The move vectors to check.
        :returns: The validated move vectors.
        """
        # Check the move vector format, and reconstruct if
        # possible.
        move_vectors = self.__checkMoveVectorsFormat(move_vectors)

        # Quick return if no move vectors.
        if move_vectors is None:
            move_vectors = []
            return move_vectors

        # Try to make the move with the move vectors and check that
        # we end up in the elements after list.
        moved_elements = [] + self._elements_before
        for (move_index, move_vector) in move_vectors:
            old_coord = self._coordinates[move_index]
            new_coord = numpy.array(old_coord) + numpy.array(
                move_vector)  #    <- Elementwise addition.

            # Find which index this corresponds to.
            subtracted = self._coordinates - new_coord
            reduced = numpy.abs(sum(abs(subtracted.transpose())))
            boolean = [rr < 1.0e-8 for rr in reduced]
            new_index = numpy.where(boolean)
            if len(new_index[0]) == 0:
                raise Error(
                    "Each move_vector must move an atom to a valid lattice site."
                )
            new_index = new_index[0][0]

            # Check that the the element at this position in the new elements
            # vector corresponds to the move.
            if self._elements_before[move_index] != self._elements_after[
                    new_index]:
                raise Error(
                    "The move vector for index %i does not match the elements after move."
                    % (move_index))

            # Perform the move.
            moved_elements[new_index] = self._elements_before[move_index]

        # With all moves performed on the elements we check if we properly reconstructed
        # the elements after the move.
        if (self._elements_after != moved_elements):
            raise Error(
                "Applying the move vectors to the elements_before does not generate the elements_after list."
            )

        return move_vectors
예제 #9
0
    def __init__(self, unit_cell=None, repetitions=None, periodic=None):
        """
        Constructor for the Lattice used in the KMC simulations.

        :param unit_cell: The unitcell object to represent the periodicity of the lattice.

        :param repetitions: The repetitions of the unitcell along the a,b and c directions
                            to create the full lattice, given as a list or tuple of three
                            integers > 0. If not given this defaults to (1,1,1)

        :param periodic: A list or tuple indicating if periodicity should be used along the
                         a, b and c directions. If not specified it defaults to (True,True,True)
        :type periodic: (bool,bool,bool)
        """
        # Check and store the unit cell.
        if not isinstance(unit_cell, KMCUnitCell):
            raise Error(
                "The 'unit_cell parameter to the KMCLattice constructor must be of type KMCUnitCell."
            )
        self.__unit_cell = unit_cell

        # Passed the tests, store.
        self.__repetitions = self.__checkRepetitions(repetitions)

        # Check the periodic input.
        self.__periodic = self.__checkPeriodic(periodic)

        # Generate the lattice sites.
        self.__sites = self.__generateLatticeSites()

        # Set the lattice map to be generated at first query.
        self.__lattice_map = None
예제 #10
0
    def __init__(self, cell_vectors=None, basis_points=None):
        """
        Constructor for the KMCUnitCell class.

        :param cell_vectors: A 3x3 matrix, nested lists or numpy array with
                             rows corresponding to the a, b and c vectors in
                             cartesian coordinates.

        :param basis_points: A Nx3 matrix, nested lists or numpy array with
                             each row corresponding to the fractional coordinates
                             of a basis point.
        """
        # Check the cell vectors.
        self.__cell_vectors = checkCellVectors(cell_vectors)

        # Check the basis points are of correct format.
        basis_points = checkCoordinateList(basis_points,
                                           varname="basis_points")

        # Check that the basis points are given in fractional coordinates.
        for point in basis_points:
            if not all([b < 1.0 and b >= 0.0 for b in point]):
                raise Error(
                    "All basis points must be given in fractional coordinates in the range 0.0 <= basis_point < 1.0."
                )

        self.__basis_points = basis_points
예제 #11
0
    def __detectTypesFormat(self, types):
        """ """
        """
        Private helper routine to detect what types format is given.

        :retrurns: "short", "long" or "buckets".
        """
        # Check that the types is a list.
        if not isinstance(types, list):
            raise Error(
                "The 'types' given to the KMCConfiguration constructor must be a list of either 'short', 'long' or 'buckets' format. See the manual for details."
            )

        # Check all elements to see if we have the short format.
        if all([isinstance(t, str) for t in types]):
            return "short"

        # Check all elements to see if we have the long format.
        elif all([isinstance(t, tuple) for t in types]) and all([
                isinstance(tt, (int, int, int, int, str)[i])
                for i, tt in enumerate(types[0])
        ]):
            return "long"

        # No other option left but to assume the buckets format.
        else:
            return "buckets"
예제 #12
0
    def setup(self, step, time, configuration):
        """
        Recieves the setup call from the before the MC loop.

        :param step: The step number of the simulation.
        :type step: int

        :param time: The time of the simulation.
        :type time: float

        :param configuration: The configuration of the simulation.
        :type configuration: KMCConfiguration
        """
        # Make sure the track type is one of the possible types.
        if not self.__track_type in configuration.possibleTypes():
            raise Error(
                "The track type of the MSD calculator is not one of the valid types of the configuration."
            )

        # Get the cell vectors out.
        abc_to_xyz = numpy.array(
            configuration.lattice().unitCell().cellVectors()).transpose()
        abc_to_xyz_cpp = numpy2DArrayToStdVectorCoordinate(abc_to_xyz)

        # Setup the backend.
        self.__backend = Backend.OnTheFlyMSD(configuration._backend(),
                                             self.__history_steps,
                                             self.__n_bins, self.__t_max, time,
                                             self.__track_type, abc_to_xyz_cpp,
                                             self.__blocksize)
예제 #13
0
    def rate(self, coords, types_before, types_after, rate_constant,
             process_number, global_coordinate):
        """
        Called from the base class to get the rate for a particular
        local geometry. Any class inheriting from the plugin base class
        must provide an implementation of this function.

        :param coords: The coordinates of the configuration as a Nx3 numpy array
                       in fractional units of the primitive cell.

        :param types_before: The types before the process, as tuple of strings.

        :param types_after: The types after the process, as tuple of strings.

        :param rate_constant: The rate constant associated with the process
                              to either update or replace.

        :param process_number: The process id number.

        :param global_coordinate: The global coordinate of the central index.

        :returns: The custom rate of the process. Note that the returned rate must
                  not be negative or zero.
        """
        raise Error(
            "The rate(self,...) API function in the 'KMCRateCalculator' base class must be overloaded when using a custom rate calculator."
        )
예제 #14
0
    def __checkValidMoveElements(self, elements_before, elements_after):
        """
        Private helper function to check if a move is valid with respect
        to atom types and wildcards.
        :param elements_before: The list of elements before the move.
        :param elements_after: The list of elements after the move.
        """
        # Check that the wildcards, if any, are not moved.
        before = [e == "*" for e in elements_before]
        after = [e == "*" for e in elements_after]

        if len(before) != len(after) or before != after:
            raise Error("Wildcards must not move during a valid process.")

        # Check that they are not identical.
        if elements_before == elements_after:
            raise Error(
                "The atomic configuration before and after a move can not be identical."
            )
예제 #15
0
    def __checkMoveVectorsFormat(self, move_vectors):
        """
        Private helper function to check the format of the move vectors.

        :param move_vectors: The input to check.

        :returns: The validated move vectors, or None if None was given
                  and a reconstruction was not possible.
        """
        if move_vectors is None:
            # Try to reconstruct the move vectors from the before and after
            # elements information.
            move_vectors = self.__reconstructMoveVectors()

        # Return if still None.
        if move_vectors is None:
            return move_vectors

        # This is the error message.
        msg = """The 'move_vectors' input to the KMCProcess constructor must be a
list of tuples, where the first element of each tuple refers to an atom index
and the second element is a cartesian vector of length 3, in internal
coordinates defining where the moved index goes."""

        # Check that we have a sequence.
        move_vectors = checkSequence(move_vectors, msg)

        # Check the format of each element.
        for t in move_vectors:
            if not isinstance(t, tuple):
                raise Error(msg)
            elif not isinstance(t[0], int):
                raise Error(msg)
            # Check the vector.
            checkSequenceOfFloats(t[1], msg)
            if len(t[1]) != 3:
                raise Error(msg)
        # Passed all tests.
        return move_vectors
예제 #16
0
    def __checkAndSetShortTypes(self, types, default_type, possible_types):
        """ """
        """
        Private helper to check the types input for the 'short' format.
        """
        # Check the default types.
        if default_type is not None:
            raise Error(
                "A default type can only be used in combination with the long types format\nwhen constructing a KMCConfiguration object."
            )

        # Check the types.
        return checkTypes(types, self.__n_lattice_sites)
예제 #17
0
    def __init__(self, configuration=None, interactions=None):
        """
        The KMCLatticeModel class is the central object in the kmclib framework
        for running a KMC simulation. Once a configuration with a lattice is
        defined and a set of interactions are setup, the KMCLatticeModel object
        unites this information, checks that the given interactions match the
        configurations, and provides means for running a KMC Lattice simulation.

        :param configuration: The KMCConfiguration to run the simulation for.

        :param interactions: The KMCInteractions that specify possible local
                             states and barriers to use in the simulation.

        """
        # Check the configuration.
        if not isinstance(configuration, KMCConfiguration):
            raise Error(
                "The 'configuration' parameter to the KMCLatticeModel must be an instance of type KMCConfiguration."
            )

        # Store.
        self.__configuration = configuration

        # Check the interactions.
        if not isinstance(interactions, KMCInteractions):
            raise Error(
                "The 'interactions' parameter to the KMCLatticeModel must be an instance of type KMCInteractions."
            )

        # Store.
        self.__interactions = interactions

        # Set the backend to be generated at first query.
        self.__backend = None

        # Set the verbosity level of output to minimal.
        self.__verbosity_level = 0
예제 #18
0
def checkCellVectors(cell_vectors):
    """
    Check that the cell vectors are of the correct dimensions and not fully linearly dependent.

    :param cell_vectors: The cell vectors to test.

    :returns: The valid cell vectors as a 3x3 numpy array.
    """
    # If this is not a numpy array, check the list and convert.
    if not isinstance(cell_vectors, numpy.ndarray):

        # If it is a list, check that it is a list of list, length 3x3
        cell_vectors = checkSequence(cell_vectors, "The 'cell_vectors' must be given as a 3x3 list or array of numbers.")

        # Check as if this was a coordinate list.
        cell_vectors = checkCoordinateList(cell_vectors, "cell_vecctors")

        # Transform to a numpy array.
        cell_vectors = numpy.array(cell_vectors)

    # Now with the cell vectors as a numpy array, check the dimension.
    if numpy.shape(cell_vectors) != (3,3):
        raise Error("The 'cell_vectors' parametes must have the shape 3x3.")

    # Check the type.
    dummy_array = numpy.array([[1.0,0.0,0.0],[0.0,1.0,0.0],[0.0,0.0,1.0]])
    if cell_vectors.dtype != dummy_array.dtype:
        raise Error("The cell_vectors elements must be floating point numbers.")

    # Check for linear dependencies.
    cell_T = numpy.transpose(cell_vectors)
    cell_determinant = numpy.linalg.det(cell_T)
    if cell_determinant < 0.00001:
        raise Error("The unit cell vectors are linearly dependent with determinant = %f."%(cell_determinant))

    # Done checking.
    return cell_vectors
예제 #19
0
    def __checkRepetitions(self, repetitions):
        """ """
        """
        Private helper routine to check the repetitions input.
        """
        # Handle the default case.
        if repetitions is None:
            repetitions = (1, 1, 1)

        # Check that it is a sequence.
        repetitions = checkSequence(
            repetitions,
            "The 'repetitions' input parametr is not given as a sequence of numbers."
        )

        # Check its length.
        if len(repetitions) != 3:
            raise Error(
                "The 'repetitions' input parameter must have length == 3.")

        # Check the values.
        for val in repetitions:
            if not isinstance(val, int):
                raise Error(
                    "The 'repetitions' input paramter must be given as a list or tuple of three integers, e.g. (5,5,6)"
                )

        # Check for the occurance of too small values.
        for val in repetitions:
            if val < 1:
                raise Error(
                    "The all elements in the 'repetitions' parameter must be larger than zero."
                )

        # Done.
        return repetitions
예제 #20
0
    def __init__(self,
                 lattice=None,
                 types=None,
                 possible_types=None,
                 default_type=None):
        """
        Constructor for the KMCConfiguration - the configuration object to use
        in the KMC simulations.

        :param lattice: The lattice of the configurartion as a KMCLattice.

        :param types: The site types at the lattice points as a list, e.g. ['type1','type2',..],
                      ordered as [a,b,c,i] with i being the fastest and a the slowest index and
                      a, b and c refers to the cell repetitions and i
                      refers to the specific basis point in the cell. When using this format
                      one cannot specify a default type, and the number of elements in the
                      types list must match the number of grid points in the lattice. Alternatively
                      one can specify the types input as a list of tuples specifying the a,b,c,i for each type,
                      e.g. [(0,0,1,0,'a'), (0,0,1,1,'b'), ...]. If one uses the longer tuple format
                      a default type should be given, which then will be used for all points not
                      explicitly specified in the list.

        :param possible_types: A list of possible types. If not given this list will be set to the
                               types present in the types list by default.

        :param default_type: This input parameter can only be given if the types are
                             given in long format i.e. [(0,0,1,0,'a'), (0,0,1,1,'b'), ...]
                             The default type will then be used for lattice sites
                             not specified in the types list.
        """
        # Check that the lattice is of the correct type.
        if not isinstance(lattice, KMCLattice):
            raise Error(
                "The lattice given to the KMCConfiguration constructor must be of type KMCLattice."
            )
        self.__lattice = lattice

        # Extract the number of lattice sites.
        self.__n_lattice_sites = len(self.__lattice.sites())

        # To be updated if bucket types are used.
        self.__use_buckets = False

        # Check and set the types.
        self.__checkAndSetTypes(types, default_type, possible_types)

        # Wait with setting up the backend until we need it.
        self.__backend = None
예제 #21
0
    def __checkAndSetBucketsTypes(self, types, default_type, possible_types):
        """ """
        """
        Private helper to check the types input for the 'buckets' format.
        """
        # Check the default types.
        if default_type is not None:
            raise Error(
                "A default type can only be used in combination with the long types format\nwhen constructing a KMCConfiguration object."
            )

        # For each element, check that the foramt is any of the valid
        # "A" - single particle
        # (N,"A") - numbered single particle
        # ["A", (N1,"B")] - a list with a combination of the other two.
        return [checkAndNormaliseBucketEntry(t) for t in types]
예제 #22
0
def checkSequence(sequence, msg="The tested object is not a sequence."):
    """
    Check that the given object is sequence.

    :param sequence: The object to test.

    :param msg: Non-default error message to print.
    :type msg: string

    :returns:        The valid sequence object.
    """
    # Check that this is a sequence.
    if not ('__len__' in dir(sequence)):
        raise Error(msg)

    # Done.
    return sequence
    def __init__(self,
                 coordinates=None,
                 types=None,
                 center=None):
        """
        Constructor for the KMCLocalConfiguration.

        :param coordinates: The coordinates of the configuration given as
                            a 3xN sequence of floating point numbers, where
                            N is the number of local sites.

        :param types: The lattice site types at the specified coordinates given
                      as a sequence of strings of length N.

        :param center: The coordinate in the list to treat as the central site
                       indexed from zero. If not given it will default to the
                       first coordinate (i.e. center == 0).
        :type center:  int
        """
        # ML:
        # FIXME: Must support bucket types information.

        # Check the coordinates.
        coordinates = checkCoordinateList(coordinates)

        # Set the center coordinate if not given.
        if center is None:
            center = 0

        # Check the bounds of the center coordinate.
        center = checkIndexWithinBounds(center,
                                        coordinates,
                                        msg="The 'center' index paramter must be one in the coordinate list.")

        # Center the coordinates.
        self.__coordinates = centerCoordinates(coordinates, center)

        # Check the types.
        if len(types) != len(coordinates):
            raise Error("The length of the types must match the coordinates.")

        self.__types = [checkAndNormaliseBucketEntry(t) for t in types]

        # Set the backend to None, to generate it at first query.
        self.__backend = None
예제 #24
0
    def __init__(self,
                 history_steps=None,
                 n_bins=None,
                 t_max=None,
                 track_type=None):
        """
        Constructor for the OnTheFlyMSD.

        :param history_steps: The number of steps per atom to store in the
                              history buffer.
        :type history_steps: int

        :param n_bins: The nuber of bins in the histogram.
        :type n_bins: int

        :param t_max: The starting value of the last bin.
        :type t_max: float

        :param track_type: The atom type to track during the simulation.
        :type track_type: str
        """
        # Check and set the history steps input.
        self.__history_steps = checkPositiveInteger(history_steps, 5,
                                                    "history_step")

        # Check and set the number of bins.
        self.__n_bins = checkPositiveInteger(n_bins, 100, "n_bins")

        # Check and set the time maximum.
        self.__t_max = checkPositiveFloat(t_max, 100.0, "t_max")

        # Check that the track type.
        if not isinstance(track_type, str):
            raise Error("The 'track_type' parameter must be a string.")
        self.__track_type = track_type

        # Calculate and store the binsize.
        self.__binsize = self.__t_max / self.__n_bins

        # Set the step counter to zero.
        self.__n_steps = 0

        # Set the blocksize to zero.
        self.__blocksize = 0
예제 #25
0
def checkSequenceOfPositiveIntegers(sequence, msg="The tested object is not a sequence of positive integers."):
    """
    Utility function to check if a parameter is a sequence of positive integers.

    :param sequence: The sequence to check.

    :param msg: Non-default error message to print.
    :type msg: string

    :returns: The valid sequence.
    """
    # Check that it is a sequence.
    sequence = checkSequenceOf(sequence, int, msg)

    # Check that each element is a positive integer.
    for s in sequence:
        if s < 0:
            raise Error(msg)

    # Done.
    return sequence
예제 #26
0
    def __checkRngType(self, rng_type, default):
        """
        Private helper function to check the random number generator input.
        """
        if rng_type is None:
            rng_type = default

        rng_dict = {
            "MT": Backend.MT,
            "MINSTD": Backend.MINSTD,
            "RANLUX24": Backend.RANLUX24,
            "RANLUX48": Backend.RANLUX48,
            "DEVICE": Backend.DEVICE,
        }

        if not rng_type in rng_dict.keys():
            raise Error(
                "'rng_type' input must be one of the supported types. Check the documentation for the list of supported types. Default is 'MT' (Mersenne-Twister) [std::mt19937]."
            )

        return rng_dict[rng_type]
예제 #27
0
    def __init__(self, processes=None, implicit_wildcards=None):
        """
        Constructor for the KMCInteractions.

        :param processes: A list of possible processes in the simulation.

        :param implicit_wildcards: A flag indicating if implicit wildcards should be used in
                                   the matching of processes with the configuration. The default
                                   is True, i.e. to use implicit wildcards.
        :type implicit_wildcards:  bool
        """
        # Check the processes input.
        processes = checkSequenceOf(
            processes,
            KMCBaseProcess,
            msg=
            "The 'processes' input must be a list of KMCProcess or KMCBucketProcess instances."
        )

        # Store the processes.
        self.__processes = processes

        # Check the implicit wildcard flag.
        if implicit_wildcards is None:
            implicit_wildcards = True
        if not isinstance(implicit_wildcards, bool):
            raise Error(
                "The 'implicit_wildcard' flag to the KMCInteractions constructor must be given as either True or False"
            )
        self.__implicit_wildcards = implicit_wildcards

        # Set the backend to be generated at first query.
        self.__backend = None

        # Set the rate calculator.
        self.__rate_calculator = None
        self.__rate_calculator_class = None
        self.__builtin_custom = False
예제 #28
0
def checkIndexWithinBounds(index, list, msg=None):
    """
    Check that the given index is within the bounds of the list.

    :param index: The index to check.
    :type index:  int

    :param list:  The list to check against.

    :param msg: The error message to print. If none is given the string default to "Index out of range."
    :type msg: string

    :returns:     The valid index.
    """
    # Set the default.
    if msg is None:
        msg = "Index out of range."

    # Stop if outside bounds.
    if (index < 0 or index >= len(list)):
        raise Error(msg)

    # Return if passed.
    return index
예제 #29
0
    def __init__(self,
                 coordinates=None,
                 minimum_match=None,
                 update=None,
                 basis_sites=None,
                 rate_constant=None):
        """
        Constructor for the KMCBucketProcess.

        :param coordinates:   The local coordinates, corresponding to the lattice
                              positions of the surrounding of the center where
                              the process will be preformed.

        :param minimum_match: The list of minimal occupations to match.

        :param update:        List of positive and negative changes in occupations
                              due to performing the process.

        :param basis_sites:   The basis sites in the lattice at which the process
                              can possibly be applied. Only if the length of this
                              list is 1 can implicit wildcards be used for the
                              matching.

        :param rate_constant: The rate constant associated with this process.
        :type rate_constant: float
        """
        # Call the base class constructor.
        super(KMCBucketProcess, self).__init__(coordinates, basis_sites,
                                               rate_constant)

        # Check the minimum match.
        self.__minimum_match = [
            checkAndNormaliseBucketEntry(m) for m in minimum_match
        ]

        if not len(self.__minimum_match) == len(coordinates):
            raise Error(
                "The types 'match' information to a KMCBucketProcess constructor must be of the same length as the coordinates."
            )

        # Check the update information.
        self.__update = self.__checkUpdateInfo(update)

        # Construct elements before based on the match and update.
        self._elements_before = self.__minimum_match

        # Get all present types.
        all_types = []
        for e in self._elements_before:
            if isinstance(e, str):
                all_types.append(e)
            else:
                for ee in e:
                    if isinstance(ee, str):
                        all_types.append(ee)
                    elif isinstance(ee, tuple):
                        all_types.append(ee[1])

        # Remove doublets and store on the class.
        self._all_present_types = list(set(all_types))

        # We are not allowed to use move vectors with bucket processes.
        self._move_vectors = []

        # Setup the local configurations.
        center = 0
        c1 = KMCLocalConfiguration(self._coordinates, self._elements_before,
                                   center)
        self._local_configurations = (c1, )

        # To be updated later.
        self.__cpp_update = None
예제 #30
0
    def run(self,
            control_parameters=None,
            trajectory_filename=None,
            trajectory_type=None,
            analysis=None,
            breakers=None):
        """
        Run the KMC lattice model simulation with specified parameters.

        :param control_paramters:   An instance of KMCControlParameters specifying
                                    number of steps to run etc.

        :param trajectory_filename: The filename of the trajectory. If not given
                                    no trajectory will be saved.

        :param trajectory_type:     The type of trajectory to use. Either 'lattice' or 'xyz'.
                                    The 'lattice' format shows the types at the latice points.
                                    The 'xyz' format gives type and coordinate for each particle.
                                    The default type is 'lattice'.
        :param analysis:            A list of instantiated analysis objects that should be used for on-the-fly analysis.

        :param breakers:            A list of instantiated breaker objects to break the Monte Carlo loop with a custom criterion.
        """
        # Check the input.
        if not isinstance(control_parameters, KMCControlParameters):
            msg = """
The 'control_parameters' input to the KMCLatticeModel run funtion
must be an instance of type KMCControlParameters."""
            raise Error(msg)

        # Check the trajectory filename.
        use_trajectory = True
        if trajectory_filename is None:
            use_trajectory = False
            msg = """ kmclib: WARNING: No trajectory filename given -> no trajectory will be saved."""
            prettyPrint(msg)

        elif not isinstance(trajectory_filename, str):
            msg = """
The 'trajectory_filename' input to the KMCLattice model run function
must be given as string."""
            raise Error(msg)

        # Check the analysis type.
        if trajectory_type is None:
            trajectory_type = 'lattice'

        if not isinstance(trajectory_type, str):
            raise Error("The 'trajectory_type' input must given as a string.")

        # Check the analysis.
        if analysis is None:
            analysis = []
        else:
            msg = "Each element in the 'analyis' list must be an instance of KMCAnalysisPlugin."
            analysis = checkSequenceOf(analysis, KMCAnalysisPlugin, msg)

        # Check the breakers.
        if breakers is None:
            breakers = []
        else:
            msg = "Each element in the 'breakers' list must be an instance of KMCBreakerPlugin."
            breakers = checkSequenceOf(breakers, KMCBreakerPlugin, msg)

        # Set and seed the backend random number generator.
        if not Backend.setRngType(control_parameters.rngType()):
            raise Error(
                "DEVICE random number generator is not supported by your system, or the std::random_device in the standard C++ library you use is implemented using a pseudo random number generator (entropy=0)."
            )

        Backend.seedRandom(control_parameters.timeSeed(),
                           control_parameters.seed())

        # Construct the C++ lattice model.
        prettyPrint(" kmclib: setting up the backend C++ object.")

        cpp_model = self._backend()

        # Print the initial matching information if above the verbosity threshold.
        if self.__verbosity_level > 9:
            self.__printMatchInfo(cpp_model)

        # Check that we have at least one available process to  run the KMC simulation.
        if cpp_model.interactions().totalAvailableSites() == 0:
            raise Error(
                "No available processes. None of the processes defined as input match any position in the configuration. Change the initial configuration or processes to run KMC."
            )

        # Setup a trajectory object.
        last_time = self.__cpp_timer.simulationTime()
        if use_trajectory:
            if trajectory_type == 'lattice':
                trajectory = LatticeTrajectory(
                    trajectory_filename=trajectory_filename,
                    configuration=self.__configuration)
            elif trajectory_type == 'xyz':
                trajectory = XYZTrajectory(
                    trajectory_filename=trajectory_filename,
                    configuration=self.__configuration)
            else:
                raise Error(
                    "The 'trajectory_type' input must be either 'lattice' or 'xyz'."
                )

            # Add the first step.
            trajectory.append(
                simulation_time=self.__cpp_timer.simulationTime(),
                step=0,
                configuration=self.__configuration)

        # Setup the analysis objects.
        for ap in analysis:
            step = 0
            ap.setup(step, self.__cpp_timer.simulationTime(),
                     self.__configuration)

        # Get the needed parameters.
        n_steps = control_parameters.numberOfSteps()
        n_dump = control_parameters.dumpInterval()
        n_analyse = control_parameters.analysisInterval()
        dump_time = control_parameters.dumpTimeInterval()

        prettyPrint(" kmclib: Runing for %i steps, starting from time: %f\n" %
                    (n_steps, self.__cpp_timer.simulationTime()))

        # Run the KMC simulation.
        try:
            # Loop over the steps.
            step = 0
            time_step = 0
            while (step < n_steps):
                step += 1

                # Check if it is possible to take a step.
                nP = cpp_model.interactions().totalAvailableSites()
                if nP == 0:
                    raise Error("No more available processes.")

                # Take a step.
                time_before = self.__cpp_timer.simulationTime()
                cpp_model.propagateTime()
                time_after = self.__cpp_timer.simulationTime()

                # Check if it is time to dump the previous step to the equidistant trajectory.
                if ((dump_time is not None)
                        and ((time_after - last_time) >= dump_time)):
                    time_step += 1
                    sample_time = time_step * dump_time
                    prettyPrint(" kmclib: %14i steps executed. time: %20.10e" %
                                (step - 1, sample_time))
                    last_time = sample_time

                    # Perform IO using the trajectory object.
                    if use_trajectory:
                        trajectory.append(simulation_time=time_before,
                                          step=step - 1,
                                          configuration=self.__configuration)

                # Update the model.
                cpp_model.singleStep()

                # Get the current simulation time.
                now = self.__cpp_timer.simulationTime()

                # Check if it is time to write a trajectory dump.
                if ((dump_time is None) and ((step) % n_dump == 0)):
                    last_time = now
                    prettyPrint(" kmclib: %i steps executed. time: %20.10e " %
                                (step, now))

                    # Perform IO using the trajectory object.
                    if use_trajectory:
                        trajectory.append(simulation_time=now,
                                          step=step,
                                          configuration=self.__configuration)

                if ((step) % n_analyse == 0):
                    # Run all other python analysis.
                    for ap in analysis:
                        ap.registerStep(step, now, self.__configuration)

                # Check all custom break criteria.
                break_the_loop = False
                for b in breakers:

                    # If it is time to evaluate this breaker.
                    if ((step) % b.interval() == 0):
                        break_the_loop = b.evaluate(step, now,
                                                    self.__configuration)
                        # Break the inner loop.
                        if break_the_loop:
                            break

                # Break the main Monte-Carlo loop.
                if break_the_loop:
                    break

        finally:

            # Flush the trajectory buffers when done.
            if use_trajectory:
                trajectory.flush()

            # Perform the analysis post processing.
            for ap in analysis:
                ap.finalize()