Skip to content

Commit d8a6484

Browse files
authored
Merge pull request #50 from tridelat/rad_interp
Interpolate velocity history for radiation convolution
2 parents cd76da7 + 1befd61 commit d8a6484

4 files changed

Lines changed: 127 additions & 111 deletions

File tree

doc/user/_theory/theory.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ The :math:`K_{rad}(t)` function is derived by implementing the inverse continuou
114114
115115
This transform allows the frequency domain coefficients, :math:`B(\omega)`, to be remapped into the time domain, thus producing the radiation impulse response function, :math:`K_{rad}(t)`. The :math:`B(\omega)` values can be sourced using open-source BEM software.
116116

117+
In HydroChrono, the force is computed through trapezoidal integration at the time values given by the RIRF time array relative to the current simulation time step. Linear interpolation is done for the velocity history if a given time value is between two values of the time series of the stored velocity history.
117118
Wave excitation force, :math:`F_{exc}(t)`
118119
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
119120

include/hydroc/hydro_forces.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,6 @@ class ChLoadAddedMass;
153153
// TODO: Split TestHydro class from its helper classes for clearer code structure.
154154
class TestHydro {
155155
public:
156-
bool convTrapz_; // for use in ComputeForceRadiationDampingConv()
157156
TestHydro() = delete;
158157

159158
/**
@@ -193,6 +192,13 @@ class TestHydro {
193192
/**
194193
* @brief Computes the Radiation Damping force with convolution history for a 6N dimensional system.
195194
*
195+
* The discretization uses the time series of the the RIRF relative to the current step.
196+
* Linear interpolation is done on the velocity history if time_sim-time_rirf is between two values of the time
197+
* history. Trapezoidal integration is used to compute the force.
198+
*
199+
* Time history is automatically added in this function (so it should only be called once per time step), and
200+
* history that is older than the maximum RIRF time value is automatically removed.
201+
*
196202
* @return 6N dimensional force for 6 DOF and N bodies in system.
197203
*/
198204
std::vector<double> ComputeForceRadiationDampingConv();
@@ -244,12 +250,12 @@ class TestHydro {
244250
// Additional properties related to equilibrium and hydrodynamics
245251
std::vector<double> equilibrium_;
246252
std::vector<double> cb_minus_cg_;
247-
double rirf_timestep_;
248253
Eigen::VectorXd rirf_time_vector; // Assumed consistent for each body
249-
int offset_rirf; // For managing the circular nature of velocity history in convolution
254+
Eigen::VectorXd rirf_width_vector;
250255

251256
// Properties for velocity history management and time tracking
252-
std::vector<double> velocity_history_;
257+
std::vector<std::vector<std::vector<double>>> velocity_history_;
258+
std::vector<double> time_history_;
253259
double prev_time;
254260

255261
// Added mass related properties

src/h5fileinfo.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@
77
// TODO: this include statement list looks good
88
#include <H5Cpp.h>
99
#include <hydroc/h5fileinfo.h>
10-
#include <filesystem> // std::filesystem::absolute
10+
#include <filesystem> // std::filesystem::absolute
1111

1212
using namespace chrono; // TODO narrow this using namespace to specify what we use from chrono or put chrono:: in front
1313
// of it all?
1414

15-
1615
H5FileInfo::H5FileInfo(std::string file, int num_bod) {
1716
h5_file_name_ = file;
1817
num_bodies_ = num_bod;
@@ -41,7 +40,7 @@ HydroData H5FileInfo::ReadH5Data() {
4140
for (int i = 0; i < num_bodies_; i++) {
4241
// body data
4342
data_to_init.body_data_[i].body_name = "body" + std::to_string(i + 1);
44-
std::string bodyName = data_to_init.body_data_[i].body_name; // shortcut for reading later
43+
std::string bodyName = data_to_init.body_data_[i].body_name; // shortcut for reading later
4544
data_to_init.body_data_[i].body_num = i;
4645

4746
InitScalar(userH5File, bodyName + "/properties/disp_vol", data_to_init.body_data_[i].disp_vol);
@@ -54,7 +53,8 @@ HydroData H5FileInfo::ReadH5Data() {
5453

5554
Init1D(userH5File, bodyName + "/properties/cg", data_to_init.body_data_[i].cg);
5655
Init1D(userH5File, bodyName + "/properties/cb", data_to_init.body_data_[i].cb);
57-
Init2D(userH5File, bodyName + "/hydro_coeffs/linear_restoring_stiffness", data_to_init.body_data_[i].lin_matrix);
56+
Init2D(userH5File, bodyName + "/hydro_coeffs/linear_restoring_stiffness",
57+
data_to_init.body_data_[i].lin_matrix);
5858
Init2D(userH5File, bodyName + "/hydro_coeffs/added_mass/inf_freq", data_to_init.body_data_[i].inf_added_mass);
5959
data_to_init.body_data_[i].inf_added_mass *= rho;
6060
Init3D(userH5File, bodyName + "/hydro_coeffs/radiation_damping/impulse_response_fun/K",
@@ -242,5 +242,17 @@ int HydroData::GetRIRFDims(int i) const {
242242
}
243243

244244
Eigen::VectorXd HydroData::GetRIRFTimeVector() const {
245-
return body_data_[0].rirf_time_vector;
245+
double tol = 1e-10;
246+
// check if all time vectors are the same within tolerance
247+
auto& rirf_time_vector = body_data_[0].rirf_time_vector;
248+
for (size_t ii = 1; ii < body_data_.size(); ii++) {
249+
for (size_t jj = 0; jj < body_data_[ii].rirf_time_vector.size(); jj++) {
250+
if (abs(body_data_[ii].rirf_time_vector[jj] - rirf_time_vector[jj]) > tol) {
251+
throw std::runtime_error(
252+
"RIRF time vectors have to be exactly the same for all bodies. Difference found in body " +
253+
std::to_string(jj) + " at time index " + std::to_string(jj) + ".");
254+
}
255+
}
256+
}
257+
return rirf_time_vector;
246258
}

src/hydro_forces.cpp

Lines changed: 99 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@
1414
#include <chrono/physics/ChLoad.h>
1515
#include <unsupported/Eigen/Splines>
1616

17+
#include <Eigen/Dense>
1718
#include <algorithm>
1819
#include <cmath>
20+
#include <fstream>
21+
#include <iostream>
1922
#include <memory>
2023
#include <numeric> // std::accumulate
2124
#include <random>
22-
#include <vector>
23-
#include <Eigen/Dense>
24-
#include <fstream>
25-
#include <iostream>
2625
#include <stdexcept>
26+
#include <vector>
2727

2828
const int kDofPerBody = 6;
2929
const int kDofLinOrRot = 3;
@@ -96,7 +96,7 @@ ForceFunc6d::ForceFunc6d() : forces_{{this, 0}, {this, 1}, {this, 2}, {this, 3},
9696

9797
ForceFunc6d::ForceFunc6d(std::shared_ptr<ChBody> object, TestHydro* user_all_forces) : ForceFunc6d() {
9898
body_ = object;
99-
std::string temp = body_->GetNameString(); // remove "body" from "bodyN", convert N to int, get body num
99+
std::string temp = body_->GetNameString(); // remove "body" from "bodyN", convert N to int, get body num
100100
b_num_ = stoi(temp.erase(0, 4)); // 1 indexed TODO: fix b_num starting here to be 0 indexed
101101
all_hydro_forces_ = user_all_forces; // TODO switch to smart pointers? does this use = ?
102102
if (all_hydro_forces_ == NULL) {
@@ -166,19 +166,31 @@ TestHydro::TestHydro(std::vector<std::shared_ptr<ChBody>> user_bodies,
166166
: bodies_(user_bodies),
167167
num_bodies_(bodies_.size()),
168168
file_info_(H5FileInfo(h5_file_name, num_bodies_).ReadH5Data()) {
169-
170-
prev_time = -1;
171-
offset_rirf = 0;
169+
prev_time = -1;
172170

173171
// Set up time vector
174172
rirf_time_vector = file_info_.GetRIRFTimeVector();
175-
rirf_timestep_ = rirf_time_vector[1] - rirf_time_vector[0];
173+
// width array
174+
rirf_width_vector.resize(rirf_time_vector.size());
175+
for (int ii = 0; ii < rirf_width_vector.size(); ii++) {
176+
rirf_width_vector[ii] = 0.0;
177+
if (ii < rirf_time_vector.size() - 1) {
178+
rirf_width_vector[ii] += 0.5 * abs(rirf_time_vector[ii + 1] - rirf_time_vector[ii]);
179+
}
180+
if (ii > 0) {
181+
rirf_width_vector[ii] += 0.5 * abs(rirf_time_vector[ii] - rirf_time_vector[ii - 1]);
182+
}
183+
}
176184

177185
// Total degrees of freedom
178186
int total_dofs = kDofPerBody * num_bodies_;
179187

180188
// Initialize vectors
181-
velocity_history_.assign(file_info_.GetRIRFDims(2) * total_dofs, 0.0);
189+
time_history_.clear();
190+
velocity_history_.clear();
191+
for (int b = 0; b < num_bodies_; ++b) {
192+
velocity_history_.push_back(std::vector<std::vector<double>>(0));
193+
}
182194
force_hydrostatic_.assign(total_dofs, 0.0);
183195
force_radiation_damping_.assign(total_dofs, 0.0);
184196
total_force_.assign(total_dofs, 0.0);
@@ -238,48 +250,12 @@ void TestHydro::AddWaves(std::shared_ptr<WaveBase> waves) {
238250
user_waves_->Initialize();
239251
}
240252

241-
double TestHydro::GetVelHistoryVal(int step, int c) const {
242-
if (step < 0 || step >= file_info_.GetRIRFDims(2) || c < 0 || c >= num_bodies_ * kDofPerBody) {
243-
std::cout << "wrong vel history index " << std::endl;
244-
return 0;
245-
}
246-
247-
int index = c % kDofPerBody;
248-
int b = c / kDofPerBody; // 0 indexed
249-
int calculated_index = index + (kDofPerBody * b) + (kDofPerBody * num_bodies_ * step);
250-
251-
if (calculated_index >= num_bodies_ * kDofPerBody * file_info_.GetRIRFDims(2) || calculated_index < 0) {
252-
std::cout << "bad vel history math" << std::endl;
253-
return 0;
254-
}
255-
256-
return velocity_history_[calculated_index];
257-
}
258-
259-
double TestHydro::SetVelHistory(double val, int step, int b_num, int index) {
260-
if (step < 0 || step >= file_info_.GetRIRFDims(2) || b_num < 1 || b_num > num_bodies_ || index < 0 ||
261-
index >= kDofPerBody) {
262-
std::cout << "bad set vel history indexing" << std::endl;
263-
return 0;
264-
}
265-
266-
int calculated_index = index + (kDofPerBody * (b_num - 1)) + (kDofPerBody * num_bodies_ * step);
267-
268-
if (calculated_index < 0 || calculated_index >= num_bodies_ * kDofPerBody * file_info_.GetRIRFDims(2)) {
269-
std::cout << "bad set vel history math" << std::endl;
270-
return 0;
271-
}
272-
273-
velocity_history_[calculated_index] = val;
274-
return val;
275-
}
276-
277253
std::vector<double> TestHydro::ComputeForceHydrostatics() {
278254
assert(num_bodies_ > 0);
279255

280-
const double rho = file_info_.GetRhoVal();
281-
const auto g_acc = bodies_[0]->GetSystem()->Get_G_acc(); // assuming all bodies in same system
282-
const double gg = g_acc.Length();
256+
const double rho = file_info_.GetRhoVal();
257+
const auto g_acc = bodies_[0]->GetSystem()->Get_G_acc(); // assuming all bodies in same system
258+
const double gg = g_acc.Length();
283259

284260
for (int b = 0; b < num_bodies_; b++) {
285261
std::shared_ptr<chrono::ChBody> body = bodies_[b];
@@ -294,7 +270,7 @@ std::vector<double> TestHydro::ComputeForceHydrostatics() {
294270

295271
chrono::ChVectorN<double, kDofPerBody> body_displacement;
296272
for (int ii = 0; ii < kDofLinOrRot; ii++) {
297-
body_displacement[ii] = body_position[ii] - body_equilibrium[ii];
273+
body_displacement[ii] = body_position[ii] - body_equilibrium[ii];
298274
body_displacement[ii + kDofLinOrRot] = body_rotation[ii] - body_equilibrium[ii + kDofLinOrRot];
299275
}
300276

@@ -328,69 +304,92 @@ std::vector<double> TestHydro::ComputeForceRadiationDampingConv() {
328304
const int numRows = kDofPerBody * num_bodies_;
329305
const int numCols = kDofPerBody * num_bodies_;
330306

331-
// "Shift" everything left 1
332-
offset_rirf--; // Starts as 0 before timestep change
333-
334-
// Keep offset close to 0, avoids small chance of overflow errors in long simulations
335-
if (offset_rirf < -1 * size) {
336-
offset_rirf += size;
337-
}
338-
339307
assert(numRows * size > 0 && numCols > 0);
340308

341-
std::vector<double> timeseries(numRows * numCols * size, 0.0);
342-
std::vector<double> tmp_s(numRows * size, 0.0);
343-
344-
// Helper function for timeseries indexing
345-
auto TimeseriesIndex = [&](int row, int col, int step) { return (row * numCols * size) + (col * size) + step; };
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()) {
313+
throw std::runtime_error("Tried to compute the radiation damping convolution twice within the same time step!");
314+
}
315+
time_history_.insert(time_history_.begin(), t_sim);
346316

347-
// Helper function for tmp_s indexing
348-
auto TmpSIndex = [&](int row, int step) { return (row * size) + step; };
317+
// velocity history
318+
for (int b = 0; b < num_bodies_; b++) {
319+
auto& body = bodies_[b];
320+
auto& velocity_history_body = velocity_history_[b];
349321

350-
// Helper function for circular indexing
351-
auto CircularIndex = [&](int value) { return ((value % size) + size) % size; };
322+
auto vel = body->GetPos_dt();
323+
auto wvel = body->GetWvel_par();
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);
326+
}
352327

353-
// Set last entry as velocity
354-
for (int i = 0; i < 3; i++) {
355-
for (int b = 1; b <= num_bodies_; b++) {
356-
int vi = CircularIndex(size + offset_rirf);
357-
SetVelHistory(bodies_[b - 1]->GetPos_dt()[i], vi, b, i);
358-
SetVelHistory(bodies_[b - 1]->GetWvel_par()[i], vi, b, i + 3);
328+
// remove unnecessary history
329+
if (time_history_.size() > 1) {
330+
while (time_history_[time_history_.size() - 2] < t_min) {
331+
time_history_.pop_back();
332+
for (int b = 0; b < num_bodies_; b++) {
333+
auto& velocity_history_body = velocity_history_[b];
334+
velocity_history_body.pop_back();
335+
}
359336
}
360337
}
361338

362-
if (convTrapz_) {
363-
for (int row = 0; row < numRows; row++) {
364-
for (int st = 0; st < size; st++) {
365-
int vi = CircularIndex(st + offset_rirf);
366-
tmp_s[TmpSIndex(row, st)] = 0;
367-
for (int col = 0; col < numCols; col++) {
368-
timeseries[TimeseriesIndex(row, col, st)] = GetRIRFval(row, col, st) * GetVelHistoryVal(vi, col);
369-
tmp_s[TmpSIndex(row, st)] += timeseries[TimeseriesIndex(row, col, st)];
339+
if (time_history_.size() > 1) {
340+
int idx_history = 0;
341+
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;
347+
}
348+
if (idx_history >= time_history_.size() - 1) {
349+
break;
350+
}
351+
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) + ".");
370378
}
371-
if (st > 0) {
372-
// Integrate tmp_s
373-
force_radiation_damping_[row] += (tmp_s[TmpSIndex(row, st - 1)] + tmp_s[TmpSIndex(row, st)]) / 2.0 *
374-
(rirf_time_vector[st] - rirf_time_vector[st - 1]);
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+
}
375389
}
376390
}
377391
}
378392
}
379-
//else {
380-
// // Convolution integral assuming fixed dt
381-
// for (int row = 0; row < numRows; row++) {
382-
// double sumVelHistoryAndRIRF = 0.0;
383-
// for (int col = 0; col < numCols; col++) {
384-
// for (int st = 0; st < size; st++) {
385-
// int vi = CircularIndex(st + offset_rirf);
386-
// timeseries[TimeseriesIndex(row, col, st)] = GetRIRFval(row, col, st) * GetVelHistoryVal(vi, col);
387-
// sumVelHistoryAndRIRF += timeseries[TimeseriesIndex(row, col, st)];
388-
// }
389-
// }
390-
// force_radiation_damping_[row] -= sumVelHistoryAndRIRF * rirf_timestep;
391-
// }
392-
//}
393-
394393
return force_radiation_damping_;
395394
}
396395

@@ -450,8 +449,6 @@ double TestHydro::CoordinateFuncForBody(int b, int dof_index) {
450449
std::fill(force_radiation_damping_.begin(), force_radiation_damping_.end(), 0.0);
451450
std::fill(force_waves_.begin(), force_waves_.end(), 0.0);
452451

453-
// Compute forces using the trapezoidal rule (or other methods in the future)
454-
convTrapz_ = true;
455452
force_hydrostatic_ = ComputeForceHydrostatics();
456453
force_radiation_damping_ = ComputeForceRadiationDampingConv();
457454
force_waves_ = ComputeForceWaves();

0 commit comments

Comments
 (0)