Skip to content

Commit 3ef275b

Browse files
sbryngelsonclaude
andcommitted
Fix IBM force: accumulate from both predictor AND corrected velocity
Cd was ~3x too low because only the predictor IBM call contributed to force accumulation. At quasi-steady state u* inside body ≈ 0 (IBM zeroed it last step), so predictor contribution is tiny. The missing force is from the second IBM call: u^{n+1} = u*_IBM - dt*grad(p) inside body is non-zero due to pressure correction; re-zeroing it gives the pressure drag. Fix: add reset_force_accumulator() (called once per step in solver.cpp), change both apply_forcing calls to ADD to last_Fx_/Fy_/Fz_. Total force = predictor correction + pressure correction = full IBM drag. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 798c7ad commit 3ef275b

File tree

4 files changed

+34
-17
lines changed

4 files changed

+34
-17
lines changed

include/ibm_forcing.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ class IBMForcing {
6969
/// Whether GPU data is mapped
7070
bool is_gpu_ready() const { return gpu_mapped_; }
7171

72+
/// Reset force accumulator to zero; call once at the start of each time step,
73+
/// before the first apply_forcing call. Both IBM calls (predictor + corrected
74+
/// velocity) then ADD their contributions so the total captures pressure drag too.
75+
void reset_force_accumulator();
76+
7277
/// Compute drag and lift forces on the body
7378
/// @param vel Current velocity field
7479
/// @param dt Time step size

src/ibm_forcing.cpp

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,12 @@ void IBMForcing::unmap_from_gpu() {
254254
gpu_mapped_ = false;
255255
}
256256

257+
void IBMForcing::reset_force_accumulator() {
258+
last_Fx_ = 0.0;
259+
last_Fy_ = 0.0;
260+
last_Fz_ = 0.0;
261+
}
262+
257263
void IBMForcing::apply_forcing(VectorField& vel, double dt) {
258264
const int Nx = mesh_->Nx;
259265
const int Ny = mesh_->Ny;
@@ -262,14 +268,13 @@ void IBMForcing::apply_forcing(VectorField& vel, double dt) {
262268
const bool is2D = mesh_->is2D();
263269
int Nz_eff = is2D ? 1 : Nz;
264270

265-
// Accumulate force on body from predictor velocity (before weight multiply)
266-
// Only accumulate when dt > 0 (first call on velocity_star_); skip second call (dt=0).
271+
// Accumulate IBM momentum correction into force accumulator (ADD, don't reset).
272+
// Call reset_force_accumulator() once per step before the first apply_forcing call.
273+
// Both calls (predictor + corrected velocity) contribute: predictor captures viscous
274+
// damping, corrected velocity captures pressure drag (u^{n+1} = u*_IBM - dt*grad(p)).
267275
if (dt > 0.0) {
268276
const double dx = mesh_->dx;
269277
const double dz_val = is2D ? 1.0 : mesh_->dz;
270-
last_Fx_ = 0.0;
271-
last_Fy_ = 0.0;
272-
last_Fz_ = 0.0;
273278

274279
for (int k = Ng; k < Nz_eff + Ng; ++k) {
275280
for (int j = Ng; j < Ny + Ng; ++j) {
@@ -362,8 +367,9 @@ void IBMForcing::apply_forcing_device(double* u_ptr, double* v_ptr, double* w_pt
362367
const int v_n = static_cast<int>(v_total_);
363368
const int w_n = static_cast<int>(w_total_);
364369

365-
// Accumulate force on body from predictor velocity (before weight multiply)
366-
// Only accumulate when dt > 0 (first call on velocity_star_); skip second call (dt=0).
370+
// Accumulate IBM momentum correction (ADD to accumulator; caller resets via
371+
// reset_force_accumulator() once per step). Both calls (predictor + corrected
372+
// velocity) contribute so the total captures viscous + pressure drag.
367373
if (dt > 0.0) {
368374
[[maybe_unused]] const double dV = mesh_->dx * mesh_->dy * (mesh_->is2D() ? 1.0 : mesh_->dz);
369375
double Fx_acc = 0.0;
@@ -390,9 +396,9 @@ void IBMForcing::apply_forcing_device(double* u_ptr, double* v_ptr, double* w_pt
390396
}
391397
}
392398

393-
last_Fx_ = Fx_acc / dt * dV;
394-
last_Fy_ = Fy_acc / dt * dV;
395-
last_Fz_ = Fz_acc / dt * dV;
399+
last_Fx_ += Fx_acc / dt * dV;
400+
last_Fy_ += Fy_acc / dt * dV;
401+
last_Fz_ += Fz_acc / dt * dV;
396402
}
397403

398404
#pragma omp target teams distribute parallel for \

src/solver.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1751,8 +1751,11 @@ double RANSSolver::step() {
17511751
}
17521752

17531753
// 3b. Apply IBM forcing to predicted velocity (before Poisson solve)
1754-
// Must apply to velocity_star_ (the predictor), not velocity_ (current step)
1754+
// Must apply to velocity_star_ (the predictor), not velocity_ (current step).
1755+
// Reset force accumulator here so both IBM calls (predictor + corrected velocity)
1756+
// contribute: predictor captures viscous damping, second call captures pressure drag.
17551757
if (ibm_) {
1758+
ibm_->reset_force_accumulator();
17561759
if (gpu_ready_ && ibm_->is_gpu_ready()) {
17571760
ibm_->apply_forcing_device(velocity_star_u_ptr_, velocity_star_v_ptr_,
17581761
mesh_->is2D() ? nullptr : velocity_star_w_ptr_,
@@ -2220,14 +2223,16 @@ double RANSSolver::step() {
22202223
}
22212224

22222225
// 5b. Re-apply IBM forcing after velocity correction
2223-
// Pressure correction may introduce non-zero velocity inside the body
2224-
// dt=0.0 (default): do not overwrite last_Fx_/Fy_/Fz_ accumulated from predictor
2226+
// Re-force corrected velocity: pressure correction may introduce non-zero velocity
2227+
// inside the body. Pass current_dt_ so this call also contributes to force accumulator
2228+
// (pressure drag u^{n+1} = u*_IBM - dt*grad(p) inside body captures pressure force).
22252229
if (ibm_) {
22262230
if (gpu_ready_ && ibm_->is_gpu_ready()) {
22272231
ibm_->apply_forcing_device(velocity_u_ptr_, velocity_v_ptr_,
2228-
mesh_->is2D() ? nullptr : velocity_w_ptr_);
2232+
mesh_->is2D() ? nullptr : velocity_w_ptr_,
2233+
current_dt_);
22292234
} else {
2230-
ibm_->apply_forcing(velocity_, 0.0);
2235+
ibm_->apply_forcing(velocity_, current_dt_);
22312236
}
22322237
}
22332238

tests/test_ibm_forcing.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,10 @@ void test_force_computation() {
140140
}
141141
}
142142

143-
// apply_forcing with dt>0 accumulates forces from predictor velocity,
144-
// then zeroes body cells. compute_forces returns the cached values.
143+
// reset_force_accumulator() then apply_forcing(dt>0) accumulates forces.
144+
// compute_forces returns the cached values.
145145
const double dt = 0.001;
146+
ibm.reset_force_accumulator();
146147
ibm.apply_forcing(vel, dt);
147148
auto [Fx, Fy, Fz] = ibm.compute_forces(vel, dt);
148149

0 commit comments

Comments
 (0)