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)
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