From ffa2436b478796506e3ac6eae39a95d283b30643 Mon Sep 17 00:00:00 2001
From: Rafael Ravedutti <rafaelravedutti@gmail.com>
Date: Thu, 9 Nov 2023 03:12:42 +0100
Subject: [PATCH] Update LJ example

Signed-off-by: Rafael Ravedutti <rafaelravedutti@gmail.com>
---
 examples/dem.py               |  8 +++++++-
 examples/lj.py                | 13 ++++++-------
 src/pairs/__init__.py         |  7 +++++--
 src/pairs/mapping/keywords.py |  6 ++++++
 src/pairs/sim/cell_lists.py   |  8 +++++++-
 src/pairs/sim/comm.py         |  4 ++--
 src/pairs/sim/interaction.py  | 25 ++++++++++++++++++++-----
 src/pairs/sim/shapes.py       |  2 +-
 src/pairs/sim/simulation.py   |  8 ++++++--
 9 files changed, 60 insertions(+), 21 deletions(-)

diff --git a/examples/dem.py b/examples/dem.py
index 557440c..0ab64a3 100644
--- a/examples/dem.py
+++ b/examples/dem.py
@@ -131,7 +131,13 @@ lnDryResCoeff = math.log(restitutionCoefficient);
 frictionStatic = 0.0
 frictionDynamic = frictionCoefficient
 
-psim = pairs.simulation("dem", timesteps=timeSteps, double_prec=True, use_contact_history=True)
+psim = pairs.simulation(
+    "dem",
+    [pairs.sphere(), pairs.halfspace()],
+    timesteps=timeSteps,
+    double_prec=True,
+    use_contact_history=True)
+
 #psim = pairs.simulation("dem", debug=True, timesteps=timeSteps)
 psim.add_position('position')
 psim.add_property('mass', pairs.real(), 1.0)
diff --git a/examples/lj.py b/examples/lj.py
index fafdfd4..be5883c 100644
--- a/examples/lj.py
+++ b/examples/lj.py
@@ -26,17 +26,16 @@ sigma = 1.0
 epsilon = 1.0
 sigma6 = sigma ** 6
 
-psim = pairs.simulation("lj", debug=True, timesteps=200)
+psim = pairs.simulation("lj", [pairs.point_mass()], timesteps=200, double_prec=True)
 psim.add_position('position')
-psim.add_property('mass', pairs.double(), 1.0)
+psim.add_property('mass', pairs.real(), 1.0)
 psim.add_property('linear_velocity', pairs.vector())
 psim.add_property('force', pairs.vector(), volatile=True)
 psim.add_feature('type', ntypes)
-psim.add_feature_property('type', 'epsilon', pairs.double(), [sigma for i in range(ntypes * ntypes)])
-psim.add_feature_property('type', 'sigma6', pairs.double(), [epsilon for i in range(ntypes * ntypes)])
-#psim.set_domain(0.0, 0.0, 0.0, 6.7184, 6.7184, 6.7184) # for 4x4x4 setup
-psim.set_domain(0.0, 0.0, 0.0, 53.747078, 53.747078, 53.747078)
-psim.read_particle_data("data/minimd_setup_32x32x32.input", ['type', 'mass', 'position', 'linear_velocity', 'flags'], pairs.sphere())
+psim.add_feature_property('type', 'epsilon', pairs.real(), [sigma for i in range(ntypes * ntypes)])
+psim.add_feature_property('type', 'sigma6', pairs.real(), [epsilon for i in range(ntypes * ntypes)])
+psim.set_domain([0.0, 0.0, 0.0, 53.747078, 53.747078, 53.747078])
+psim.read_particle_data("data/minimd_setup_32x32x32.input", ['type', 'mass', 'position', 'linear_velocity', 'flags'], pairs.point_mass())
 psim.reneighbor_every(20)
 psim.build_neighbor_lists(cutoff_radius + skin)
 psim.vtk_output(f"output/lj_{target}")
diff --git a/src/pairs/__init__.py b/src/pairs/__init__.py
index b73e6e9..7ab7b1a 100644
--- a/src/pairs/__init__.py
+++ b/src/pairs/__init__.py
@@ -5,8 +5,8 @@ from pairs.sim.shapes import Shapes
 from pairs.sim.simulation import Simulation
 
 
-def simulation(ref, dims=3, timesteps=100, double_prec=False, use_contact_history=False, debug=False):
-    return Simulation(CGen(ref, debug), dims, timesteps, double_prec, use_contact_history)
+def simulation(ref, shapes, dims=3, timesteps=100, double_prec=False, use_contact_history=False, debug=False):
+    return Simulation(CGen(ref, debug), shapes, dims, timesteps, double_prec, use_contact_history)
 
 def target_cpu():
     return Target(Target.Backend_CPP, Target.Feature_CPU)
