From 8cc2dcc24a702e4005a084bcda2406a2f7718716 Mon Sep 17 00:00:00 2001
From: Rafael Ravedutti <rafaelravedutti@gmail.com>
Date: Fri, 15 Mar 2024 00:03:33 +0100
Subject: [PATCH] Add comments to simulation.py

Signed-off-by: Rafael Ravedutti <rafaelravedutti@gmail.com>
---
 src/pairs/sim/simulation.py | 89 +++++++++++++++++++++++++++++++------
 1 file changed, 75 insertions(+), 14 deletions(-)

diff --git a/src/pairs/sim/simulation.py b/src/pairs/sim/simulation.py
index 011b8b1..d8d7518 100644
--- a/src/pairs/sim/simulation.py
+++ b/src/pairs/sim/simulation.py
@@ -35,6 +35,8 @@ from pairs.transformations import Transformations
 
 
 class Simulation:
+    """P4IRS Simulation class, this class is the center of kernel simulations which contains all
+       fundamental data structures to generate a P4IRS simulation code"""
     def __init__(
         self,
         code_gen,
@@ -46,8 +48,10 @@ class Simulation:
         particle_capacity=800000,
         neighbor_capacity=100):
 
+        # Code generator for the simulation
         self.code_gen = code_gen
         self.code_gen.assign_simulation(self)
+        # Data structures to be generated
         self.position_prop = None
         self.properties = Properties(self)
         self.vars = Variables(self)
@@ -55,6 +59,8 @@ class Simulation:
         self.features = Features(self)
         self.feature_properties = FeatureProperties(self)
         self.contact_properties = ContactProperties(self)
+
+        # General capacities, sizes and particle properties
         self.particle_capacity = \
             self.add_var('particle_capacity', Types.Int32, particle_capacity, runtime=True)
         self.neighbor_capacity = self.add_var('neighbor_capacity', Types.Int32, neighbor_capacity)
@@ -64,45 +70,62 @@ class Simulation:
         self.particle_uid = self.add_property('uid', Types.Int32, 0)
         self.particle_shape = self.add_property('shape', Types.Int32, 0)
         self.particle_flags = self.add_property('flags', Types.Int32, 0)
+
+        # Grid for the simulation
         self.grid = None
+
+        # Acceleration structures
         self.cell_lists = None
         self._store_neighbors_per_cell = False
         self.neighbor_lists = None
+
+        # Context information used to partially build the program AST
         self.scope = []
         self.nested_count = 0
         self.nest = False
         self._capture_statements = True
         self._block = Block(self, [])
+
+        # Different segments of particle code/functions
         self.setups = Block(self, [])
         self.setup_functions = []
         self.pre_step_functions = []
         self.functions = []
         self.module_list = []
         self.kernel_list = []
+
+        # Structures to generated resize code for capacities
         self._check_properties_resize = False
         self._resizes_to_check = {}
-        self._module_name = None
-        self._double_prec = double_prec
-        self.dims = dims
-        self.ntimesteps = timesteps
-        self.expr_id = 0
-        self.iter_id = 0
-        self.reneighbor_frequency = 1
+
+        # VTK data
         self.vtk_file = None
         self.vtk_frequency = 0
+
+        # Domain partitioning
         self._dom_part = None
         self._partitioner = None
-        self._target = None
-        self._pbc = [True for _ in range(dims)]
+
+        # Contact history
         self._use_contact_history = use_contact_history
         self._contact_history = ContactHistory(self) if use_contact_history else None
-        self._shapes = shapes
-        self._compute_half = False
-        self._apply_list = None
-        self._enable_profiler = False
-        self._compute_thermo = 0
+
+
+        self._module_name = None                # Current module name
+        self._double_prec = double_prec         # Use double-precision FP arithmetic
+        self.dims = dims                        # Number of dimensions
+        self.ntimesteps = timesteps             # Number of time-steps
+        self.reneighbor_frequency = 1           # Re-neighbor frequency
+        self._target = None                     # Hardware target info
+        self._pbc = [True for _ in range(dims)] # PBC flags for each dimension
+        self._shapes = shapes                   # List of shapes used in the simulation
+        self._compute_half = False              # Compute half of interactions (Newton 3D Law)
+        self._apply_list = None                 # Context elements when using apply() directive
+        self._enable_profiler = False           # Enable/disable profiler
+        self._compute_thermo = 0                # Compute thermo information
 
     def set_domain_partitioner(self, partitioner):
