From 98a3381a9ffd825fa0fa64336b6adf5a9f688006 Mon Sep 17 00:00:00 2001 From: Behzad Safaei <iwia103h@alex1.nhr.fau.de> Date: Wed, 5 Feb 2025 15:49:48 +0100 Subject: [PATCH] Add samples for modular P4IRS --- examples/modular/sd_sample_1_CPU_GPU.cpp | 46 ++++++++ examples/modular/sd_sample_2_CPU_GPU.cpp | 68 ++++++++++++ examples/modular/sd_sample_3_CPU.cpp | 95 +++++++++++++++++ examples/modular/sd_sample_3_GPU.cu | 129 +++++++++++++++++++++++ examples/modular/spring_dashpot.py | 111 +++++++++++++++++++ 5 files changed, 449 insertions(+) create mode 100644 examples/modular/sd_sample_1_CPU_GPU.cpp create mode 100644 examples/modular/sd_sample_2_CPU_GPU.cpp create mode 100644 examples/modular/sd_sample_3_CPU.cpp create mode 100644 examples/modular/sd_sample_3_GPU.cu create mode 100644 examples/modular/spring_dashpot.py diff --git a/examples/modular/sd_sample_1_CPU_GPU.cpp b/examples/modular/sd_sample_1_CPU_GPU.cpp new file mode 100644 index 0000000..03b835c --- /dev/null +++ b/examples/modular/sd_sample_1_CPU_GPU.cpp @@ -0,0 +1,46 @@ +#include <iostream> +#include <memory> + +#include "spring_dashpot.hpp" + +int main(int argc, char **argv) { + + auto pairs_sim = std::make_shared<PairsSimulation>(); + pairs_sim->initialize(); + + pairs_sim->set_domain(argc, argv, 0, 0, 0, 1, 1, 1); + + pairs_sim->create_halfspace(0,0,0, 1, 0, 0, 0, 13); + pairs_sim->create_halfspace(0,0,0, 0, 1, 0, 0, 13); + pairs_sim->create_halfspace(0,0,0, 0, 0, 1, 0, 13); + pairs_sim->create_halfspace(1,1,1, -1, 0, 0, 0, 13); + pairs_sim->create_halfspace(1,1,1, 0, -1, 0, 0, 13); + pairs_sim->create_halfspace(1,1,1, 0, 0, -1, 0, 13); + pairs_sim->create_sphere(0.6, 0.6, 0.7, -2, -2, 0, 1000, 0.05, 0, 0); + pairs_sim->create_sphere(0.4, 0.4, 0.68, 2, 2, 0, 1000, 0.05, 0, 0); + + pairs_sim->setup_sim(); + + int num_timesteps = 2000; + int vtk_freq = 20; + double dt = 1e-3; + + for (int t=0; t<num_timesteps; ++t){ + if ((t%500==0) && pairs_sim->rank()==0) std::cout << "Timestep: " << t << std::endl; + + pairs_sim->communicate(t); + + pairs_sim->update_cells(); + + pairs_sim->gravity(); + pairs_sim->spring_dashpot(); + pairs_sim->euler(dt); + + pairs_sim->reset_volatiles(); + + pairs_sim->vtk_write("output/dem_sd_local", 0, pairs_sim->nlocal(), t, vtk_freq); + pairs_sim->vtk_write("output/dem_sd_ghost", pairs_sim->nlocal(), pairs_sim->size(), t, vtk_freq); + } + + pairs_sim->end(); +} diff --git a/examples/modular/sd_sample_2_CPU_GPU.cpp b/examples/modular/sd_sample_2_CPU_GPU.cpp new file mode 100644 index 0000000..330003f --- /dev/null +++ b/examples/modular/sd_sample_2_CPU_GPU.cpp @@ -0,0 +1,68 @@ +#include <iostream> +#include <memory> + +#include <blockforest/BlockForest.h> +#include <blockforest/Initialization.h> + +#include "spring_dashpot.hpp" + +int main(int argc, char **argv) { + + auto pairs_sim = std::make_shared<PairsSimulation>(); + pairs_sim->initialize(); + + // Create forest + // ------------------------------------------------------------------------------- + walberla::math::AABB domain(0, 0, 0, 1, 1, 1); + std::shared_ptr<walberla::mpi::MPIManager> mpiManager = walberla::mpi::MPIManager::instance(); + mpiManager->initializeMPI(&argc, &argv); + mpiManager->useWorldComm(); + auto procs = mpiManager->numProcesses(); + + walberla::Vector3<int> block_config; + if (procs==1) block_config = walberla::Vector3<int>(1, 1, 1); + else if (procs==4) block_config = walberla::Vector3<int>(2, 2, 1); + else { std::cout << "Error: Check block_config" << std::endl; exit(-1);} + + auto ref_level = 0; + std::shared_ptr<walberla::BlockForest> forest = walberla::blockforest::createBlockForest( + domain, block_config, walberla::Vector3<bool>(false, false, false), procs, ref_level); + + // Pass forest to P4IRS + // ------------------------------------------------------------------------------- + pairs_sim->use_domain(forest); + + pairs_sim->create_halfspace(0,0,0, 1, 0, 0, 0, 13); + pairs_sim->create_halfspace(0,0,0, 0, 1, 0, 0, 13); + pairs_sim->create_halfspace(0,0,0, 0, 0, 1, 0, 13); + pairs_sim->create_halfspace(1,1,1, -1, 0, 0, 0, 13); + pairs_sim->create_halfspace(1,1,1, 0, -1, 0, 0, 13); + pairs_sim->create_halfspace(1,1,1, 0, 0, -1, 0, 13); + pairs_sim->create_sphere(0.6, 0.6, 0.7, -2, -2, 0, 1000, 0.05, 0, 0); + pairs_sim->create_sphere(0.4, 0.4, 0.68, 2, 2, 0, 1000, 0.05, 0, 0); + + pairs_sim->setup_sim(); + + int num_timesteps = 2000; + int vtk_freq = 20; + double dt = 1e-3; + + for (int t=0; t<num_timesteps; ++t){ + if ((t%500==0) && pairs_sim->rank()==0) std::cout << "Timestep: " << t << std::endl; + + pairs_sim->communicate(t); + + pairs_sim->update_cells(); + + pairs_sim->gravity(); + pairs_sim->spring_dashpot(); + pairs_sim->euler(dt); + + pairs_sim->reset_volatiles(); + + pairs_sim->vtk_write("output/dem_sd_local", 0, pairs_sim->nlocal(), t, vtk_freq); + pairs_sim->vtk_write("output/dem_sd_ghost", pairs_sim->nlocal(), pairs_sim->size(), t, vtk_freq); + } + + pairs_sim->end(); +} diff --git a/examples/modular/sd_sample_3_CPU.cpp b/examples/modular/sd_sample_3_CPU.cpp new file mode 100644 index 0000000..ad532f4 --- /dev/null +++ b/examples/modular/sd_sample_3_CPU.cpp @@ -0,0 +1,95 @@ +#include <iostream> +#include <memory> + +#include "spring_dashpot.hpp" + +void change_gravitational_force(std::shared_ptr<PairsAccessor> &ac, int idx){ + pairs::Vector3<double> upward_gravity(0.0, 0.0, 2 * ac->getMass(idx) * 9.81); + ac->setForce(idx, ac->getForce(idx) + upward_gravity); +} + +int main(int argc, char **argv) { + + auto pairs_sim = std::make_shared<PairsSimulation>(); + pairs_sim->initialize(); + + auto ac = std::make_shared<PairsAccessor>(pairs_sim.get()); + + pairs_sim->set_domain(argc, argv, 0, 0, 0, 1, 1, 1); + + pairs_sim->create_halfspace(0,0,0, 1, 0, 0, 0, 13); + pairs_sim->create_halfspace(0,0,0, 0, 1, 0, 0, 13); + pairs_sim->create_halfspace(0,0,0, 0, 0, 1, 0, 13); + pairs_sim->create_halfspace(1,1,1, -1, 0, 0, 0, 13); + pairs_sim->create_halfspace(1,1,1, 0, -1, 0, 0, 13); + pairs_sim->create_halfspace(1,1,1, 0, 0, -1, 0, 13); + + pairs::id_t pUid = pairs_sim->create_sphere(0.6, 0.6, 0.7, 0, 0, 0, 1000, 0.05, 0, 0); + pairs::id_t pUid2 = pairs_sim->create_sphere(0.4, 0.4, 0.76, 2, 2, 0, 1000, 0.05, 0, 0); + + MPI_Allreduce(MPI_IN_PLACE, &pUid, 1, MPI_LONG_LONG_INT, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &pUid2, 1, MPI_LONG_LONG_INT, MPI_SUM, MPI_COMM_WORLD); + + auto pIsLocalInMyRank = [&](pairs::id_t uid){return ac->uidToIdxLocal(uid) != ac->getInvalidIdx();}; + + pairs_sim->setup_sim(); + + pairs_sim->communicate(); + + int num_timesteps = 2000; + int vtk_freq = 20; + double dt = 1e-3; + + for (int t=0; t<num_timesteps; ++t){ + + // Print position of particle pUid + //------------------------------------------------------------------------------------------- + if(pIsLocalInMyRank(pUid)){ + std::cout << "Timestep (" << t << "): Particle " << pUid << " is in rank " << pairs_sim->rank() << std::endl; + int idx = ac->uidToIdxLocal(pUid); + std::cout << "Position = (" + << ac->getPosition(idx)[0] << ", " + << ac->getPosition(idx)[1] << ", " + << ac->getPosition(idx)[2] << ")" << std::endl; + + } + + // Calculate forces + //------------------------------------------------------------------------------------------- + pairs_sim->update_cells(); + pairs_sim->gravity(); + pairs_sim->spring_dashpot(); + + // Change gravitational force on particle pUid + //------------------------------------------------------------------------------------------- + if(pIsLocalInMyRank(pUid)){ + int idx = ac->uidToIdxLocal(pUid); + + std::cout << "Force before changing = (" + << ac->getForce(idx)[0] << ", " + << ac->getForce(idx)[1] << ", " + << ac->getForce(idx)[2] << ")" << std::endl; + + change_gravitational_force(ac, idx); + + std::cout << "Force after changing = (" + << ac->getForce(idx)[0] << ", " + << ac->getForce(idx)[1] << ", " + << ac->getForce(idx)[2] << ")" << std::endl; + } + + // Euler + //------------------------------------------------------------------------------------------- + pairs_sim->euler(dt); + pairs_sim->reset_volatiles(); + + // Communicate + //------------------------------------------------------------------------------------------- + pairs_sim->communicate(t); + + pairs_sim->vtk_write("output/dem_sd_local", 0, ac->nlocal(), t, vtk_freq); + pairs_sim->vtk_write("output/dem_sd_ghost", ac->nlocal(), ac->size(), t, vtk_freq); + } + + pairs_sim->end(); +} \ No newline at end of file diff --git a/examples/modular/sd_sample_3_GPU.cu b/examples/modular/sd_sample_3_GPU.cu new file mode 100644 index 0000000..ef379e5 --- /dev/null +++ b/examples/modular/sd_sample_3_GPU.cu @@ -0,0 +1,129 @@ +#include <iostream> +#include <memory> +#include <cuda_runtime.h> + +#include "spring_dashpot.hpp" + +void checkCudaError(cudaError_t err, const char* func) { + if (err != cudaSuccess) { + fprintf(stderr, "CUDA error in %s: %s\n", func, cudaGetErrorString(err)); + exit(err); + } +} + +__global__ void print_position(PairsAccessor ac, int idx){ + printf("Position [from device] = (%f, %f, %f) \n", ac.getPosition(idx)[0], ac.getPosition(idx)[1], ac.getPosition(idx)[2]); +} + +__global__ void change_gravitational_force(PairsAccessor ac, int idx){ + printf("Force [from device] before setting = (%f, %f, %f) \n", ac.getForce(idx)[0], ac.getForce(idx)[1], ac.getForce(idx)[2]); + + pairs::Vector3<double> upward_gravity(0.0, 0.0, 2 * ac.getMass(idx) * 9.81); + ac.setForce(idx, ac.getForce(idx) + upward_gravity); + + printf("Force [from device] after setting = (%f, %f, %f) \n", ac.getForce(idx)[0], ac.getForce(idx)[1], ac.getForce(idx)[2]); +} + +int main(int argc, char **argv) { + + auto pairs_sim = std::make_shared<PairsSimulation>(); + pairs_sim->initialize(); + + // Create PairsAccessor after PairsSimulation is initialized + auto ac = std::make_shared<PairsAccessor>(pairs_sim.get()); + + pairs_sim->set_domain(argc, argv, 0, 0, 0, 1, 1, 1); + + pairs_sim->create_halfspace(0,0,0, 1, 0, 0, 0, 13); + pairs_sim->create_halfspace(0,0,0, 0, 1, 0, 0, 13); + pairs_sim->create_halfspace(0,0,0, 0, 0, 1, 0, 13); + pairs_sim->create_halfspace(1,1,1, -1, 0, 0, 0, 13); + pairs_sim->create_halfspace(1,1,1, 0, -1, 0, 0, 13); + pairs_sim->create_halfspace(1,1,1, 0, 0, -1, 0, 13); + + pairs::id_t pUid = pairs_sim->create_sphere(0.6, 0.6, 0.7, 0, 0, 0, 1000, 0.05, 0, 0); + pairs::id_t pUid2 = pairs_sim->create_sphere(0.4, 0.4, 0.76, 2, 2, 0, 1000, 0.05, 0, 0); + + MPI_Allreduce(MPI_IN_PLACE, &pUid, 1, MPI_LONG_LONG_INT, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &pUid2, 1, MPI_LONG_LONG_INT, MPI_SUM, MPI_COMM_WORLD); + + auto pIsLocalInMyRank = [&](pairs::id_t uid){return ac->uidToIdxLocal(uid) != ac->getInvalidIdx();}; + + pairs_sim->setup_sim(); + + pairs_sim->communicate(); + // PairsAccessor requires an update when particles are communicated + ac->update(); + + int num_timesteps = 2000; + int vtk_freq = 20; + double dt = 1e-3; + + for (int t=0; t<num_timesteps; ++t){ + // Up-to-date uids might be on host or device. So sync uid before accessing them from host + ac->syncUid(); + + // Print position of particle pUid + //------------------------------------------------------------------------------------------- + if(pIsLocalInMyRank(pUid)){ + std::cout << "Timestep (" << t << "): Particle " << pUid << " is in rank " << pairs_sim->rank() << std::endl; + int idx = ac->uidToIdxLocal(pUid); + + // Up-to-date position might be on host or device. So sync position before printing it from host: + ac->syncPosition(); + std::cout << "Position [from host] = (" + << ac->getPosition(idx)[0] << ", " + << ac->getPosition(idx)[1] << ", " + << ac->getPosition(idx)[2] << ")" << std::endl; + + // Position is still synced. Print it from device: + print_position<<<1,1>>>(*ac, idx); + checkCudaError(cudaDeviceSynchronize(), "print_position"); + } + + // Calculate forces + //------------------------------------------------------------------------------------------- + pairs_sim->update_cells(); + pairs_sim->gravity(); + pairs_sim->spring_dashpot(); + + // Change gravitational force on particle pUid + //------------------------------------------------------------------------------------------- + ac->syncUid(); + if(pIsLocalInMyRank(pUid)){ + std::cout << "Force Timestep (" << t << "): Particle " << pUid << " is in rank " << pairs_sim->rank() << std::endl; + int idx = ac->uidToIdxLocal(pUid); + + // Up-to-date force and mass might be on host or device. So sync before accessing them on device: + ac->syncForce(); + ac->syncMass(); + + // Modify force from device: + change_gravitational_force<<<1,1>>>(*ac, idx); + checkCudaError(cudaDeviceSynchronize(), "change_gravitational_force"); + + // Force on device was modified. So sync force before printing it from host: + ac->syncForce(); + std::cout << "Force [from host] after changing = (" + << ac->getForce(idx)[0] << ", " + << ac->getForce(idx)[1] << ", " + << ac->getForce(idx)[2] << ")" << std::endl; + } + + // Euler + //------------------------------------------------------------------------------------------- + pairs_sim->euler(dt); + pairs_sim->reset_volatiles(); + + // Communicate + //------------------------------------------------------------------------------------------- + pairs_sim->communicate(t); + // PairsAccessor requires an update when particles are communicated + ac->update(); + + pairs_sim->vtk_write("output/dem_sd_local", 0, ac->nlocal(), t, vtk_freq); + pairs_sim->vtk_write("output/dem_sd_ghost", ac->nlocal(), ac->size(), t, vtk_freq); + } + + pairs_sim->end(); +} \ No newline at end of file diff --git a/examples/modular/spring_dashpot.py b/examples/modular/spring_dashpot.py new file mode 100644 index 0000000..94e5ef8 --- /dev/null +++ b/examples/modular/spring_dashpot.py @@ -0,0 +1,111 @@ +import math +import pairs +import sys +import os + +def update_mass_and_inertia(i): + rotation_matrix[i] = diagonal_matrix(1.0) + rotation[i] = default_quaternion() + + if is_sphere(i): + inv_inertia[i] = inversed(diagonal_matrix(0.4 * mass[i] * radius[i] * radius[i])) + + else: + mass[i] = infinity + inv_inertia[i] = 0.0 + +def spring_dashpot(i, j): + delta_ij = -penetration_depth(i, j) + skip_when(delta_ij < 0.0) + + velocity_wf_i = linear_velocity[i] + cross(angular_velocity[i], contact_point(i, j) - position[i]) + velocity_wf_j = linear_velocity[j] + cross(angular_velocity[j], contact_point(i, j) - position[j]) + + rel_vel = -(velocity_wf_i - velocity_wf_j) + rel_vel_n = dot(rel_vel, contact_normal(i, j)) + rel_vel_t = rel_vel - rel_vel_n * contact_normal(i, j) + + fNabs = stiffness[i,j] * delta_ij + damping_norm[i,j] * rel_vel_n + fN = fNabs * contact_normal(i, j) + + fTabs = min(damping_tan[i,j] * length(rel_vel_t), friction[i, j] * fNabs) + fT = fTabs * normalized(rel_vel_t) + + partial_force = fN + fT + apply(force, partial_force) + apply(torque, cross(contact_point(i, j) - position, partial_force)) + +def euler(i): + inv_mass = 1.0 / mass[i] + position[i] += 0.5 * inv_mass * force[i] * dt * dt + linear_velocity[i] * dt + linear_velocity[i] += inv_mass * force[i] * dt + wdot = rotation_matrix[i] * (inv_inertia[i] * torque[i]) * transposed(rotation_matrix[i]) + phi = angular_velocity[i] * dt + 0.5 * wdot * dt * dt + rotation[i] = quaternion(phi, length(phi)) * rotation[i] + rotation_matrix[i] = quaternion_to_rotation_matrix(rotation[i]) + angular_velocity[i] += wdot * dt + +def gravity(i): + force[i][2] = force[i][2] - mass[i] * gravity_SI + +gravity_SI = 9.81 +diameter = 100 # required for linkedCellWidth. TODO: set linkedCellWidth at runtime +linkedCellWidth = 1.01 * diameter + +ntypes = 1 + +file_name = os.path.basename(__file__) +file_name_without_extension = os.path.splitext(file_name)[0] + +psim = pairs.simulation( + file_name_without_extension, + [pairs.sphere(), pairs.halfspace()], + double_prec=True, + particle_capacity=1000000, + neighbor_capacity=20, + debug=True, + generate_whole_program=False) + + +target = sys.argv[1] if len(sys.argv[1]) > 1 else "none" + +if target == 'gpu': + psim.target(pairs.target_gpu()) +elif target == 'cpu': + psim.target(pairs.target_cpu()) +else: + print(f"Invalid target, use {sys.argv[0]} <cpu/gpu>") + + +psim.add_position('position') +psim.add_property('mass', pairs.real()) +psim.add_property('linear_velocity', pairs.vector()) +psim.add_property('angular_velocity', pairs.vector()) +psim.add_property('force', pairs.vector(), volatile=True) +psim.add_property('torque', pairs.vector(), volatile=True) +psim.add_property('hydrodynamic_force', pairs.vector(), reduce=True) +psim.add_property('hydrodynamic_torque', pairs.vector(), reduce=True) +psim.add_property('old_hydrodynamic_force', pairs.vector()) +psim.add_property('old_hydrodynamic_torque', pairs.vector()) +psim.add_property('radius', pairs.real()) +psim.add_property('normal', pairs.vector()) +psim.add_property('inv_inertia', pairs.matrix()) +psim.add_property('rotation_matrix', pairs.matrix()) +psim.add_property('rotation', pairs.quaternion()) +psim.add_feature('type', ntypes) +psim.add_feature_property('type', 'stiffness', pairs.real(), [3000 for i in range(ntypes * ntypes)]) +psim.add_feature_property('type', 'damping_norm', pairs.real(), [10.0 for i in range(ntypes * ntypes)]) +psim.add_feature_property('type', 'damping_tan', pairs.real(), [0 for i in range(ntypes * ntypes)]) +psim.add_feature_property('type', 'friction', pairs.real(), [0 for i in range(ntypes * ntypes)]) + +psim.set_domain_partitioner(pairs.block_forest()) +psim.pbc([False, False, False]) +psim.build_cell_lists(linkedCellWidth) + +psim.setup(update_mass_and_inertia, symbols={'infinity': math.inf }) +psim.compute(spring_dashpot, linkedCellWidth) +psim.compute(euler, parameters={'dt': pairs.real()}) +psim.compute(gravity, symbols={'gravity_SI': gravity_SI }) + +psim.generate() + -- GitLab