def numpy2DArrayToStdVectorStdVectorDouble(array):
    """
    Convert a 2D numpy array to a std::vector<std::vector<double> > representation.

    :param array: The array to convert.

    :returns: A corresponding std::vector<std::vector<double> > object.
    """
    # Get the shape of the array.
    shape = numpy.shape(array)
    nI = shape[0]
    nJ = shape[1]

    # Setup the c++ object.
    cpp_2D_vector = Backend.StdVectorStdVectorDouble(nI)

    # Copy the values over.
    for i in range(nI):
        cpp_vector = Backend.StdVectorDouble(nJ)
        for j in range(nJ):
            cpp_vector[j] = array[i][j]
        cpp_2D_vector[i] = cpp_vector

    # Done.
    return cpp_2D_vector
Exemple #2
0
 def _map(self):
     """
     Query for the lattice map.
     """
     # Generate the lattice map if not done allready.
     if self.__lattice_map is None:
         self.__lattice_map = Backend.LatticeMap(
             len(self.__unit_cell.basis()),
             Backend.StdVectorInt(self.__repetitions),
             Backend.StdVectorBool(self.__periodic))
     # Return the lattice map.
     return self.__lattice_map
Exemple #3
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)
def stringListToStdVectorStdVectorString(string_list):
    """
    Converts a list of strings to a std::vector<std::vector<std::string> >
    object with one elemnt in each of the inner vectors.

    :param string_list: The list of strings.

    :returns: A corresponding std::vector<std::vector<std::string> > object.
    """
    # Get the size.
    size = len(string_list)
    # Setup the c++ object.
    cpp_list = Backend.StdVectorStdVectorString()

    # Copy values over.
    for s in string_list:
        cpp_list.push_back(Backend.StdVectorString(1, s))
    # Done.
    return cpp_list
def numpy2DArrayToStdVectorCoordinate(array):
    """
    Convert a Nx3 2D numpy array to a std::vector<Coordinate> representation.

    :param array: The array to convert.

    :returns: A corresponding std::vector<Coordinate> object.
    """
    # Get the shape of the array.
    shape = numpy.shape(array)
    nI = shape[0]

    # Setup the c++ object.
    cpp_vector = Backend.StdVectorCoordinate()

    # Copy the values over.
    for i in range(nI):
        cpp_vector.push_back(
            Backend.Coordinate(array[i][0], array[i][1], array[i][2]))
    # Done.
    return cpp_vector
Exemple #6
0
    def _backend(self):
        """
        Query function for the c++ backend object.
        """
        if self.__backend is None:
            # Construct the c++ backend object.
            if self.__use_buckets:
                cpp_types = bucketListToStdVectorStdVectorString(self.__types)
            else:
                cpp_types = stringListToStdVectorStdVectorString(self.__types)

            cpp_coords = numpy2DArrayToStdVectorStdVectorDouble(
                self.__lattice.sites())
            cpp_possible_types = Backend.StdMapStringInt(self.__possible_types)

            # Send in the coordinates and types to construct the backend configuration.
            self.__backend = Backend.Configuration(cpp_coords, cpp_types,
                                                   cpp_possible_types)

        # Return the backend.
        return self.__backend
    def _backend(self):
        """
        Function for generating the C++ backend reperesentation of this object.

        :returns: The C++ LatticeModel based on the parameters given to this class on construction.
        """
        if self.__backend is None:
            # Setup the C++ objects we need.
            cpp_config = self.__configuration._backend()
            cpp_lattice_map = self.__configuration._latticeMap()
            cpp_interactions = self.__interactions._backend(
                self.__configuration.possibleTypes(), cpp_lattice_map.nBasis(),
                self.__configuration)
            # Construct a timer.
            self.__cpp_timer = Backend.SimulationTimer()

            # Construct the backend object.
            self.__backend = Backend.LatticeModel(cpp_config, self.__cpp_timer,
                                                  cpp_lattice_map,
                                                  cpp_interactions)
        # Return.
        return self.__backend
    def _update(self):
        """
        Query for the update info.

        :returns: The update stored on the class.
        """
        if self.__cpp_update is None:

            # Create the cpp update vector of maps.
            self.__cpp_update = Backend.StdVectorStdMapStringInt()

            # Take each entry in the update list.
            for update_entry in self.__update:

                # Convert to C++
                cpp_map = Backend.StdMapStringInt()
                for e in update_entry:
                    cpp_map[e[1]] = e[0]

                # Add.
                self.__cpp_update.push_back(cpp_map)

        return self.__cpp_update
def bucketListToStdVectorStdVectorString(bucket_list):
    """
    Converts a list of the format [[(n,"A"), ...], ...]
    to a std::vector< std::vector<std::string> > representation.

    :param bucket_list: The list to convert.

    :returns: A corresponding std::vector<std::vector<std::string> > object.
    """
    cpp_list = Backend.StdVectorStdVectorString()

    # For each site.
    for ss in bucket_list:
        # For all types at this site.
        site_list = Backend.StdVectorString()
        for s in ss:
            # Add the number of types.
            for i in range(s[0]):
                site_list.push_back(s[1])
        cpp_list.push_back(site_list)

    # Done.
    return cpp_list
    def _backend(self, possible_types):
        """
        Query for the local configuration backend object.

        :param possible_types: A dict with the global mapping of type strings
                               to integers.

        :returns: The interactions object in C++
        """
        if self.__backend is None:

            # Construct the c++ backend object.
            cpp_types = bucketListToStdVectorStdVectorString(self.__types)

            cpp_coords = numpy2DArrayToStdVectorStdVectorDouble(self.__coordinates)
            cpp_possible_types = Backend.StdMapStringInt(possible_types)

            # Send in the coordinates and types to construct the backend configuration.
            self.__backend = Backend.Configuration(cpp_coords,
                                                   cpp_types,
                                                   cpp_possible_types)

        # Return the backend.
        return self.__backend
