From 6da0d42e698312968d5a90d1ab5faac2377f35bf Mon Sep 17 00:00:00 2001
From: Behzad Safaei <iwia103h@a0522.nhr.fau.de>
Date: Sun, 4 May 2025 01:04:57 +0200
Subject: [PATCH] Add option for scaling the ballast app

---
 examples/modular/ballast/ballast.cpp | 79 ++++++++++++++++++++--------
 examples/modular/ballast/ballast.py  |  3 +-
 runtime/utility/read_from_file.cpp   | 14 +++--
 runtime/utility/read_from_file.hpp   |  2 +-
 4 files changed, 70 insertions(+), 28 deletions(-)

diff --git a/examples/modular/ballast/ballast.cpp b/examples/modular/ballast/ballast.cpp
index 20e55af..fbbf79c 100644
--- a/examples/modular/ballast/ballast.cpp
+++ b/examples/modular/ballast/ballast.cpp
@@ -33,17 +33,26 @@ void set_feature_properties(std::shared_ptr<PairsAccessor> &ac){
     ac->syncTypeDampingTan();
 }
 
+void print_usage(){
+    std::cerr << "Required args: num_beds_x num_beds_y num_timesteps" << std::endl;
+    exit(-1);
+}
+
 int main(int argc, char **argv) {
+    if (argc<4) print_usage();
+
     auto pairs_sim = std::make_shared<PairsSimulation>();
     pairs_sim->initialize();
     auto ac = std::make_shared<PairsAccessor>(pairs_sim.get());
     auto pairs_runtime = pairs_sim->getPairsRuntime();
 
     // ===================================================================================================
-    // Domain
+    // Domain 
     // ===================================================================================================
-    double domain_size[3] = {1.5, 1.0, 0.8};
-    pairs_runtime->initDomain(&argc, &argv, 0, 0, 0, domain_size[0], domain_size[1], domain_size[2], false); 
+    int nbeds[2] = {std::stoi(argv[1]), std::stoi(argv[2])};
+    double bed_size[3] = {1.5, 1.0, 0.65};
+    double domain_size[3] = {bed_size[0]*nbeds[0], bed_size[1]*nbeds[1], bed_size[2]};
+    pairs_runtime->initDomain(&argc, &argv, 0, 0, 0, domain_size[0], domain_size[1], domain_size[2], false);
 
     // ===================================================================================================
     // Spheres
@@ -61,7 +70,14 @@ int main(int argc, char **argv) {
 
     // Settled bed with box in place
     // ------------------------------------------------------------------
-    pairs::read_spheres(pairs_runtime, "../examples/modular/ballast/spheres_bed_with_box.txt");
+    for(int i=0; i<nbeds[0]; ++i){
+        for(int j=0; j<nbeds[1]; ++j){
+            pairs::read_spheres(pairs_runtime, 
+                "../examples/modular/ballast/spheres_bed_with_box.txt", 
+                {i*bed_size[0], j*bed_size[1], 0.0});
+
+        }
+    }
 
     // ===================================================================================================
     // Box (sleeper)
@@ -70,10 +86,21 @@ int main(int argc, char **argv) {
     double box_density = 8000;
     double box_size[3] = {0.4, 0.3, 0.15};
     double box_pos[3] = {0.779191, 0.497293, 0.484325};
-    auto box_uid = pairs::create_box(pairs_runtime, box_pos[0], box_pos[1], box_pos[2],
-                                        0, 0, 0, 
-                                        box_size[0], box_size[1], box_size[2],  
-                                        box_density, 1, pairs::flags::GLOBAL); 
+    int num_boxes = nbeds[0] * nbeds[1];
+    pairs::id_t box_uid[num_boxes];
+    for(int i=0; i<nbeds[0]; ++i){
+        for(int j=0; j<nbeds[1]; ++j){
+            box_uid[i*nbeds[1] + j] = pairs::create_box(pairs_runtime, 
+                box_pos[0] + i*bed_size[0], 
+                box_pos[1] + j*bed_size[1], 
+                box_pos[2],
+                0, 0, 0, 
+                box_size[0], box_size[1], box_size[2],  
+                box_density, 1, pairs::flags::GLOBAL); 
+        }
+    }
+
+    pairs::Vector3<double> external_force(100, 0, 0);
 
     // ===================================================================================================
     // Halfspace (bottom plate)
@@ -92,7 +119,7 @@ int main(int argc, char **argv) {
     pairs_sim->update_mass_and_inertia();
     pairs_sim->updateDomain();
 
-    int num_timesteps = 500000; 
+    int num_timesteps = std::stoi(argv[3]); 
     int vtk_freq = 5000;
     double dt = 1e-5;
     set_feature_properties(ac);
@@ -127,14 +154,15 @@ int main(int argc, char **argv) {
 
         // Apply external force to Box
         // ------------------------------------------------------------------
-        auto box_idx = ac->uidToIdxLocal(box_uid);
-        ac->syncForce(PairsAccessor::Host);
-        ac->syncTorque(PairsAccessor::Host);
-        pairs::Vector3<double> external_force(500, 0, 0);
-        ac->setForce(box_idx, ac->getForce(box_idx) + external_force);
-        ac->setTorque(box_idx, {0.0, 0.0, 0.0});    // Reset torque to prevent rotation
-        ac->syncForce(PairsAccessor::Host);
-        ac->syncTorque(PairsAccessor::Host);
+        for(int i=0; i<num_boxes; ++i){
+            auto box_idx = ac->uidToIdxLocal(box_uid[i]);
+            ac->syncForce(PairsAccessor::Host);
+            ac->syncTorque(PairsAccessor::Host);
+            ac->setForce(box_idx, ac->getForce(box_idx) + external_force);
+            ac->setTorque(box_idx, {0.0, 0.0, 0.0});    // Reset torque to prevent rotation
+            ac->syncForce(PairsAccessor::Host);
+            ac->syncTorque(PairsAccessor::Host);
+        }
 
         // Update positions and reneighbor
         // ------------------------------------------------------------------
@@ -147,9 +175,15 @@ int main(int argc, char **argv) {
             pairs::vtk_write_data(pairs_runtime, "output/local_spheres", 0, pairs_sim->nlocal(), t);
 
             if(rank==0){
+                auto runtime = std::chrono::duration<double>(std::chrono::high_resolution_clock::now() - start).count();
+                std::cout << "Timestep: " << t << " - Time = " << dt*t << " - Runtime = " << runtime << std::endl;
                 ac->syncPosition(PairsAccessor::Host);
-                std::cout << "Timestep: " << t << " - Time = " << dt*t << " - Box position = " << ac->getPosition(box_idx) << std::endl;
+                for(int i=0; i<num_boxes; ++i){
+                    auto box_idx = ac->uidToIdxLocal(box_uid[i]);
+                    std::cout << "\t Box (" << i << ") position = " << ac->getPosition(box_idx) << std::endl;
+                }
                 pairs::vtk_with_rotation(pairs_runtime, pairs::Shapes::Box, "output/local_boxes", 0, pairs_sim->nlocal(), t);
+                
             }
         }
     }
@@ -172,12 +206,15 @@ int main(int argc, char **argv) {
         std::cout << "PUPS: " << pups << std::endl;
 
         ac->syncUid(PairsAccessor::Host);
-        auto plate_idx = ac->uidToIdxLocal(plate_uid);
-        auto box_idx = ac->uidToIdxLocal(box_uid);
+        for(int i=0; i<num_boxes; ++i){
+            auto box_idx = ac->uidToIdxLocal(box_uid[i]);
+            std::cout << "box (" << i << ") final position = " << ac->getPosition(box_idx) << std::endl;
+        }
 
-        std::cout << "box final position = " << ac->getPosition(box_idx) << std::endl;
+        auto plate_idx = ac->uidToIdxLocal(plate_uid);
         std::cout << "plate final position = " << ac->getPosition(plate_idx) << std::endl;
     }
+    pairs::log_timers(pairs_runtime);
 
     ac->end();
     pairs_sim->end();
diff --git a/examples/modular/ballast/ballast.py b/examples/modular/ballast/ballast.py
index 2950b80..0fb1571 100644
--- a/examples/modular/ballast/ballast.py
+++ b/examples/modular/ballast/ballast.py
@@ -99,7 +99,8 @@ psim.add_feature_property('type', 'damping_tan', pairs.real())
 psim.add_feature_property('type', 'friction', pairs.real())
 
 # psim.set_domain_partitioner(pairs.block_forest())
-psim.set_domain_partitioner(pairs.regular_domain_partitioner())
+# psim.set_domain_partitioner(pairs.regular_domain_partitioner())
+psim.set_domain_partitioner(pairs.regular_domain_partitioner_xy())
 psim.pbc([True, True, False])
 psim.build_cell_lists()
 
diff --git a/runtime/utility/read_from_file.cpp b/runtime/utility/read_from_file.cpp
index c19695f..7e325a8 100644
--- a/runtime/utility/read_from_file.cpp
+++ b/runtime/utility/read_from_file.cpp
@@ -139,7 +139,7 @@ void write_spheres(PairsRuntime *pr, const char *filename){
     MPI_Barrier(MPI_COMM_WORLD);
 }
 
-void read_spheres(PairsRuntime *pr, const char *filename){
+void read_spheres(PairsRuntime *pr, const char *filename, std::array<double, 3> offset){
     int n = pr->getTrackedVariableAsInteger("nlocal");
     auto shapes = pr->getAsIntegerProperty(pr->getPropertyByName("shape"));
     auto flags = pr->getAsIntegerProperty(pr->getPropertyByName("flags"));
@@ -168,16 +168,20 @@ void read_spheres(PairsRuntime *pr, const char *filename){
         double mass;
         in_stream >> shape >> flag >> type >> x >> y >> z >> radius >> mass;
 
+        double shifted_posx = x + offset[0];
+        double shifted_posy = y + offset[1];
+        double shifted_posz = z + offset[2];
+
         if(shape == Shapes::Sphere){
-            bool within_subdom = pr->getDomainPartitioner()->isWithinSubdomain(x, y, z);
+            bool within_subdom = pr->getDomainPartitioner()->isWithinSubdomain(shifted_posx, shifted_posy, shifted_posz);
             if(within_subdom || (flag & (flags::INFINITE | flags::GLOBAL))){
                 shapes(n) = shape;
                 flags(n) = flag;
                 types(n) = type;
                 uids(n) = (flag & (flags::INFINITE |flags::GLOBAL)) ? UniqueID::createGlobal(pr) : UniqueID::create(pr);
-                positions(n,0) = x;
-                positions(n,1) = y;
-                positions(n,2) = z;
+                positions(n,0) = shifted_posx;
+                positions(n,1) = shifted_posy;
+                positions(n,2) = shifted_posz;
                 radii(n) = radius;
                 masses(n) = mass;
                 ++n;
diff --git a/runtime/utility/read_from_file.hpp b/runtime/utility/read_from_file.hpp
index f1cb633..ba7d681 100644
--- a/runtime/utility/read_from_file.hpp
+++ b/runtime/utility/read_from_file.hpp
@@ -14,7 +14,7 @@ namespace pairs {
 void write_boxes(PairsRuntime *pr, const char *filename);
 void read_boxes(PairsRuntime *pr, const char *filename);
 void write_spheres(PairsRuntime *pr, const char *filename);
-void read_spheres(PairsRuntime *pr, const char *filename);
+void read_spheres(PairsRuntime *pr, const char *filename, std::array<double, 3> offset = {0.0, 0.0, 0.0});
 
 void read_grid_data(PairsRuntime *ps, const char *filename, real_t *grid_buffer);
 
-- 
GitLab