@@ -35,6 +35,9 @@ def matrix():
 def quaternion():
     return Types.Quaternion
 
+def point_mass():
+    return Shapes.PointMass
+
 def sphere():
     return Shapes.Sphere
 
diff --git a/src/pairs/mapping/keywords.py b/src/pairs/mapping/keywords.py
index 3754454..2d7917f 100644
--- a/src/pairs/mapping/keywords.py
+++ b/src/pairs/mapping/keywords.py
@@ -29,6 +29,12 @@ class Keywords:
         method = self.get_method(f"keyword_{keyword}")
         return method is not None
 
+    def keyword_is_point_mass(self, args):
+        assert len(args) == 1, "is_point_mass() keyword requires one parameter."
+        particle_id = args[0]
+        assert particle_id.type() == Types.Int32, "Particle ID must be an integer."
+        return ScalarOp.cmp(self.sim.particle_shape[particle_id], Shapes.PointMass)
+
     def keyword_is_sphere(self, args):
         assert len(args) == 1, "is_sphere() keyword requires one parameter."
         particle_id = args[0]
diff --git a/src/pairs/sim/cell_lists.py b/src/pairs/sim/cell_lists.py
index 955f622..697ab87 100644
--- a/src/pairs/sim/cell_lists.py
+++ b/src/pairs/sim/cell_lists.py
@@ -29,6 +29,7 @@ class CellLists:
         self.ncells_capacity    =   self.sim.add_var('ncells_capacity', Types.Int32, 100)
         self.cell_capacity      =   self.sim.add_var('cell_capacity', Types.Int32, 20)
         self.dim_ncells         =   self.sim.add_static_array('dim_cells', self.sim.ndims(), Types.Int32)
+        self.shapes_buffer      =   self.sim.add_static_array('shapes_buffer', self.sim.max_shapes(), Types.Int32)
         self.cell_particles     =   self.sim.add_array('cell_particles', [self.ncells_capacity, self.cell_capacity], Types.Int32)
         self.cell_sizes         =   self.sim.add_array('cell_sizes', self.ncells_capacity, Types.Int32)
         self.nshapes            =   self.sim.add_array('nshapes', [self.ncells_capacity, self.sim.max_shapes()], Types.Int32)
@@ -116,19 +117,24 @@ class PartitionCellLists(Lowerable):
     def lower(self):
         self.sim.module_name("partition_cell_lists")
         cell_particles = self.cell_lists.cell_particles
+        shapes_buffer = self.cell_lists.shapes_buffer
+
+        for s in range(self.sim.max_shapes()):
+            Assign(self.sim, shapes_buffer[s], self.sim.get_shape_id(s))
 
         for cell in For(self.sim, 0, self.cell_lists.ncells):
             start = self.sim.add_temp_var(0)
             end = self.sim.add_temp_var(0)
 
             for shape in For(self.sim, 0, self.sim.max_shapes()):
+                shape_id = shapes_buffer[shape]
                 shape_start = self.sim.add_temp_var(start)
                 Assign(self.sim, end, self.cell_lists.cell_sizes[cell] - 1)
 
                 for _ in While(self.sim, start <= end):
                     particle = cell_particles[cell][start]
 
-                    for unmatch in Branch(self.sim, ScalarOp.neq(self.sim.particle_shape[particle], shape)):
+                    for unmatch in Branch(self.sim, ScalarOp.neq(self.sim.particle_shape[particle], shape_id)):
                         if unmatch:
                             for _ in Filter(self.sim, ScalarOp.neq(start, end)):
                                 Assign(self.sim, cell_particles[cell][start], cell_particles[cell][end])
diff --git a/src/pairs/sim/comm.py b/src/pairs/sim/comm.py
index af60379..7e114ef 100644
--- a/src/pairs/sim/comm.py
+++ b/src/pairs/sim/comm.py
@@ -38,7 +38,7 @@ class Comm:
     def synchronize(self):
         # Every property that is not constant across timesteps and have neighbor accesses during any
         # interaction kernel (i.e. property[j] in force calculation kernel)
-        prop_list = [self.sim.property(p) for p in ['position', 'linear_velocity', 'angular_velocity']]
+        prop_list = [self.sim.property(p) for p in ['position', 'linear_velocity', 'angular_velocity'] if self.sim.property(p) is not None]
         for step in range(self.dom_part.number_of_steps()):
             PackGhostParticles(self, step, prop_list)
             CommunicateData(self, step, prop_list)
@@ -49,7 +49,7 @@ class Comm:
         # Every property that has neighbor accesses during any interaction kernel (i.e. property[j]
         # exists in any force calculation kernel)
         # We ignore normal because there should be no halfspace ghosts