+        """Selects domain-partitioner used and create its object for this simulation instance"""
         self._partitioner = partitioner
 
         if partitioner in (DomainPartitioners.Regular, DomainPartitioners.RegularXY):
@@ -138,6 +161,8 @@ class Simulation:
             self.module_list.append(module)
 
     def modules(self):
+        """List simulation modudles, with main always in the last position"""
+
         sorted_mods = []
         main_mod = None
         for m in self.module_list:
@@ -240,23 +265,29 @@ class Simulation:
         self.setups.add_statement(ParticleLattice(self, grid, spacing, props, self.position()))
 
     def read_particle_data(self, filename, prop_names, shape_id):
+        """Generate statement to read particle data from file"""
         props = [self.property(prop_name) for prop_name in prop_names]
         self.setups.add_statement(ReadParticleData(self, filename, props, shape_id))
 
     def copper_fcc_lattice(self, nx, ny, nz, rho, temperature, ntypes):
+        """Specific initialization for MD Copper FCC lattice case"""
         self.setups.add_statement(CopperFCCLattice(self, nx, ny, nz, rho, temperature, ntypes))
 
     def dem_sc_grid(self, xmax, ymax, zmax, spacing, diameter, min_diameter, max_diameter, initial_velocity, particle_density, ntypes):
+        """Specific initialization for DEM grid"""
         self.setups.add_statement(
             DEMSCGrid(self, xmax, ymax, zmax, spacing, diameter, min_diameter, max_diameter,
                       initial_velocity, particle_density, ntypes))
 
     def build_cell_lists(self, spacing, store_neighbors_per_cell=False):
+        """Add routines to build the linked-cells acceleration structure"""
         self._store_neighbors_per_cell = store_neighbors_per_cell
         self.cell_lists = CellLists(self, self._dom_part, spacing, spacing)
         return self.cell_lists
 
     def build_neighbor_lists(self, spacing):
+        """Add routines to build the Verlet Lists acceleration structure"""
+
         assert self._store_neighbors_per_cell is False, \
             "Using neighbor-lists with store_neighbors_per_cell option is invalid."
 
@@ -271,6 +302,7 @@ class Simulation:
         return setup(self, func, symbols)
 
     def init_block(self):
+        """Initialize new block in this simulation instance"""
         self._block = Block(self, [])
         self._check_properties_resize = False
         self._resizes_to_check = {}
@@ -280,15 +312,20 @@ class Simulation:
         self._module_name = name
 
     def check_properties_resize(self):
+        """Enable checking properties for resizing"""
         self._check_properties_resize = True
 
     def check_resize(self, capacity, size):
+        """Determine that capacity must always be checked with respect to size in a block/module"""
+
         if capacity not in self._resizes_to_check:
             self._resizes_to_check[capacity] = size
         else:
             raise Exception("Two sizes assigned to same capacity!")
 
     def build_setup_module_with_statements(self):
+        """Build a Module in the setup part of the program using the last initialized block"""
+
         self.setup_functions.append(
             Module(self,
                 name=self._module_name,
@@ -298,6 +335,7 @@ class Simulation:
                 run_on_device=False))
 
     def build_pre_step_module_with_statements(self, run_on_device=True, skip_first=False, profile=False):