Exemple #11
0
    def backendRateCallbackBuckets(self, cpp_coords, coords_len, occupations,
                                   update, types_map, rate_constant,
                                   process_number, global_x, global_y,
                                   global_z):
        """
        Function called from C++ to get the rate. It function recieves
        the data from C++ and parse it to a Python friendly format to send it
        forward to the custom rate function.
        """
        # PERFORMME: move operations to C++.

        # Determine the occupations after the move.
        occupations_after = Backend.StdVectorTypeBucket()

        for i in range(len(update)):
            occupations_after.push_back(occupations[i].add(update[i]))

        # Call and return the custom rate.
        global_coordinate = (global_x, global_y, global_z)
        return self.rate(
            numpy.array(cpp_coords).reshape(coords_len, 3),
            stdVectorTypeBucketToPython(occupations, types_map),
            stdVectorTypeBucketToPython(occupations_after, types_map),
            rate_constant, process_number, global_coordinate)
    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()
    def _backend(self, possible_types, n_basis, configuration):
        """
        Query for the interactions backend object.

        :param possible_types: A dict with the global mapping of type strings
                               to integers.

        :param n_basis: The size of the configuration basis is.
        :type n_basis: int

        :param configuration: The configuration of the systm, to be passed
                              on to any attached custom rate calculator.

        :returns: The interactions object in C++
        """
        if self.__backend is None:

            # Check the possible_types against the types in the processes.
            for process_number, process in enumerate(self.__processes):
                all_elements = process.allPresentTypes()
                if (not all([(e in possible_types) for e in all_elements])):
                    raise Error(
                        "Process %i contains elements not present in the list of possible types of the configuration."
                        % (process_number))

            # Setup the correct type of backend process objects
            # depending on the presence of a rate calculator.

            if self.__rate_calculator_class is not None:

                # Instantiate the rate calculator.
                if self.__builtin_custom == False:
                    rate_calculator = self.__rate_calculator_class(
                        configuration)
                else:
                    rate_calculator = self.__rate_calculator_class(
                        configuration._backend())

                if self.__builtin_custom == False:
                    if not isinstance(rate_calculator,
                                      KMCRateCalculatorPlugin):
                        msg = """
The 'rate_calculator' given to the KMCInteractions class must
inherit from the KMCRateCalculatorPlugin. """
                        raise Error(msg)
                    elif rate_calculator.__class__ == KMCRateCalculatorPlugin(
                            configuration).__class__:
                        msg = """
The 'rate_calculator' given to the KMCInteractions class must
inherit from the KMCRateCalculatorPlugin class. It may not be
the KMCRateCalculatorPlugin class itself. """
                        raise Error(msg)
                # Tests passed. Save the instantiated rate calculator on the class.
                self.__rate_calculator = rate_calculator

                # Generate the process vector.
                cpp_processes = Backend.StdVectorCustomRateProcess()
            else:
                # Generate the process vector.
                cpp_processes = Backend.StdVectorProcess()

            # For each interaction.
            for process_number, process in enumerate(self.__processes):

                # Get the corresponding C++ objects.
                cpp_config1 = process.localConfigurations()[0]._backend(
                    possible_types)

                if len(process.localConfigurations()) == 2:
                    cpp_config2 = process.localConfigurations()[1]._backend(
                        possible_types)
                else:
                    # Take a copy of the first configuration and set the update from the
                    # process.
                    cpp_config2 = cpp_config1
                    cpp_config2.setUpdateInfo(process._update())

                rate_constant = process.rateConstant()

                basis_list = range(n_basis)
                if process.basisSites() is not None:
                    # Make sure this basis list does not contain positions
                    # that are not in the configuration.
                    basis_list = []
                    for b in process.basisSites():
                        if b < n_basis:
                            basis_list.append(b)

                # And construct the C++ entry.
                cpp_basis = Backend.StdVectorInt(basis_list)

                # Setup the move vectors representation in C++.
                move_origins = [int(v[0]) for v in process.moveVectors()]
                cpp_move_origins = Backend.StdVectorInt(move_origins)
                cpp_move_vectors = Backend.StdVectorCoordinate()
                for v in process.moveVectors():
                    cpp_move_vectors.push_back(
                        Backend.Coordinate(v[1][0], v[1][1], v[1][2]))

                # Construct and store the C++ process.
                if self.__rate_calculator is not None:
                    # Set the cutoff correctly.
                    cutoff = self.__rate_calculator.cutoff()
                    if cutoff is None:
                        cutoff = 1.0

                    # Get the cache_rate flag.
                    cache_rate = (self.__rate_calculator.cacheRates() and \
                                      not process_number in self.__rate_calculator.excludeFromCaching())

                    cpp_processes.push_back(
                        Backend.CustomRateProcess(cpp_config1, cpp_config2,
                                                  rate_constant, cpp_basis,
                                                  cutoff, cpp_move_origins,
                                                  cpp_move_vectors,
                                                  process_number, cache_rate))
                else:
                    cpp_processes.push_back(
                        Backend.Process(cpp_config1, cpp_config2,
                                        rate_constant, cpp_basis,
                                        cpp_move_origins, cpp_move_vectors,
                                        process_number))

            # Construct the C++ interactions object.
            if self.__rate_calculator is not None:
                self.__backend = Backend.Interactions(
                    cpp_processes, self.__implicit_wildcards,
                    self.__rate_calculator)
            else:
                self.__backend = Backend.Interactions(
                    cpp_processes, self.__implicit_wildcards)

        # Return the stored backend.
        return self.__backend