diff --git a/examples/modular/linear_spring_dashpot.py b/examples/modular/linear_spring_dashpot.py new file mode 100644 index 0000000000000000000000000000000000000000..342af5ef82e8fba0ffefc04e04e7b4a3981db263 --- /dev/null +++ b/examples/modular/linear_spring_dashpot.py @@ -0,0 +1,158 @@ +import math +import pairs +import sys + + +def update_mass_and_inertia(i): + rotation_matrix[i] = diagonal_matrix(1.0) + rotation_quat[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 linear_spring_dashpot(i, j): + delta_ij = -penetration_depth(i, j) + skip_when(delta_ij < 0.0) + + meff = 1.0 / ((1.0 / mass[i]) + (1.0 / mass[j])) + stiffness_norm = meff * (pi * pi + lnDryResCoeff * lnDryResCoeff) / \ + (collisionTime_SI * collisionTime_SI) + stiffness_tan = kappa * stiffness_norm + damping_norm = -2.0 * meff * lnDryResCoeff / collisionTime_SI + damping_tan = sqrt(kappa) * damping_norm + + 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)) * contact_normal(i, j) + rel_vel_t = rel_vel - rel_vel_n + fN = stiffness_norm * delta_ij * contact_normal(i, j) + damping_norm * rel_vel_n; + + tan_spring_disp = tangential_spring_displacement[i, j] + impact_vel_magnitude = impact_velocity_magnitude[i, j] + impact_magnitude = select(impact_vel_magnitude > 0.0, impact_vel_magnitude, length(rel_vel)) + sticking = is_sticking[i, j] + + rot_tan_disp = tan_spring_disp - contact_normal(i, j) * dot(tan_spring_disp, contact_normal(i, j)) + rot_tan_disp_len2 = squared_length(rot_tan_disp) + new_tan_spring_disp = dt * rel_vel_t + \ + select(rot_tan_disp_len2 <= 0.0, + zero_vector(), + rot_tan_disp * sqrt(squared_length(tan_spring_disp) / rot_tan_disp_len2)) + + fTLS = stiffness_tan * new_tan_spring_disp + damping_tan * rel_vel_t + fTLS_len = length(fTLS) + t = normalized(fTLS) + + f_friction_abs_static = friction_static[i, j] * length(fN) + f_friction_abs_dynamic = friction_dynamic[i, j] * length(fN) + tan_vel_threshold = 1e-8 + + cond1 = sticking == 1 and length(rel_vel_t) < tan_vel_threshold and fTLS_len < f_friction_abs_static + cond2 = sticking == 1 and fTLS_len < f_friction_abs_dynamic + f_friction_abs = select(cond1, f_friction_abs_static, f_friction_abs_dynamic) + n_sticking = select(cond1 or cond2 or fTLS_len < f_friction_abs_dynamic, 1, 0) + tangential_spring_displacement[i, j] = \ + select(not cond1 and not cond2 and stiffness_tan > 0.0, + (f_friction_abs * t - damping_tan * rel_vel_t) / stiffness_tan, + new_tan_spring_disp) + + impact_velocity_magnitude[i, j] = impact_magnitude + is_sticking[i, j] = n_sticking + + fTabs = min(fTLS_len, f_friction_abs) + fT = fTabs * t + partial_force = fN + fT + + apply(force, partial_force) + apply(torque, cross(contact_point(i, j) - position, partial_force)) + + +def euler(i): + skip_when(is_fixed(i) or is_infinite(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_quat[i] = quaternion(phi, length(phi)) * rotation_quat[i] + rotation_matrix[i] = quaternion_to_rotation_matrix(rotation_quat[i]) + angular_velocity[i] += wdot * dt + + +def gravity(i): + force[i][2] -= mass[i] * gravity_SI + + +cmd = sys.argv[0] +target = sys.argv[1] if len(sys.argv[1]) > 1 else "none" +if target != 'cpu' and target != 'gpu': + print(f"Invalid target, use {cmd} <cpu/gpu>") + +# Config file parameters +gravity_SI = 9.81 +ntypes = 1 +frictionCoefficient = 0.5 +restitutionCoefficient = 0.1 +poissonsRatio = 0.22 +kappa = 2.0 * (1.0 - poissonsRatio) / (2.0 - poissonsRatio) # from Thornton et al +lnDryResCoeff = math.log(restitutionCoefficient) +frictionStatic = 0.0 +frictionDynamic = frictionCoefficient + +psim = pairs.simulation( + "linear_spring_dashpot", + [pairs.sphere(), pairs.halfspace()], + double_prec=True, + use_contact_history=True, + particle_capacity=1000000, + neighbor_capacity=20) + +if target == 'gpu': + psim.target(pairs.target_gpu()) +else: + psim.target(pairs.target_cpu()) + #psim.target(pairs.target_cpu(parallel=True)) + +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('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_quat', pairs.quaternion()) + +psim.add_feature('type', ntypes) +psim.add_feature_property('type', 'friction_static', pairs.real(), [frictionStatic for i in range(ntypes * ntypes)]) +psim.add_feature_property('type', 'friction_dynamic', pairs.real(), [frictionDynamic for i in range(ntypes * ntypes)]) + +psim.add_contact_property('is_sticking', pairs.int32(), 0) +psim.add_contact_property('tangential_spring_displacement', pairs.vector(), [0.0, 0.0, 0.0]) +psim.add_contact_property('impact_velocity_magnitude', pairs.real(), 0.0) + +psim.set_domain_partitioner(pairs.regular_domain_partitioner()) +psim.pbc([False, False, False]) +psim.build_cell_lists() + +psim.compute(update_mass_and_inertia, symbols={'infinity': math.inf }) + +psim.compute(gravity, symbols={'gravity_SI': gravity_SI}) + +psim.compute(linear_spring_dashpot, + parameters={'dt': pairs.real(),'collisionTime_SI': pairs.real()}, + symbols={'pi': math.pi, + 'kappa': kappa, + 'lnDryResCoeff': lnDryResCoeff}) + +psim.compute(euler, parameters={'dt': pairs.real()}) +psim.generate() diff --git a/examples/modular/lsd_1.cpp b/examples/modular/lsd_1.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9603f29c2abb54c66f661b4d75edfcc6f79067f4 --- /dev/null +++ b/examples/modular/lsd_1.cpp @@ -0,0 +1,61 @@ +#include <iostream> +#include <memory> + +#include "linear_spring_dashpot.hpp" +// cmake -DINPUT_SCRIPT=../examples/modular/linear_spring_dashpot.py -DCOMPILE_CUDA=ON -DUSE_WALBERLA=OFF -DCMAKE_BUILD_TYPE=Release -DUSER_SOURCE_FILES=../examples/modular/lsd_1.cpp .. + +int main(int argc, char **argv) { + + auto pairs_sim = std::make_shared<PairsSimulation>(); + pairs_sim->initialize(); + + auto pairs_runtime = pairs_sim->getPairsRuntime(); + + double dom_size = 0.1; + pairs_runtime->initDomain(&argc, &argv, 0, 0, 0, dom_size, dom_size, dom_size); + + pairs::create_halfspace(pairs_runtime, 0,0,0, 1, 0, 0, 0, 13); + pairs::create_halfspace(pairs_runtime, 0,0,0, 0, 1, 0, 0, 13); + pairs::create_halfspace(pairs_runtime, 0,0,0, 0, 0, 1, 0, 13); + pairs::create_halfspace(pairs_runtime, dom_size, dom_size, dom_size, -1, 0, 0, 0, 13); + pairs::create_halfspace(pairs_runtime, dom_size, dom_size, dom_size, 0, -1, 0, 0, 13); + pairs::create_halfspace(pairs_runtime, dom_size, dom_size, dom_size, 0, 0, -1, 0, 13); + + double density = 2550; + double diameter = 0.0029; + double sphere_spacing = diameter * 1.2; + double init_vel = 0.2; + + pairs::dem_sc_grid(pairs_runtime, dom_size/2.0, dom_size/2.0, dom_size/1.5, + sphere_spacing, + diameter, diameter, diameter, init_vel, density, 1); + + // pairs::create_sphere(pairs_runtime, 0.03, 0.03, 0.02, -0.2, -0.2, 0, density, diameter/2.0, 0, 0); + // pairs::create_sphere(pairs_runtime, 0.02, 0.02, 0.02, 0.2, 0.2, 0, density, diameter/2.0, 0, 0); + + pairs_sim->update_mass_and_inertia(); + + pairs_sim->setCellWidth(diameter, diameter, diameter); + pairs_sim->setInteractionRadius(diameter); + pairs_sim->updateDomain(); + + int num_timesteps = 20000; + int vtk_freq = 200; + double dt = 1e-5; + double collision_time = dt*10; + pairs::vtk_write_subdom(pairs_runtime, "output/subdom_init", 0); + + for (int t=0; t<num_timesteps; ++t){ + if ((t%500==0) && pairs_sim->rank()==0) std::cout << "Timestep: " << t << std::endl; + + pairs_sim->gravity(); + pairs_sim->linear_spring_dashpot(collision_time, dt); + pairs_sim->euler(dt); + pairs_sim->reneighbor(); + + pairs::vtk_write_data(pairs_runtime, "output/sd_1_local", 0, pairs_sim->nlocal(), t, vtk_freq); + pairs::vtk_write_data(pairs_runtime, "output/sd_1_ghost", pairs_sim->nlocal(), pairs_sim->size(), t, vtk_freq); + } + + pairs_sim->end(); +} diff --git a/runtime/domain/regular_6d_stencil.cpp b/runtime/domain/regular_6d_stencil.cpp index b469af0170a6437c4ee6c46278ebaa43ffabf4a0..d1d12f6e1dc82de3dac265b331f9b9c8cc27a90d 100644 --- a/runtime/domain/regular_6d_stencil.cpp +++ b/runtime/domain/regular_6d_stencil.cpp @@ -89,8 +89,8 @@ void Regular6DStencil::initialize(int *argc, char ***argv) { this->setBoundingBox(); if (rank==0) { std::cout << "Domain Partitioner: Regular-6D" << std::endl; - std::cout << "Domain: [ <" << subdom_min[0] << "," << subdom_min[1] << "," << subdom_min[2] << ">, <" - << subdom_max[0] << "," << subdom_max[1] << "," << subdom_max[2] << "> ]"<< std::endl; + std::cout << "Domain: [ <" << grid_min[0] << "," << grid_min[1] << "," << grid_min[2] << ">, <" + << grid_max[0] << "," << grid_max[1] << "," << grid_max[2] << "> ]"<< std::endl; std::cout << "Configuration: <" << nranks[0] << "," << nranks[1] << "," << nranks[2] << ">" <<std::endl; } } diff --git a/src/pairs/code_gen/interface.py b/src/pairs/code_gen/interface.py index 9a67d659c1734c7b15bce67875023cb016546f28..bbc1699f5df5917bedc0c1934170b1f41e2775d1 100644 --- a/src/pairs/code_gen/interface.py +++ b/src/pairs/code_gen/interface.py @@ -34,12 +34,6 @@ class InterfaceModules: self.refreshGhosts() self.reverseCommunicate() self.resetVolatiles() - - if self.sim._use_contact_history: - if self.neighbor_lists: - self.buildContactHistory(self.sim.reneighbor_frequency) - self.resetContactHistory() - self.rank() self.nlocal() self.nghost() @@ -126,7 +120,8 @@ class InterfaceModules: @pairs_interface_block def reneighbor(self): self.sim.module_name('reneighbor') - reneighboring_procedures = Block.from_list(self.sim, [ + + reneighboring_procedures = [ Exchange(self.sim._comm), Borders(self.sim._comm), # Note: DomainUpdateLocal must happen after exchange since local particles must be contained in AABBs @@ -134,8 +129,16 @@ class InterfaceModules: BuildCellListsStencil(self.sim, self.sim.cell_lists), self.sim.update_cells_procedures, ResetVolatileProperties(self.sim) - ]) - self.sim.add_statement(reneighboring_procedures) + ] + + if self.sim._use_contact_history: + reneighboring_procedures += [ + BuildContactHistory(self.sim, self.sim._contact_history, self.sim.cell_lists), + ResetContactHistoryUsageStatus(self.sim, self.sim._contact_history), + ClearUnusedContactHistory(self.sim, self.sim._contact_history) + ] + + self.sim.add_statement(Block.from_list(self.sim, reneighboring_procedures)) @pairs_interface_block def refreshGhosts(self): @@ -152,25 +155,6 @@ class InterfaceModules: self.sim.module_name('resetVolatiles') self.sim.add_statement(ResetVolatileProperties(self.sim)) - @pairs_interface_block - def buildContactHistory(self, reneighbor_frequency=1): - self.sim.module_name('buildContactHistory') - timestep = Parameter(self.sim, f'timestep', Types.Int32) - cond = ScalarOp.inline(ScalarOp.or_op( - ScalarOp.cmp((timestep + 1) % reneighbor_frequency, 0), - ScalarOp.cmp(timestep, 0) - )) - - self.sim.add_statement( - Filter(self.sim, cond, - BuildContactHistory(self.sim, self.sim._contact_history, self.sim.cell_lists))) - - @pairs_interface_block - def resetContactHistory(self): - self.sim.module_name('resetContactHistory') - self.sim.add_statement(ResetContactHistoryUsageStatus(self.sim, self.sim._contact_history)) - self.sim.add_statement(ClearUnusedContactHistory(self.sim, self.sim._contact_history)) - @pairs_interface_block def rank(self): self.sim.module_name('rank') diff --git a/src/pairs/sim/interaction.py b/src/pairs/sim/interaction.py index 2671a907e526e1b9aefe0677349c5d2233f962f7..49aff87d50c1f6b7d508e54b3df3347d1004869d 100644 --- a/src/pairs/sim/interaction.py +++ b/src/pairs/sim/interaction.py @@ -47,8 +47,8 @@ class NeighborFor: self.particle = particle self.cell_lists = cell_lists self.neighbor_lists = neighbor_lists - # self.shapes = range(sim.max_shapes()) if shapes is None else shapes - self.shapes = [shapes] + self.shapes = range(sim.max_shapes()) if shapes is None else [shapes] + # self.shapes = [shapes] def __str__(self): return f"NeighborFor<{self.particle}>"