+        """Build a Module in the pre-step part of the program using the last initialized block"""
         module = Module(self, name=self._module_name,
                               block=Block(self, self._block),
                               resizes_to_check=self._resizes_to_check,
@@ -314,6 +352,7 @@ class Simulation:
             self.pre_step_functions.append(module)
 
     def build_module_with_statements(self, run_on_device=True, skip_first=False, profile=False):
+        """Build a Module in the compute part of the program using the last initialized block"""
         module = Module(self, name=self._module_name,
                               block=Block(self, self._block),
                               resizes_to_check=self._resizes_to_check,
@@ -329,9 +368,11 @@ class Simulation:
             self.functions.append(module)
 
     def capture_statements(self, capture=True):
+        """When toggled, all constructed statements are captured and automatically added to the last initialized block"""
         self._capture_statements = capture
 
     def add_statement(self, stmt):
+        """Add captured statements to the last block when _capture_statements is toggled"""
         if self._capture_statements:
             if not self.scope:
                 self._block.add_statement(stmt)
@@ -341,6 +382,7 @@ class Simulation:
         return stmt
 
     def nest_mode(self):
+        """When explicitly constructing loops in P4IRS, make them nested"""
         self.nested_count = 0
         self.nest = True
         yield
@@ -349,9 +391,11 @@ class Simulation:
             self.scope.pop()
 
     def enter(self, scope):
+        """Enter a new scope, used for tracking scopes when building P4IRS AST elements"""
         self.scope.append(scope)
 
     def leave(self):
+        """Leave last scope, used for tracking scopes when building P4IRS AST elements"""
         if not self.nest:
             self.scope.pop()
         else:
@@ -384,10 +428,16 @@ class Simulation:
         self._compute_thermo = every
 
     def generate(self):
+        """Generate the code for the simulation"""
+
         assert self._target is not None, "Target not specified!"
+
+        # Initialize communication instance with specified domain-partitioner
         comm = Comm(self, self._dom_part)
+        # Params that determine when a method must be called only when reneighboring
         every_reneighbor_params = {'every': self.reneighbor_frequency}
 
+        # First steps executed during each time-step in the simulation
         timestep_procedures = self.pre_step_functions + [
             (comm.exchange(), every_reneighbor_params),
             (comm.borders(), comm.synchronize(), every_reneighbor_params),
@@ -395,14 +445,17 @@ class Simulation:
             (PartitionCellLists(self, self.cell_lists), every_reneighbor_params)
         ]
 
+        # Add routine to build neighbor-lists per cell
         if self._store_neighbors_per_cell:
             timestep_procedures.append(
                 (BuildCellNeighborLists(self, self.cell_lists), every_reneighbor_params))
 
+        # Add routine to build neighbor-lists per particle (standard Verlet Lists)
         if self.neighbor_lists is not None:
             timestep_procedures.append(
                 (BuildNeighborLists(self, self.neighbor_lists), every_reneighbor_params))
 
+        # Add routines for contact history management
         if self._use_contact_history:
             if self.neighbor_lists is not None:
                 timestep_procedures.append(
@@ -411,23 +464,29 @@ class Simulation:
 
             timestep_procedures.append(ResetContactHistoryUsageStatus(self, self._contact_history))
 
+        # Reset volatile properties and add computational kernels
         timestep_procedures += [ResetVolatileProperties(self)] + self.functions
 
+        # Clear unused contact history
         if self._use_contact_history:
             timestep_procedures.append(ClearUnusedContactHistory(self, self._contact_history))
 
+        # Add routine to calculate thermal data
         if self._compute_thermo != 0:
             timestep_procedures.append(
                 (ComputeThermo(self), {'every': self._compute_thermo}))
 
+        # Construct the time-step loop
         timestep = Timestep(self, self.ntimesteps, timestep_procedures)
         self.enter(timestep.block)
 
+        # Add routine to write VTK data when set
         if self.vtk_file is not None:
             timestep.add(VTKWrite(self, self.vtk_file, timestep.timestep(), self.vtk_frequency))
 
         self.leave()
 
+        # Initialization and setup functions, together with time-step loop
         body = Block.from_list(self, [
             self.setups,
             self.setup_functions,
@@ -435,6 +494,7 @@ class Simulation:
             timestep.as_block()
         ])
 
+        # Data structures and timer/markers initialization
         inits = Block.from_list(self, [
             DeclareVariables(self),
             DeclareArrays(self),
@@ -445,6 +505,7 @@ class Simulation:
             RegisterMarkers(self)
         ])
 
+        # Combine everything into a whole program
         program = Module(self, name='main', block=Block.merge_blocks(inits, body))
 
         # Apply transformations
-- 
GitLab