Coverage for gamdpy/runtime_actions/runtime_action.py: 80%
59 statements
« prev ^ index » next coverage.py v7.4.4, created at 2025-06-14 15:25 +0200
« prev ^ index » next coverage.py v7.4.4, created at 2025-06-14 15:25 +0200
1from numba import cuda
2from abc import ABC, abstractmethod
3from gamdpy import Configuration
4from typing import Callable
7class RuntimeAction(ABC):
8 """
9 Abstract Base Class specifying the requirements for a runtime_action, i.e. an action to compiled into to innner MD kernel
10 """
12 def get_compute_flags(self):
13 return None
15 def setup(self, configuration: Configuration, num_timeblocks: int, steps_per_timeblock: int, output, verbose=False) -> None:
16 pass
18 @abstractmethod
19 def get_prestep_kernel(self, configuration: Configuration, compute_plan: dict) -> Callable:
20 """
21 Get a kernel (or python function depending on compute_plan["gridsync"]) that implements the runtime_action.
22 The generated kernel is called after evaluation of interactions, before intergration step is performed, see class Simulation
23 """
25 pass
27 @abstractmethod
28 def get_poststep_kernel(self, configuration: Configuration, compute_plan: dict) -> Callable:
29 """
30 Get a kernel (or python function depending on compute_plan["gridsync"]) that implements the runtime_action
31 The generated kernel is called immediately after evaluation intergration step is performed, see class Simulation
32 """
34 pass
36 @abstractmethod
37 def get_params(self, configuration: Configuration, compute_plan: dict) -> tuple :
38 """
39 Get a tuple with the parameters expected by the associated kernel
40 """
42 pass
44 def update_at_end_of_timeblock(self, timeblock: int, output_reference):
45 """
46 Method to be called at the end of a timeblock, for e.g. saving data to a file if needed
47 """
49 pass
51 def initialize_before_timeblock(self, timeblock: int, output_reference):
52 """
53 Method to be called before each timeblock
54 """
56 pass
58def merge_runtime_actions(configuration: Configuration, prestep_kernelA: Callable, poststep_kernelA: Callable, paramsA: tuple, actionB: RuntimeAction, compute_plan: dict) -> tuple[Callable, Callable, tuple] :
59 paramsB = actionB.get_params(configuration, compute_plan)
60 prestep_kernelB = actionB.get_prestep_kernel(configuration, compute_plan)
61 poststep_kernelB = actionB.get_poststep_kernel(configuration, compute_plan)
63 if compute_plan['gridsync']:
64 # A device function, calling a number of device functions, using gridsync to syncronize
65 @cuda.jit( device=compute_plan['gridsync'])
66 def prestep_kernel(grid, vectors, scalars, r_im, sim_box, step, runtime_actions_params):
67 prestep_kernelA(grid, vectors, scalars, r_im, sim_box, step, runtime_actions_params[0])
68 grid.sync() # Not always necessary !!!
69 prestep_kernelB(grid, vectors, scalars, r_im, sim_box, step, runtime_actions_params[1])
70 return
72 @cuda.jit( device=compute_plan['gridsync'])
73 def poststep_kernel(grid, vectors, scalars, r_im, sim_box, step, runtime_actions_params):
74 poststep_kernelA(grid, vectors, scalars, r_im, sim_box, step, runtime_actions_params[0])
75 grid.sync() # Not always necessary !!!
76 poststep_kernelB(grid, vectors, scalars, r_im, sim_box, step, runtime_actions_params[1])
77 return
79 return prestep_kernel, poststep_kernel, (paramsA, paramsB, )
80 else:
81 # Two python function, making several kernel calls to syncronize
82 def prestep_kernel(grid, vectors, scalars, r_im, sim_box, step, runtime_actions_params):
83 prestep_kernelA(grid, vectors, scalars, r_im, sim_box, step, runtime_actions_params[0])
84 prestep_kernelB(grid, vectors, scalars, r_im, sim_box, step, runtime_actions_params[1])
85 return
87 def poststep_kernel(grid, vectors, scalars, r_im, sim_box, step, runtime_actions_params):
88 poststep_kernelA(grid, vectors, scalars, r_im, sim_box, step, runtime_actions_params[0])
89 poststep_kernelB(grid, vectors, scalars, r_im, sim_box, step, runtime_actions_params[1])
90 return
92 return prestep_kernel, poststep_kernel, (paramsA, paramsB, )
94def add_runtime_actions_list(configuration: Configuration, runtime_actions_list: list[RuntimeAction], compute_plan: dict, verbose: bool = False) -> tuple[Callable, Callable, tuple]:
96 # Setup first interaction and cuda.jit it if gridsync is used for syncronization
97 params = runtime_actions_list[0].get_params(configuration, compute_plan)
98 prestep_kernel = runtime_actions_list[0].get_prestep_kernel(configuration, compute_plan)
99 poststep_kernel = runtime_actions_list[0].get_poststep_kernel(configuration, compute_plan)
101 if compute_plan['gridsync']:
102 prestep_kernel: Callable = cuda.jit( device=compute_plan['gridsync'] )(prestep_kernel)
103 poststep_kernel: Callable = cuda.jit( device=compute_plan['gridsync'] )(poststep_kernel)
105 # Merge in the rest of the runtime_actions (maximum recursion depth might set a maximum for number of interactions)
106 for i in range(1, len(runtime_actions_list)):
107 prestep_kernel, poststep_kernel, params = merge_runtime_actions(configuration, prestep_kernel, poststep_kernel, params, runtime_actions_list[i], compute_plan)
109 return prestep_kernel, poststep_kernel, params