def testBackendNoFailWrongBasisMatch(self): """ Test for no failure when constructing backend with wrong n_basis """ # A first process. coords = [[1.0, 2.0, 3.4], [1.1, 1.2, 1.3]] types0 = ["A", "B"] types1 = ["B", "A"] rate_0_1 = 3.5 process_0 = KMCProcess(coords, types0, types1, basis_sites=[0, 4], rate_constant=rate_0_1) # A second process. types0 = ["A", "C"] types1 = ["C", "A"] rate_0_1 = 1.5 process_1 = KMCProcess(coords, types0, types1, basis_sites=[0, 1], rate_constant=rate_0_1) processes = [process_0, process_1] # Construct the interactions object. kmc_interactions = KMCInteractions(processes=processes) # Setup a dict with the possible types. possible_types = { "A": 13, "D": 2, "B": 3, "J": 4, "C": 5, } # Use a dummy argument for the configuration. config = "DummyConfig" # Get the backend - The [0,4] sites list for process_0 is simply ignored. cpp_interactions = kmc_interactions._backend(possible_types, 2, config) self.assertEqual(len(cpp_interactions.processes()[0].basisSites()), 1) self.assertEqual(cpp_interactions.processes()[0].basisSites()[0], 0) self.assertEqual(len(cpp_interactions.processes()[1].basisSites()), 2) self.assertEqual(cpp_interactions.processes()[1].basisSites()[0], 0) self.assertEqual(cpp_interactions.processes()[1].basisSites()[1], 1)
def testBackendNoFailWrongBasisMatch(self): """ Test for no failure when constructing backend with wrong n_basis """ # A first process. coords = [[1.0,2.0,3.4],[1.1,1.2,1.3]] types0 = ["A","B"] types1 = ["B","A"] rate_0_1 = 3.5 process_0 = KMCProcess(coords, types0, types1, basis_sites=[0,4], rate_constant=rate_0_1) # A second process. types0 = ["A","C"] types1 = ["C","A"] rate_0_1 = 1.5 process_1 = KMCProcess(coords, types0, types1, basis_sites=[0,1], rate_constant=rate_0_1) processes = [process_0, process_1] # Construct the interactions object. kmc_interactions = KMCInteractions(processes=processes) # Setup a dict with the possible types. possible_types = { "A" : 13, "D" : 2, "B" : 3, "J" : 4, "C" : 5, } # Use a dummy argument for the configuration. config = "DummyConfig" # Get the backend - The [0,4] sites list for process_0 is simply ignored. cpp_interactions = kmc_interactions._backend(possible_types, 2, config) self.assertEqual( len(cpp_interactions.processes()[0].basisSites()), 1 ) self.assertEqual( cpp_interactions.processes()[0].basisSites()[0], 0 ) self.assertEqual( len(cpp_interactions.processes()[1].basisSites()), 2 ) self.assertEqual( cpp_interactions.processes()[1].basisSites()[0], 0 ) self.assertEqual( cpp_interactions.processes()[1].basisSites()[1], 1 )
def testBackendWithCustomRates(self): """ Test that we can construct the backend object with custom rates. """ # A first process. coords = [[1.0,2.0,3.4],[1.1,1.2,1.3]] types0 = ["A","B"] types1 = ["B","A"] rate_0_1 = 3.5 process_0 = KMCProcess(coords, types0, types1, basis_sites=[0], rate_constant=rate_0_1) # A second process. types0 = ["A","C"] types1 = ["C","A"] rate_0_1 = 1.5 process_1 = KMCProcess(coords, types0, types1, basis_sites=[0], rate_constant=rate_0_1) processes = [process_0, process_1] # Set the custom rates class to use. custom_rates_class = CustomRateCalculator # Set the rate function on the custom rates calculator for testing. ref_rnd = numpy.random.uniform(0.0,1.0) def testRateFunction(obj, coords, types_before, types_after, rate_const, process_number, global_coordinate): return ref_rnd # Store the original. custom_rate_function = custom_rates_class.rate try: custom_rates_class.rate = testRateFunction # Construct the interactions object. kmc_interactions = KMCInteractions(processes=processes, implicit_wildcards=False) # Setup a dict with the possible types. possible_types = { "A" : 13, "D" : 2, "B" : 3, "J" : 4, "C" : 5, } # Use a dummy argument for the configuration. config = "DummyConfig" # Test that it fails if the rate calculator is of wrong class. kmc_interactions.setRateCalculator(rate_calculator=Error) self.assertRaises( Error, lambda : kmc_interactions._backend(possible_types, 2, config) ) # Test that it fails if the rate calculator is the base class. kmc_interactions.setRateCalculator(rate_calculator=KMCRateCalculatorPlugin) self.assertRaises( Error, lambda : kmc_interactions._backend(possible_types, 2, config) ) # But this should work. kmc_interactions.setRateCalculator(rate_calculator=custom_rates_class) # Construct the backend. cpp_interactions = kmc_interactions._backend(possible_types, 2, config) # Test that the configuration on the custom rate class is the one given. self.assertTrue(kmc_interactions._KMCInteractions__rate_calculator.configuration == config) # <- Check by reference. # Get the rate calculator reference out of the C++ object and # check that a call from C++ calls the Python extension. cpp_coords = Backend.StdVectorDouble() cpp_types1 = Backend.StdVectorString() cpp_types2 = Backend.StdVectorString() rate_constant = 543.2211 process_number = 33 coordinate = (1.2,3.4,5.6) self.assertAlmostEqual( cpp_interactions.rateCalculator().backendRateCallback(cpp_coords, cpp_coords.size()/3, cpp_types1, cpp_types2, rate_constant, process_number, coordinate[0], coordinate[1], coordinate[2]), ref_rnd, 12 ) self.assertAlmostEqual( kmc_interactions._KMCInteractions__rate_calculator.backendRateCallback(cpp_coords, cpp_coords.size()/3, cpp_types1, cpp_types2, rate_constant, process_number, coordinate[0], coordinate[1], coordinate[2]), ref_rnd, 12 ) finally: # Reset the class. custom_rates_class.rate = custom_rate_function # Construct a C++ RateCalculator object directly and check that this object # returns the rate given to it. cpp_rate_calculator = Backend.RateCalculator() self.assertAlmostEqual( cpp_rate_calculator.backendRateCallback(cpp_coords, cpp_coords.size()/3, cpp_types1, cpp_types2, rate_constant, process_number, coordinate[0], coordinate[1], coordinate[2]), rate_constant, 12 )
def testBackendBuckets(self): """ Test that the backend behaves as expected with bucket processes. """ # A first process. coords = [[1.0,2.0,3.4],[12.0,13.0,-1.0],[1.1,1.2,1.3]] minimum_match = ["A","*","B"] update = [[(-1,"A"), (1, "B")],[(0,"*")],[(1,"A"), (-1, "B")]] rate_0_1 = 3.5 process_0 = KMCBucketProcess(coordinates=coords, minimum_match=minimum_match, update=update, basis_sites=[0,1,3], rate_constant=rate_0_1) # A second process. coords = [[1.0,2.0,3.4],[12.0,13.0,-1.0],[1.1,1.2,1.3]] minimum_match = ["A","*","C"] update = [[(-1,"A"), (1, "C")],[(0,"*")],[(1,"A"), (-1, "C")]] rate_0_1 = 1.5 process_1 = KMCBucketProcess(coordinates=coords, minimum_match=minimum_match, update=update, basis_sites=[0,1,3], rate_constant=rate_0_1) processes = [process_0, process_1] # Construct the interactions object. kmc_interactions = KMCInteractions(processes=processes) # Setup a dict with the possible types. possible_types = { "A" : 0, "B" : 1, "J" : 2, "C" : 3, } # Use a dummy argument for the configuration. config = "DummyConfig" # Check that setting up the backend fails if we have types in the processes that do # not corresponds to a type in the list of possible types. self.assertRaises( Error, lambda : kmc_interactions._backend(possible_types, 2, config) ) possible_types = { "A" : 0, "B" : 1, "D" : 2, "J" : 3, "C" : 4, } self.assertRaises( Error, lambda : kmc_interactions._backend(possible_types, 2, config) ) possible_types = { "A" : 0, "B" : 1, "F" : 2, "J" : 3, "C" : 4, } self.assertRaises( Error, lambda : kmc_interactions._backend(possible_types, 2, config) ) # Both "D" and "F" must be present, and the widcard "*". possible_types["D"] = 5 possible_types["*"] = 6 # Get the backend. cpp_interactions = kmc_interactions._backend(possible_types, 2, config) # Check the type. self.assertTrue( isinstance(cpp_interactions, Backend.Interactions) ) # Get the processes out. cpp_processes = cpp_interactions.processes() # Check the length of the processes. self.assertEqual(cpp_processes.size(), 2) # Get the elements out of the second process. update_types = cpp_processes[1].processMatchList()[0].update_types # This is an "A" match_types = cpp_processes[1].processMatchList()[0].match_types self.assertEqual(match_types[0], 1) self.assertEqual(match_types[1], 0) self.assertEqual(match_types[2], 0) self.assertEqual(match_types[3], 0) self.assertEqual(match_types[4], 0) self.assertEqual(match_types[5], 0) self.assertEqual(match_types[6], 0) # This is an "C" match_types = cpp_processes[1].processMatchList()[1].match_types self.assertEqual(match_types[0], 0) self.assertEqual(match_types[1], 0) self.assertEqual(match_types[2], 0) self.assertEqual(match_types[3], 0) self.assertEqual(match_types[4], 1) self.assertEqual(match_types[5], 0) self.assertEqual(match_types[6], 0) # This is a "*" match_types = cpp_processes[1].processMatchList()[2].match_types self.assertEqual(match_types[0], 0) self.assertEqual(match_types[1], 0) self.assertEqual(match_types[2], 0) self.assertEqual(match_types[3], 0) self.assertEqual(match_types[4], 0) self.assertEqual(match_types[5], 0) self.assertEqual(match_types[6], 1)
def testBackend(self): """ Test that the generated backend object is what we expect. """ # A first process. coords = [[1.0,2.0,3.4],[12.0,13.0,-1.0],[1.1,1.2,1.3]] types0 = ["A","D","B"] types1 = ["B","F","A"] rate_0_1 = 3.5 process_0 = KMCProcess(coords, types0, types1, basis_sites=[0,1,3], rate_constant=rate_0_1) # A second process. coords = [[1.0,2.0,3.4],[1.1,1.2,1.3]] types0 = ["A","C"] types1 = ["C","A"] rate_0_1 = 1.5 process_1 = KMCProcess(coords, types0, types1, basis_sites=[0,1,3], rate_constant=rate_0_1) processes = [process_0, process_1] # Construct the interactions object. kmc_interactions = KMCInteractions(processes=processes) # Setup a dict with the possible types. possible_types = { "A" : 13, "B" : 3, "J" : 4, "C" : 5, } # Use a dummy argument for the configuration. config = "DummyConfig" # Check that setting up the backend fails if we have types in the processes that do # not corresponds to a type in the list of possible types. self.assertRaises( Error, lambda : kmc_interactions._backend(possible_types, 2, config) ) possible_types = { "A" : 13, "B" : 3, "D" : 2, "J" : 4, "C" : 5, } self.assertRaises( Error, lambda : kmc_interactions._backend(possible_types, 2, config) ) possible_types = { "A" : 13, "B" : 3, "F" : 2, "J" : 4, "C" : 5, } self.assertRaises( Error, lambda : kmc_interactions._backend(possible_types, 2, config) ) # Both "D" and "F" must be present. possible_types["D"] = 123 # Get the backend. cpp_interactions = kmc_interactions._backend(possible_types, 2, config) # Check the type. self.assertTrue( isinstance(cpp_interactions, Backend.Interactions) ) # Get the processes out. cpp_processes = cpp_interactions.processes() # Check the length of the processes. self.assertEqual(cpp_processes.size(), 2) # Get the elements out of the second process. match_types = cpp_processes[1].processMatchList()[0].match_types update_types = cpp_processes[1].processMatchList()[0].update_types # Match type should be "A" -> 15 and update type "C" -> 5 self.assertEqual( match_types[13], 1) self.assertEqual( update_types[5], 1) # Get the elements out of the second process. match_types = cpp_processes[1].processMatchList()[1].match_types update_types = cpp_processes[1].processMatchList()[1].update_types # Match type should be "C" -> 5 and update type "A" -> 13 self.assertEqual( match_types[5], 1) self.assertEqual( update_types[13], 1)
def testBackendWithCustomRates(self): """ Test that we can construct the backend object with custom rates. """ # A first process. coords = [[1.0, 2.0, 3.4], [1.1, 1.2, 1.3]] types0 = ["A", "B"] types1 = ["B", "A"] rate_0_1 = 3.5 process_0 = KMCProcess(coords, types0, types1, basis_sites=[0], rate_constant=rate_0_1) # A second process. types0 = ["A", "C"] types1 = ["C", "A"] rate_0_1 = 1.5 process_1 = KMCProcess(coords, types0, types1, basis_sites=[0], rate_constant=rate_0_1) processes = [process_0, process_1] # Set the custom rates class to use. custom_rates_class = CustomRateCalculator # Set the rate function on the custom rates calculator for testing. ref_rnd = numpy.random.uniform(0.0, 1.0) def testRateFunction(obj, coords, types_before, types_after, rate_const, process_number, global_coordinate): return ref_rnd # Store the original. custom_rate_function = custom_rates_class.rate try: custom_rates_class.rate = testRateFunction # Construct the interactions object. kmc_interactions = KMCInteractions(processes=processes, implicit_wildcards=False) # Setup a dict with the possible types. possible_types = { "A": 13, "D": 2, "B": 3, "J": 4, "C": 5, } # Use a dummy argument for the configuration. config = "DummyConfig" # Test that it fails if the rate calculator is of wrong class. kmc_interactions.setRateCalculator(rate_calculator=Error) self.assertRaises( Error, lambda: kmc_interactions._backend(possible_types, 2, config)) # Test that it fails if the rate calculator is the base class. kmc_interactions.setRateCalculator( rate_calculator=KMCRateCalculatorPlugin) self.assertRaises( Error, lambda: kmc_interactions._backend(possible_types, 2, config)) # But this should work. kmc_interactions.setRateCalculator( rate_calculator=custom_rates_class) # Construct the backend. cpp_interactions = kmc_interactions._backend( possible_types, 2, config) # Test that the configuration on the custom rate class is the one given. self.assertTrue( kmc_interactions._KMCInteractions__rate_calculator. configuration == config) # <- Check by reference. # Get the rate calculator reference out of the C++ object and # check that a call from C++ calls the Python extension. cpp_coords = Backend.StdVectorDouble() cpp_types1 = Backend.StdVectorString() cpp_types2 = Backend.StdVectorString() rate_constant = 543.2211 process_number = 33 coordinate = (1.2, 3.4, 5.6) self.assertAlmostEqual( cpp_interactions.rateCalculator().backendRateCallback( cpp_coords, cpp_coords.size() / 3, cpp_types1, cpp_types2, rate_constant, process_number, coordinate[0], coordinate[1], coordinate[2]), ref_rnd, 12) self.assertAlmostEqual( kmc_interactions._KMCInteractions__rate_calculator. backendRateCallback(cpp_coords, cpp_coords.size() / 3, cpp_types1, cpp_types2, rate_constant, process_number, coordinate[0], coordinate[1], coordinate[2]), ref_rnd, 12) finally: # Reset the class. custom_rates_class.rate = custom_rate_function # Construct a C++ RateCalculator object directly and check that this object # returns the rate given to it. cpp_rate_calculator = Backend.RateCalculator() self.assertAlmostEqual( cpp_rate_calculator.backendRateCallback( cpp_coords, cpp_coords.size() / 3, cpp_types1, cpp_types2, rate_constant, process_number, coordinate[0], coordinate[1], coordinate[2]), rate_constant, 12)
def testBackendBuckets(self): """ Test that the backend behaves as expected with bucket processes. """ # A first process. coords = [[1.0, 2.0, 3.4], [12.0, 13.0, -1.0], [1.1, 1.2, 1.3]] minimum_match = ["A", "*", "B"] update = [[(-1, "A"), (1, "B")], [(0, "*")], [(1, "A"), (-1, "B")]] rate_0_1 = 3.5 process_0 = KMCBucketProcess(coordinates=coords, minimum_match=minimum_match, update=update, basis_sites=[0, 1, 3], rate_constant=rate_0_1) # A second process. coords = [[1.0, 2.0, 3.4], [12.0, 13.0, -1.0], [1.1, 1.2, 1.3]] minimum_match = ["A", "*", "C"] update = [[(-1, "A"), (1, "C")], [(0, "*")], [(1, "A"), (-1, "C")]] rate_0_1 = 1.5 process_1 = KMCBucketProcess(coordinates=coords, minimum_match=minimum_match, update=update, basis_sites=[0, 1, 3], rate_constant=rate_0_1) processes = [process_0, process_1] # Construct the interactions object. kmc_interactions = KMCInteractions(processes=processes) # Setup a dict with the possible types. possible_types = { "A": 0, "B": 1, "J": 2, "C": 3, } # Use a dummy argument for the configuration. config = "DummyConfig" # Check that setting up the backend fails if we have types in the processes that do # not corresponds to a type in the list of possible types. self.assertRaises( Error, lambda: kmc_interactions._backend(possible_types, 2, config)) possible_types = { "A": 0, "B": 1, "D": 2, "J": 3, "C": 4, } self.assertRaises( Error, lambda: kmc_interactions._backend(possible_types, 2, config)) possible_types = { "A": 0, "B": 1, "F": 2, "J": 3, "C": 4, } self.assertRaises( Error, lambda: kmc_interactions._backend(possible_types, 2, config)) # Both "D" and "F" must be present, and the widcard "*". possible_types["D"] = 5 possible_types["*"] = 6 # Get the backend. cpp_interactions = kmc_interactions._backend(possible_types, 2, config) # Check the type. self.assertTrue(isinstance(cpp_interactions, Backend.Interactions)) # Get the processes out. cpp_processes = cpp_interactions.processes() # Check the length of the processes. self.assertEqual(cpp_processes.size(), 2) # Get the elements out of the second process. update_types = cpp_processes[1].processMatchList()[0].update_types # This is an "A" match_types = cpp_processes[1].processMatchList()[0].match_types self.assertEqual(match_types[0], 1) self.assertEqual(match_types[1], 0) self.assertEqual(match_types[2], 0) self.assertEqual(match_types[3], 0) self.assertEqual(match_types[4], 0) self.assertEqual(match_types[5], 0) self.assertEqual(match_types[6], 0) # This is an "C" match_types = cpp_processes[1].processMatchList()[1].match_types self.assertEqual(match_types[0], 0) self.assertEqual(match_types[1], 0) self.assertEqual(match_types[2], 0) self.assertEqual(match_types[3], 0) self.assertEqual(match_types[4], 1) self.assertEqual(match_types[5], 0) self.assertEqual(match_types[6], 0) # This is a "*" match_types = cpp_processes[1].processMatchList()[2].match_types self.assertEqual(match_types[0], 0) self.assertEqual(match_types[1], 0) self.assertEqual(match_types[2], 0) self.assertEqual(match_types[3], 0) self.assertEqual(match_types[4], 0) self.assertEqual(match_types[5], 0) self.assertEqual(match_types[6], 1)
def testBackend(self): """ Test that the generated backend object is what we expect. """ # A first process. coords = [[1.0, 2.0, 3.4], [12.0, 13.0, -1.0], [1.1, 1.2, 1.3]] types0 = ["A", "D", "B"] types1 = ["B", "F", "A"] rate_0_1 = 3.5 process_0 = KMCProcess(coords, types0, types1, basis_sites=[0, 1, 3], rate_constant=rate_0_1) # A second process. coords = [[1.0, 2.0, 3.4], [1.1, 1.2, 1.3]] types0 = ["A", "C"] types1 = ["C", "A"] rate_0_1 = 1.5 process_1 = KMCProcess(coords, types0, types1, basis_sites=[0, 1, 3], rate_constant=rate_0_1) processes = [process_0, process_1] # Construct the interactions object. kmc_interactions = KMCInteractions(processes=processes) # Setup a dict with the possible types. possible_types = { "A": 13, "B": 3, "J": 4, "C": 5, } # Use a dummy argument for the configuration. config = "DummyConfig" # Check that setting up the backend fails if we have types in the processes that do # not corresponds to a type in the list of possible types. self.assertRaises( Error, lambda: kmc_interactions._backend(possible_types, 2, config)) possible_types = { "A": 13, "B": 3, "D": 2, "J": 4, "C": 5, } self.assertRaises( Error, lambda: kmc_interactions._backend(possible_types, 2, config)) possible_types = { "A": 13, "B": 3, "F": 2, "J": 4, "C": 5, } self.assertRaises( Error, lambda: kmc_interactions._backend(possible_types, 2, config)) # Both "D" and "F" must be present. possible_types["D"] = 123 # Get the backend. cpp_interactions = kmc_interactions._backend(possible_types, 2, config) # Check the type. self.assertTrue(isinstance(cpp_interactions, Backend.Interactions)) # Get the processes out. cpp_processes = cpp_interactions.processes() # Check the length of the processes. self.assertEqual(cpp_processes.size(), 2) # Get the elements out of the second process. match_types = cpp_processes[1].processMatchList()[0].match_types update_types = cpp_processes[1].processMatchList()[0].update_types # Match type should be "A" -> 15 and update type "C" -> 5 self.assertEqual(match_types[13], 1) self.assertEqual(update_types[5], 1) # Get the elements out of the second process. match_types = cpp_processes[1].processMatchList()[1].match_types update_types = cpp_processes[1].processMatchList()[1].update_types # Match type should be "C" -> 5 and update type "A" -> 13 self.assertEqual(match_types[5], 1) self.assertEqual(update_types[13], 1)
def testBackendWithCustomRates(self): """ Test that we can construct the backend object with custom rates. """ # A first process. coords = [[1.0,2.0,3.4],[1.1,1.2,1.3]] types0 = ["A","B"] types1 = ["B","A"] rate_0_1 = 3.5 process_0 = KMCProcess(coords, types0, types1, basis_sites=[0], rate_constant=rate_0_1) # A second process. types0 = ["A","C"] types1 = ["C","A"] rate_0_1 = 1.5 process_1 = KMCProcess(coords, types0, types1, basis_sites=[0], rate_constant=rate_0_1) processes = [process_0, process_1] # Set the custom rates class to use. custom_rates_class = CustomRateCalculator # Construct the interactions object. kmc_interactions = KMCInteractions(processes=processes, implicit_wildcards=False) kmc_interactions.setRateCalculator(rate_calculator=custom_rates_class) # Set the rate function on the custom rates calculator for testing. ref_rnd = numpy.random.uniform(0.0,1.0) def testRateFunction(coords, types_before, types_after, rate_const, process_number, global_coordinate): return ref_rnd kmc_interactions._KMCInteractions__rate_calculator.rate = testRateFunction # Setup a dict with the possible types. possible_types = { "A" : 13, "D" : 2, "B" : 3, "J" : 4, "C" : 5, } # Construct the backend. cpp_interactions = kmc_interactions._backend(possible_types, 2) # Get the rate calculator reference out of the C++ object and # check that a call from C++ calls the Python extension. cpp_coords = Backend.StdVectorDouble() cpp_types1 = Backend.StdVectorString() cpp_types2 = Backend.StdVectorString() rate_constant = 543.2211 process_number = 33 coordinate = (1.2,3.4,5.6) self.assertAlmostEqual( cpp_interactions.rateCalculator().backendRateCallback(cpp_coords, cpp_coords.size()/3, cpp_types1, cpp_types2, rate_constant, process_number, coordinate[0], coordinate[1], coordinate[2]), ref_rnd, 12 ) self.assertAlmostEqual( kmc_interactions._KMCInteractions__rate_calculator.backendRateCallback(cpp_coords, cpp_coords.size()/3, cpp_types1, cpp_types2, rate_constant, process_number, coordinate[0], coordinate[1], coordinate[2]), ref_rnd, 12 ) # Construct a C++ RateCalculator object directly and check that this object # returns the rate given to it. cpp_rate_calculator = Backend.RateCalculator() self.assertAlmostEqual( cpp_rate_calculator.backendRateCallback(cpp_coords, cpp_coords.size()/3, cpp_types1, cpp_types2, rate_constant, process_number, coordinate[0], coordinate[1], coordinate[2]), rate_constant, 12 )
def testBackendWithCustomRates(self): """ Test that we can construct the backend object with custom rates. """ # A first process. coords = [[1.0, 2.0, 3.4], [1.1, 1.2, 1.3]] types0 = ["A", "B"] types1 = ["B", "A"] rate_0_1 = 3.5 process_0 = KMCProcess(coords, types0, types1, basis_sites=[0], rate_constant=rate_0_1) # A second process. types0 = ["A", "C"] types1 = ["C", "A"] rate_0_1 = 1.5 process_1 = KMCProcess(coords, types0, types1, basis_sites=[0], rate_constant=rate_0_1) processes = [process_0, process_1] # Set the custom rates class to use. custom_rates_class = CustomRateCalculator # Construct the interactions object. kmc_interactions = KMCInteractions(processes=processes, implicit_wildcards=False) kmc_interactions.setRateCalculator(rate_calculator=custom_rates_class) # Set the rate function on the custom rates calculator for testing. ref_rnd = numpy.random.uniform(0.0, 1.0) def testRateFunction(coords, types_before, types_after, rate_const, process_number, global_coordinate): return ref_rnd kmc_interactions._KMCInteractions__rate_calculator.rate = testRateFunction # Setup a dict with the possible types. possible_types = { "A": 13, "D": 2, "B": 3, "J": 4, "C": 5, } # Construct the backend. cpp_interactions = kmc_interactions._backend(possible_types, 2) # Get the rate calculator reference out of the C++ object and # check that a call from C++ calls the Python extension. cpp_coords = Backend.StdVectorDouble() cpp_types1 = Backend.StdVectorString() cpp_types2 = Backend.StdVectorString() rate_constant = 543.2211 process_number = 33 coordinate = (1.2, 3.4, 5.6) self.assertAlmostEqual( cpp_interactions.rateCalculator().backendRateCallback( cpp_coords, cpp_coords.size() / 3, cpp_types1, cpp_types2, rate_constant, process_number, coordinate[0], coordinate[1], coordinate[2]), ref_rnd, 12) self.assertAlmostEqual( kmc_interactions._KMCInteractions__rate_calculator. backendRateCallback(cpp_coords, cpp_coords.size() / 3, cpp_types1, cpp_types2, rate_constant, process_number, coordinate[0], coordinate[1], coordinate[2]), ref_rnd, 12) # Construct a C++ RateCalculator object directly and check that this object # returns the rate given to it. cpp_rate_calculator = Backend.RateCalculator() self.assertAlmostEqual( cpp_rate_calculator.backendRateCallback( cpp_coords, cpp_coords.size() / 3, cpp_types1, cpp_types2, rate_constant, process_number, coordinate[0], coordinate[1], coordinate[2]), rate_constant, 12)