-        prop_list = [self.sim.property(p) for p in ['uid', 'mass', 'radius', 'position', 'linear_velocity', 'angular_velocity', 'shape']]
+        prop_list = [self.sim.property(p) for p in ['uid', 'mass', 'radius', 'position', 'linear_velocity', 'angular_velocity', 'shape'] if self.sim.property(p) is not None]
         Assign(self.sim, self.nsend_all, 0)
         Assign(self.sim, self.sim.nghost, 0)
 
diff --git a/src/pairs/sim/interaction.py b/src/pairs/sim/interaction.py
index f76fdbf..02bf67f 100644
--- a/src/pairs/sim/interaction.py
+++ b/src/pairs/sim/interaction.py
@@ -158,8 +158,6 @@ class ParticleInteraction(Lowerable):
     def lower(self):
         if self.nbody == 2:
             position = self.sim.position()
-            radius = self.sim.property('radius')
-            normal = self.sim.property('normal')
             cell_lists = self.sim.cell_lists
             neighbor_lists = None if self.use_cell_lists else self.sim.neighbor_lists
 
@@ -169,9 +167,23 @@ class ParticleInteraction(Lowerable):
                     for neigh in NeighborFor(self.sim, i, cell_lists, neighbor_lists):
                         interaction_data = self.interactions_data[interaction]
                         shape = interaction_data.shape()
+                        shape_id = self.sim.get_shape_id(shape)
                         j = neigh.particle_index()
 
-                        if shape == Shapes.Sphere:
+                        if shape_id == Shapes.PointMass:
+                            delta = position[i] - position[j]
+                            squared_distance = delta.x() * delta.x() + \
+                                               delta.y() * delta.y() + \
+                                               delta.z() * delta.z()
+                            separation_dist = self.cutoff_radius
+                            cutoff_condition = squared_distance < self.cutoff_radius
+                            distance = Sqrt(self.sim, squared_distance)
+                            penetration_depth = None
+                            contact_normal = None
+                            contact_point = None
+
+                        elif shape_id == Shapes.Sphere:
+                            radius = self.sim.property('radius')
                             delta = position[i] - position[j]
                             squared_distance = delta.x() * delta.x() + \
                                                delta.y() * delta.y() + \
@@ -184,7 +196,10 @@ class ParticleInteraction(Lowerable):
                             k = radius[j] + 0.5 * penetration_depth
                             contact_point = position[j] + contact_normal * k
 
-                        elif shape == Shapes.Halfspace:
+                        elif shape_id == Shapes.Halfspace:
+                            radius = self.sim.property('radius')
+                            normal = self.sim.property('normal')
+
                             d = normal[j][0] * position[j][0] + \
                                 normal[j][1] * position[j][1] + \
                                 normal[j][2] * position[j][2]
@@ -200,7 +215,7 @@ class ParticleInteraction(Lowerable):
                             contact_point = position[i] - Vector(self.sim, [tmp, tmp, tmp]) * normal[j]
 
                         else:
-                            raise Exception("Invalid shape!")
+                            raise Exception("Invalid shape id.")
 
                         interaction_data.i().assign(i)
                         interaction_data.j().assign(j)
diff --git a/src/pairs/sim/shapes.py b/src/pairs/sim/shapes.py
index dbd37db..3f768c6 100644
--- a/src/pairs/sim/shapes.py
+++ b/src/pairs/sim/shapes.py
@@ -1,4 +1,4 @@
 class Shapes:
     Sphere      =   0
     Halfspace   =   1
-    PointMasses =   2
+    PointMass   =   2
diff --git a/src/pairs/sim/simulation.py b/src/pairs/sim/simulation.py
index d55722d..302868c 100644
--- a/src/pairs/sim/simulation.py
+++ b/src/pairs/sim/simulation.py
@@ -32,7 +32,7 @@ from pairs.transformations import Transformations
 
 
 class Simulation:
-    def __init__(self, code_gen, dims=3, timesteps=100, double_prec=False, use_contact_history=False):
+    def __init__(self, code_gen, shapes, dims=3, timesteps=100, double_prec=False, use_contact_history=False):
         self.code_gen = code_gen
         self.code_gen.assign_simulation(self)
         self.position_prop = None
@@ -79,12 +79,16 @@ class Simulation:
         self._pbc = [True for _ in range(dims)]
         self._use_contact_history = use_contact_history
         self._contact_history = ContactHistory(self) if use_contact_history else None
+        self._shapes = shapes
 
     def use_double_precision(self):
         return self._double_prec
 
+    def get_shape_id(self, shape):
+        return self._shapes[shape]
+
     def max_shapes(self):
-        return 2
+        return len(self._shapes)
 
     def add_module(self, module):
         assert isinstance(module, Module), "add_module(): Given parameter is not of type Module!"
-- 
GitLab