def testStdVectorCoordinateToNumpy2DArray(self):
        """ Test the conversion from a std::vector<Coordinate> to a numpy array. """
        # Get a reference.
        ref_array = numpy.random.rand(6).reshape((2, 3))

        # Setup the data to convert.
        cpp_vector = Backend.StdVectorCoordinate()
        cpp_vector.push_back(
            Backend.Coordinate(ref_array[0, 0], ref_array[0, 1], ref_array[0,
                                                                           2]))
        cpp_vector.push_back(
            Backend.Coordinate(ref_array[1, 0], ref_array[1, 1], ref_array[1,
                                                                           2]))

        # Convert.
        py_array = stdVectorCoordinateToNumpy2DArray(cpp_vector)

        # Check.
        diff = numpy.linalg.norm(ref_array - py_array)
        self.assertAlmostEqual(diff, 0.0, 10)
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
    def _backend(self, possible_types, n_basis):
        """
        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

        :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 = list(
                    set(process.elementsBefore() + process.elementsAfter()))
                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 is not None:
                cpp_processes = Backend.StdVectorCustomRateProcess()
            else:
                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)
                cpp_config2 = process.localConfigurations()[1]._backend(
                    possible_types)
                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

                    cpp_processes.push_back(
                        Backend.CustomRateProcess(cpp_config1, cpp_config2,
                                                  rate_constant, cpp_basis,
                                                  cutoff, cpp_move_origins,
                                                  cpp_move_vectors,
                                                  process_number))
                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
    def testUsage(self):
        """ Test that the KMCRateCalculatorPlugin can be used in a simulation. """
        # To get the random numbers and process numbers returned.
        ref_randoms = []
        ref_process_numbers = []
        ref_coordinates = []

        # Define a derrived class.
        class RateCalc(KMCRateCalculatorPlugin):
            # Overload the initialize function.
            def initialize(self):
                # Save something on the class here.
                self._times_called = 0

            # Overload the rate function.
            def rate(self, coords, types_befpre, types_after, rate_constant,
                     process_number, coordinate):
                # Do some simple counting and return the random number.
                self._times_called += 1
                rnd = numpy.random.uniform(0.0, 1.0)
                ref_randoms.append(rnd)
                ref_process_numbers.append(process_number)
                ref_coordinates.append(coordinate)
                return rnd

            # Overload the additive rate function.
            def useAdditiveRate(self):
                return False

        # Construct.
        calculator = RateCalc("DummyConfig")

        # Send it to C++ to get the rate out, call it 4 times.
        cpp_coords = Backend.StdVectorCoordinate()
        cpp_coords.push_back(Backend.Coordinate(1.0, 2.9, 3.4))
        cpp_types1 = Backend.StdVectorString()
        cpp_types1.push_back("A")
        cpp_types2 = Backend.StdVectorString()
        cpp_types2.push_back("B")
        rate_constant = 3.1415927
        ret_randoms = []
        process_numbers = [21, 12, 10, 2]
        global_xyz = numpy.array([[0.2, 0.4, 0.5], [1.1, 1.3, 1.4],
                                  [3.4, 4.3, 3.3], [4.2, 3.2, 1.9]])

        ret_randoms.append(
            Backend.getRate(calculator, cpp_coords, cpp_types1, cpp_types2,
                            rate_constant, process_numbers[0],
                            global_xyz[0, 0], global_xyz[0, 1], global_xyz[0,
                                                                           2]))
        ret_randoms.append(
            Backend.getRate(calculator, cpp_coords, cpp_types1, cpp_types2,
                            rate_constant, process_numbers[1],
                            global_xyz[1, 0], global_xyz[1, 1], global_xyz[1,
                                                                           2]))
        ret_randoms.append(
            Backend.getRate(calculator, cpp_coords, cpp_types1, cpp_types2,
                            rate_constant, process_numbers[2],
                            global_xyz[2, 0], global_xyz[2, 1], global_xyz[2,
                                                                           2]))
        ret_randoms.append(
            Backend.getRate(calculator, cpp_coords, cpp_types1, cpp_types2,
                            rate_constant, process_numbers[3],
                            global_xyz[3, 0], global_xyz[3, 1], global_xyz[3,
                                                                           2]))

        # Check that it was called 4 times.
        self.assertEqual(calculator._times_called, 4)

        # Check the values.
        self.assertAlmostEqual(ret_randoms, ref_randoms, 12)

        # Check the reference process numbers.
        self.assertEqual(ref_process_numbers[0], 21)
        self.assertEqual(ref_process_numbers[1], 12)
        self.assertEqual(ref_process_numbers[2], 10)
        self.assertEqual(ref_process_numbers[3], 2)

        # Check the reference coordinate.
        self.assertAlmostEqual(
            numpy.linalg.norm(global_xyz - numpy.array(ref_coordinates)), 0.0,
            10)
Example #5
0
    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