sparkl2d_kernels/cuda/
particle_updater.rs1use crate::{cuda::InterpolatedParticleData, GpuColliderSet, GpuParticleModel};
2use sparkl_core::math::{Matrix, Real, Vector};
3use sparkl_core::prelude::{
4 ActiveTimestepBounds, ParticlePhase, ParticlePosition, ParticleStatus, ParticleVelocity,
5 ParticleVolume,
6};
7
8#[cfg(not(feature = "std"))]
9use na::ComplexField;
10
11pub trait ParticleUpdater {
12 fn artificial_pressure_stiffness(&self) -> f32 {
13 0.0
14 }
15
16 unsafe fn estimate_particle_timestep_length(
17 &self,
18 cell_width: Real,
19 particle_id: u32,
20 particle_status: &ParticleStatus,
21 particle_volume: &ParticleVolume,
22 particle_vel: &ParticleVelocity,
23 ) -> (ActiveTimestepBounds, Real);
24
25 unsafe fn update_particle_and_compute_kirchhoff_stress(
26 &self,
27 dt: Real,
28 cell_width: Real,
29 colliders: &GpuColliderSet,
30 particle_id: u32,
31 particle_status: &mut ParticleStatus,
32 particle_pos: &mut ParticlePosition,
33 particle_vel: &mut ParticleVelocity,
34 particle_volume: &mut ParticleVolume,
35 particle_phase: &mut ParticlePhase,
36 interpolated_data: &mut InterpolatedParticleData,
37 ) -> Option<(Matrix<Real>, Vector<Real>)>;
38}
39
40pub struct DefaultParticleUpdater {
41 pub models: *mut GpuParticleModel,
42}
43
44impl ParticleUpdater for DefaultParticleUpdater {
45 #[inline(always)]
46 unsafe fn estimate_particle_timestep_length(
47 &self,
48 cell_width: Real,
49 particle_id: u32,
50 particle_status: &ParticleStatus,
51 particle_volume: &ParticleVolume,
52 particle_vel: &ParticleVelocity,
53 ) -> (ActiveTimestepBounds, Real) {
54 let model = &*self.models.add(particle_status.model_index);
55 let active_timestep_bounds = model.constitutive_model.active_timestep_bounds();
56
57 if active_timestep_bounds.contains(ActiveTimestepBounds::CONSTITUTIVE_MODEL_BOUND) {
58 (
59 active_timestep_bounds,
60 model.constitutive_model.timestep_bound(
61 particle_id,
62 particle_volume,
63 particle_vel,
64 cell_width,
65 ),
66 )
67 } else {
68 (active_timestep_bounds, Real::MAX)
69 }
70 }
71
72 #[inline(always)]
73 unsafe fn update_particle_and_compute_kirchhoff_stress(
74 &self,
75 dt: Real,
76 cell_width: Real,
77 colliders: &GpuColliderSet,
78 particle_id: u32,
79 particle_status: &mut ParticleStatus,
80 particle_pos: &mut ParticlePosition,
81 particle_vel: &mut ParticleVelocity,
82 particle_volume: &mut ParticleVolume,
83 particle_phase: &mut ParticlePhase,
84 interpolated_data: &mut InterpolatedParticleData,
85 ) -> Option<(Matrix<Real>, Vector<Real>)> {
86 let model = &*self.models.add(particle_status.model_index);
87
88 particle_vel.vector = interpolated_data.velocity;
89
90 let is_fluid = model.constitutive_model.is_fluid();
91
92 if particle_status.kinematic_vel_enabled {
110 particle_vel.vector = particle_status.kinematic_vel;
111 }
112
113 if particle_vel
114 .vector
115 .iter()
116 .any(|x| x.abs() * dt >= cell_width)
117 {
118 particle_vel
119 .vector
120 .apply(|x| *x = x.signum() * cell_width / dt);
121 }
122
123 particle_pos.point += particle_vel.vector * dt;
124
125 if !is_fluid {
129 particle_volume.deformation_gradient +=
130 (interpolated_data.velocity_gradient * dt) * particle_volume.deformation_gradient;
131 } else {
132 particle_volume.deformation_gradient[(0, 0)] +=
133 (interpolated_data.velocity_gradient_det * dt)
134 * particle_volume.deformation_gradient[(0, 0)];
135 model
136 .constitutive_model
137 .update_internal_energy_and_pressure(
138 particle_id,
139 particle_volume,
140 dt,
141 cell_width,
142 &interpolated_data.velocity_gradient,
143 );
144 }
145
146 if let Some(plasticity) = &model.plastic_model {
147 plasticity.update_particle(particle_id, particle_volume, particle_phase.phase);
148 }
149
150 if particle_status.is_static {
151 particle_vel.vector.fill(0.0);
152 interpolated_data.velocity_gradient.fill(0.0);
153 }
154
155 if particle_volume.density_def_grad() == 0.0
156 || particle_status.failed
157 || (!is_fluid && particle_volume.deformation_gradient[(0, 0)].abs() > 1.0e4)
161 {
162 particle_status.failed = true;
163 particle_volume.deformation_gradient = Matrix::identity();
164 return None;
165 }
166
167 {
173 let energy = model.constitutive_model.pos_energy(
174 particle_id,
175 particle_volume,
176 particle_phase.phase,
177 );
178 particle_phase.psi_pos = particle_phase.psi_pos.max(energy);
179 }
180
181 {
186 if let Some(failure_model) = &model.failure_model {
187 let stress = model.constitutive_model.kirchhoff_stress(
188 particle_id,
189 particle_volume,
190 particle_phase.phase,
191 &interpolated_data.velocity_gradient,
192 );
193 if failure_model.particle_failed(&stress) {
194 particle_phase.phase = 0.0;
195 }
196 }
197 }
198
199 let mut penalty_force = Vector::zeros();
204 if false {
205 for collider in colliders.iter() {
207 if collider.penalty_stiffness > 0.0 {
208 if let Some(proj) = collider.shape.project_point_with_max_dist(
209 &collider.position,
210 &particle_pos.point,
211 false,
212 100.0 * cell_width,
213 ) {
214 if proj.is_inside {
215 penalty_force +=
216 (proj.point - particle_pos.point) * collider.penalty_stiffness;
217 }
218 }
219 }
220 }
221 }
222
223 if !particle_status.failed {
225 let stress = model.constitutive_model.kirchhoff_stress(
226 particle_id,
227 particle_volume,
228 particle_phase.phase,
229 &interpolated_data.velocity_gradient,
230 );
231
232 Some((stress, penalty_force))
233 } else {
234 Some((Matrix::zeros(), Vector::zeros()))
235 }
236 }
237}