Skip to content

Commit 1d50653

Browse files
committed
Refactor hydrostatics and radiation damping for clarity and safety
Rewrite ComputeForceHydrostatics and ComputeForceRadiationDampingConv for readability and explicit naming. Use Eigen-only math for matrix–vector ops; avoid Eigen/Chrono type mixing. Keep behavior unchanged; improve interpolation and indexing robustness.
1 parent 5872e5e commit 1d50653

2 files changed

Lines changed: 156 additions & 103 deletions

File tree

build.ps1

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,15 @@ function Get-BuildArguments {
608608
# Deterministic prefix path so our roots win over ambient environment
609609
$prefixParts = @()
610610
foreach ($p in @($Config.ChronoDir, $Config.Hdf5Dir, $Config.EigenDir, $Config.IrrlichtDir, $Config.PythonRoot)) {
611-
if ($p -and (Test-Path $p)) { $prefixParts += ($p -replace '\\','/') }
611+
if ($p) {
612+
$isValid = $false
613+
try { $null = [System.IO.Path]::GetFullPath($p); $isValid = $true } catch { $isValid = $false }
614+
if ($isValid) {
615+
try {
616+
if (Test-Path -LiteralPath $p) { $prefixParts += ($p -replace '\\','/') }
617+
} catch { }
618+
}
619+
}
612620
}
613621
$prefixPath = ($prefixParts -join ';')
614622
$testsFlag = if ($NoTests) { "OFF" } else { "ON" }

src/hydro_forces.cpp

Lines changed: 147 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -254,142 +254,187 @@ std::vector<double> TestHydro::ComputeForceHydrostatics() {
254254
assert(num_bodies_ > 0);
255255

256256
const double rho = file_info_.GetRhoVal();
257-
const auto g_acc = bodies_[0]->GetSystem()->GetGravitationalAcceleration(); // assuming all bodies in same system
258-
const double gg = g_acc.Length();
257+
const auto gravitational_acceleration = bodies_[0]->GetSystem()->GetGravitationalAcceleration(); // same system for all bodies
258+
const double rho_times_g = rho * gravitational_acceleration.Length();
259259

260-
for (int b = 0; b < num_bodies_; b++) {
261-
std::shared_ptr<chrono::ChBody> body = bodies_[b];
262-
263-
int b_offset = kDofPerBody * b;
264-
double* body_force_hydrostatic = &force_hydrostatic_[b_offset];
265-
double* body_equilibrium = &equilibrium_[b_offset];
266-
267-
// hydrostatic stiffness due to offset from equilibrium
268-
const auto body_position = body->GetPos();
269-
const auto body_rotation = body->GetRot().GetCardanAnglesXYZ();
270-
271-
chrono::ChVectorN<double, kDofPerBody> body_displacement;
272-
for (int ii = 0; ii < kDofLinOrRot; ii++) {
273-
body_displacement[ii] = body_position[ii] - body_equilibrium[ii];
274-
body_displacement[ii + kDofLinOrRot] = body_rotation[ii] - body_equilibrium[ii + kDofLinOrRot];
275-
}
276-
277-
const auto force_offset = -gg * rho * file_info_.GetLinMatrix(b) * body_displacement;
278-
for (int dof = 0; dof < kDofPerBody; dof++) {
279-
body_force_hydrostatic[dof] += force_offset[dof];
280-
}
281-
282-
// buoyancy at equilibrium
283-
const auto buoyancy = rho * (-g_acc) * file_info_.GetDispVolVal(b);
284-
285-
for (int ii = 0; ii < kDofLinOrRot; ii++) {
286-
body_force_hydrostatic[ii] += buoyancy[ii];
260+
for (int b = 0; b < num_bodies_; ++b) {
261+
const auto& body = bodies_[b];
262+
263+
const int body_offset = kDofPerBody * b;
264+
double* const body_force_hydrostatic = &force_hydrostatic_[body_offset];
265+
const double* const body_equilibrium = &equilibrium_[body_offset];
266+
267+
// Current pose
268+
const chrono::ChVector3d position_world = body->GetPos();
269+
const chrono::ChVector3d rotation_rpy = body->GetRot().GetCardanAnglesXYZ();
270+
271+
// 6-DOF displacement from equilibrium (translation xyz, rotation rpy)
272+
Eigen::Matrix<double, kDofPerBody, 1> displacement_from_equilibrium;
273+
displacement_from_equilibrium[0] = position_world.x() - body_equilibrium[0];
274+
displacement_from_equilibrium[1] = position_world.y() - body_equilibrium[1];
275+
displacement_from_equilibrium[2] = position_world.z() - body_equilibrium[2];
276+
displacement_from_equilibrium[3] = rotation_rpy.x() - body_equilibrium[3];
277+
displacement_from_equilibrium[4] = rotation_rpy.y() - body_equilibrium[4];
278+
displacement_from_equilibrium[5] = rotation_rpy.z() - body_equilibrium[5];
279+
280+
// Linear hydrostatic restoring force/torque
281+
const Eigen::MatrixXd restoring_stiffness_matrix = file_info_.GetLinMatrix(b);
282+
const Eigen::Matrix<double, kDofPerBody, 1> restoring_force_torque =
283+
-rho_times_g * (restoring_stiffness_matrix * displacement_from_equilibrium);
284+
for (int i = 0; i < kDofPerBody; ++i) {
285+
body_force_hydrostatic[i] += restoring_force_torque[i];
287286
}
288287

289-
int r_offset = kDofLinOrRot * b;
290-
const auto cg2cb =
291-
chrono::ChVector3d(cb_minus_cg_[r_offset], cb_minus_cg_[r_offset + 1], cb_minus_cg_[r_offset + 2]);
292-
const auto buoyancy2 = cg2cb % buoyancy;
293-
294-
for (int ii = 0; ii < kDofLinOrRot; ii++) {
295-
body_force_hydrostatic[ii + kDofLinOrRot] += buoyancy2[ii];
296-
}
288+
// Buoyancy force at equilibrium: F = rho * (-g) * displaced_volume
289+
const double displaced_volume = file_info_.GetDispVolVal(b);
290+
const chrono::ChVector3d buoyancy_force = rho * (-gravitational_acceleration) * displaced_volume;
291+
body_force_hydrostatic[0] += buoyancy_force.x();
292+
body_force_hydrostatic[1] += buoyancy_force.y();
293+
body_force_hydrostatic[2] += buoyancy_force.z();
294+
295+
// Buoyancy-induced moment about CG: (r_CB - r_CG) x buoyancy
296+
const int rotation_offset = kDofLinOrRot * b;
297+
const chrono::ChVector3d cg_to_cb(
298+
cb_minus_cg_[rotation_offset + 0],
299+
cb_minus_cg_[rotation_offset + 1],
300+
cb_minus_cg_[rotation_offset + 2]
301+
);
302+
const chrono::ChVector3d buoyancy_torque = cg_to_cb % buoyancy_force;
303+
body_force_hydrostatic[3] += buoyancy_torque.x();
304+
body_force_hydrostatic[4] += buoyancy_torque.y();
305+
body_force_hydrostatic[5] += buoyancy_torque.z();
297306
}
298307

299308
return force_hydrostatic_;
300309
}
301310

302311
std::vector<double> TestHydro::ComputeForceRadiationDampingConv() {
303-
const int size = file_info_.GetRIRFDims(2);
304-
const int numRows = kDofPerBody * num_bodies_;
305-
const int numCols = kDofPerBody * num_bodies_;
312+
const int rirf_steps = file_info_.GetRIRFDims(2);
313+
const int total_dofs = kDofPerBody * num_bodies_;
306314

307-
assert(numRows * size > 0 && numCols > 0);
315+
assert(total_dofs > 0 && rirf_steps > 0);
308316

309-
// time history
310-
auto t_sim = bodies_[0]->GetChTime();
311-
auto t_min = t_sim - rirf_time_vector.tail<1>()[0];
312-
if (time_history_.size() > 0 && t_sim == time_history_.front()) {
317+
// Current time and minimum history time window required
318+
const double simulation_time = bodies_[0]->GetChTime();
319+
const int rirf_last_index = static_cast<int>(rirf_time_vector.size()) - 1;
320+
const double history_min_time = simulation_time - (rirf_last_index >= 0 ? rirf_time_vector[rirf_last_index] : 0.0);
321+
322+
// Prevent duplicate computation within same step
323+
if (!time_history_.empty() && simulation_time == time_history_.front()) {
313324
throw std::runtime_error("Tried to compute the radiation damping convolution twice within the same time step!");
314325
}
315-
time_history_.insert(time_history_.begin(), t_sim);
316326

317-
// velocity history
318-
for (int b = 0; b < num_bodies_; b++) {
319-
auto& body = bodies_[b];
327+
// Record current time at the front (most recent first)
328+
time_history_.insert(time_history_.begin(), simulation_time);
329+
330+
// Record current velocities per body at the front (matching time_history_ ordering)
331+
for (int b = 0; b < num_bodies_; ++b) {
332+
auto& body = bodies_[b];
320333
auto& velocity_history_body = velocity_history_[b];
321334

322-
auto vel = body->GetPosDt();
323-
auto wvel = body->GetAngVelParent();
324-
std::vector<double> vel_vec = {vel[0], vel[1], vel[2], wvel[0], wvel[1], wvel[2]};
325-
velocity_history_body.insert(velocity_history_body.begin(), vel_vec);
335+
const auto linear_velocity_world = body->GetPosDt();
336+
const auto angular_velocity_world = body->GetAngVelParent();
337+
std::vector<double> velocity_dof_vector = {
338+
linear_velocity_world[0], linear_velocity_world[1], linear_velocity_world[2],
339+
angular_velocity_world[0], angular_velocity_world[1], angular_velocity_world[2]
340+
};
341+
velocity_history_body.insert(velocity_history_body.begin(), std::move(velocity_dof_vector));
326342
}
327343

328-
// remove unnecessary history
344+
// Prune history older than the max RIRF time span
329345
if (time_history_.size() > 1) {
330-
while (time_history_[time_history_.size() - 2] < t_min) {
346+
while (time_history_.size() > 1 && time_history_[time_history_.size() - 2] < history_min_time) {
331347
time_history_.pop_back();
332-
for (int b = 0; b < num_bodies_; b++) {
348+
for (int b = 0; b < num_bodies_; ++b) {
333349
auto& velocity_history_body = velocity_history_[b];
334-
velocity_history_body.pop_back();
350+
if (!velocity_history_body.empty()) {
351+
velocity_history_body.pop_back();
352+
}
335353
}
336354
}
337355
}
338356

339-
if (time_history_.size() > 1) {
340-
int idx_history = 0;
357+
// Nothing to convolve with if we don't yet have at least 2 time points
358+
if (time_history_.size() <= 1) {
359+
return force_radiation_damping_;
360+
}
361+
362+
// Walk through RIRF steps and accumulate convolution
363+
size_t history_index = 0; // index into descending time_history_ (front is most recent)
364+
for (int step = 0; step < rirf_steps; ++step) {
365+
const double rirf_query_time = simulation_time - rirf_time_vector[step];
366+
367+
// Advance history_index until [history_index, history_index+1] brackets rirf_query_time, or we run out
368+
while ((history_index + 1) < time_history_.size() && time_history_[history_index + 1] > rirf_query_time) {
369+
++history_index;
370+
}
371+
if ((history_index + 1) >= time_history_.size()) {
372+
break; // not enough older history to interpolate
373+
}
341374

342-
// iterate over RIRF steps
343-
for (int step = 0; step < size; step++) {
344-
auto t_rirf = t_sim - rirf_time_vector[step];
345-
while (time_history_[idx_history + 1] > t_rirf && idx_history < time_history_.size() - 1) {
346-
idx_history += 1;
375+
const double newer_time = time_history_[history_index];
376+
const double older_time = time_history_[history_index + 1];
377+
378+
// For each body, interpolate 6-DOF velocity at rirf_query_time and apply convolution
379+
for (int body_index = 0; body_index < num_bodies_; ++body_index) {
380+
auto& velocity_history_body = velocity_history_[body_index];
381+
382+
// Ensure history is consistent across bodies
383+
if (velocity_history_body.size() <= history_index) {
384+
continue; // skip if inconsistent; should not happen in normal flow
347385
}
348-
if (idx_history >= time_history_.size() - 1) {
349-
break;
386+
387+
// Interpolate velocities
388+
double interpolated_velocity_dof[kDofPerBody];
389+
if (rirf_query_time == older_time) {
390+
const auto& older_velocity = velocity_history_body[history_index + 1];
391+
interpolated_velocity_dof[0] = older_velocity[0];
392+
interpolated_velocity_dof[1] = older_velocity[1];
393+
interpolated_velocity_dof[2] = older_velocity[2];
394+
interpolated_velocity_dof[3] = older_velocity[3];
395+
interpolated_velocity_dof[4] = older_velocity[4];
396+
interpolated_velocity_dof[5] = older_velocity[5];
397+
} else if (rirf_query_time == newer_time) {
398+
const auto& newer_velocity = velocity_history_body[history_index];
399+
interpolated_velocity_dof[0] = newer_velocity[0];
400+
interpolated_velocity_dof[1] = newer_velocity[1];
401+
interpolated_velocity_dof[2] = newer_velocity[2];
402+
interpolated_velocity_dof[3] = newer_velocity[3];
403+
interpolated_velocity_dof[4] = newer_velocity[4];
404+
interpolated_velocity_dof[5] = newer_velocity[5];
405+
} else if (rirf_query_time > older_time && rirf_query_time < newer_time) {
406+
const double time_delta = (newer_time - older_time);
407+
const double weight_older = (time_delta != 0.0) ? ((newer_time - rirf_query_time) / time_delta) : 0.0;
408+
const double weight_newer = 1.0 - weight_older;
409+
const auto& older_velocity = velocity_history_body[history_index + 1];
410+
const auto& newer_velocity = velocity_history_body[history_index];
411+
interpolated_velocity_dof[0] = weight_older * older_velocity[0] + weight_newer * newer_velocity[0];
412+
interpolated_velocity_dof[1] = weight_older * older_velocity[1] + weight_newer * newer_velocity[1];
413+
interpolated_velocity_dof[2] = weight_older * older_velocity[2] + weight_newer * newer_velocity[2];
414+
interpolated_velocity_dof[3] = weight_older * older_velocity[3] + weight_newer * newer_velocity[3];
415+
interpolated_velocity_dof[4] = weight_older * older_velocity[4] + weight_newer * newer_velocity[4];
416+
interpolated_velocity_dof[5] = weight_older * older_velocity[5] + weight_newer * newer_velocity[5];
417+
} else {
418+
throw std::runtime_error(
419+
"Radiation convolution: interpolation error; rirf_query_time not bracketed by adjacent history.");
350420
}
351421

352-
// iterate over bodies
353-
for (int idx_body = 0; idx_body < num_bodies_; idx_body++) {
354-
auto& velocity_history_body = velocity_history_[idx_body];
355-
std::vector<double> vel(kDofPerBody);
356-
357-
// interpolate velocity at t_rirf from recorded velocity history
358-
// time values
359-
auto t1 = time_history_[idx_history + 1];
360-
auto t2 = time_history_[idx_history];
361-
if (t_rirf == t1) {
362-
vel = velocity_history_body[idx_history + 1];
363-
} else if (t_rirf == t2) {
364-
vel = velocity_history_body[idx_history];
365-
} else if (t_rirf > t1 && t_rirf < t2) {
366-
// weights
367-
auto w1 = (t2 - t_rirf) / (t2 - t1);
368-
auto w2 = 1.0 - w1;
369-
// velocity values
370-
auto vel1 = velocity_history_body[idx_history + 1];
371-
auto vel2 = velocity_history_body[idx_history];
372-
for (int dof = 0; dof < kDofPerBody; dof++) {
373-
vel[dof] = w1 * vel1[dof] + w2 * vel2[dof];
374-
}
375-
} else {
376-
throw std::runtime_error("Radiation convolution: wrong interpolation: " + std::to_string(t_rirf) +
377-
" not between " + std::to_string(t1) + " and " + std::to_string(t2) + ".");
422+
// Apply convolution: for each DOF column, add RIRF[:, col, step] * vel[dof] * width
423+
const double step_width = rirf_width_vector[step];
424+
const int body_col_offset = body_index * kDofPerBody;
425+
for (int dof = 0; dof < kDofPerBody; ++dof) {
426+
const int col = body_col_offset + dof;
427+
const double contribution_scale = interpolated_velocity_dof[dof] * step_width;
428+
if (contribution_scale == 0.0) {
429+
continue;
378430
}
379-
380-
for (int dof = 0; dof < kDofPerBody; dof++) {
381-
// get column index
382-
int col = dof + idx_body * kDofPerBody;
383-
384-
// iterate over rows
385-
for (int row = 0; row < numRows; row++) {
386-
force_radiation_damping_[row] +=
387-
GetRIRFval(row, col, step) * vel[dof] * rirf_width_vector[step];
388-
}
431+
for (int row = 0; row < total_dofs; ++row) {
432+
force_radiation_damping_[row] += GetRIRFval(row, col, step) * contribution_scale;
389433
}
390434
}
391435
}
392436
}
437+
393438
return force_radiation_damping_;
394439
}
395440

0 commit comments

Comments
 (0)