1515#include < fstream>
1616#include < iomanip>
1717#include < iostream>
18+ #include < limits>
1819
1920#include < hydroc/logging.h>
2021
22+ bool is_in_deep_water (double wavenumber, double water_depth) {
23+ // Keep behavior consistent with ComputeWaveNumber(), which treats non-positive depth, very large
24+ // depth, or infinite depth as "effectively infinite".
25+ constexpr double EFFECTIVE_INFINITE_DEPTH = 1000.0 ;
26+ if (water_depth <= 0.0 || water_depth > EFFECTIVE_INFINITE_DEPTH || std::isinf (water_depth)) {
27+ return true ;
28+ }
29+
30+ // Numerical threshold for using deep-water asymptotic form (tanh(kh) ≈ 1).
31+ return (std::abs (wavenumber) * water_depth > 89.4 );
32+ }
33+
34+ Eigen::Vector3d GetWheelerStretchedPosition (const Eigen::Vector3d& position, double eta, double water_depth, double mwl) {
35+ // Wheeler stretching assumes finite depth. If depth is being used as a sentinel for "infinite",
36+ // fall back to no stretching to avoid undefined behavior.
37+ if (water_depth <= 0.0 || std::isinf (water_depth)) {
38+ return position;
39+ }
40+
41+ // Position relative to mean water level
42+ double z_pos = position.z () - mwl;
43+ // Wheeler stretching
44+ double denom = water_depth + eta;
45+ const double scale = std::max (1.0 , std::abs (water_depth));
46+ const double eps = std::max (1e-12 * scale, 100.0 * std::numeric_limits<double >::epsilon () * scale);
47+ if (std::abs (denom) < eps) {
48+ // Pathological case (e.g., eta ≈ -water_depth): avoid division-by-zero blow-up.
49+ return position;
50+ }
51+ double z_stretched = water_depth * (z_pos - eta) / denom;
52+ return Eigen::Vector3d (position.x (), position.y (), z_stretched + mwl);
53+ }
54+
2155double GetEta (const Eigen::Vector3d& position,
2256 double time,
2357 double omega,
@@ -76,16 +110,31 @@ Eigen::Vector3d GetWaterVelocity(const Eigen::Vector3d& position,
76110 double z_pos = position.z () - mwl;
77111
78112 Eigen::Vector3d water_velocity (0.0 , 0.0 , 0.0 );
79- if (2 * M_PI / wavenumber > water_depth || wavenumber * water_depth > 500.0 ) {
113+ const double k_mag = std::abs (wavenumber);
114+ if (!std::isfinite (k_mag) || k_mag == 0.0 || !std::isfinite (omega) || omega == 0.0 || !std::isfinite (amplitude) || amplitude == 0.0 ) {
115+ return water_velocity;
116+ }
117+
118+ const double phase_arg = wavenumber * x_pos - omega * time + phase;
119+
120+ if (is_in_deep_water (wavenumber, water_depth)) {
80121 water_velocity[0 ] =
81- omega * amplitude * std::exp (wavenumber * z_pos) * cos (wavenumber * x_pos - omega * time + phase );
122+ omega * amplitude * std::exp (k_mag * z_pos) * cos (phase_arg );
82123 water_velocity[2 ] =
83- omega * amplitude * std::exp (wavenumber * z_pos) * sin (wavenumber * x_pos - omega * time + phase );
124+ omega * amplitude * std::exp (k_mag * z_pos) * sin (phase_arg );
84125 } else {
85- water_velocity[0 ] = omega * amplitude * std::cosh (wavenumber * (z_pos + water_depth)) /
86- std::sinh (wavenumber * water_depth) * cos (wavenumber * x_pos - omega * time + phase);
87- water_velocity[2 ] = omega * amplitude * std::sinh (wavenumber * (z_pos + water_depth)) /
88- std::sinh (wavenumber * water_depth) * sin (wavenumber * x_pos - omega * time + phase);
126+ const double kh = k_mag * water_depth;
127+ if (std::abs (kh) < 1e-8 ) {
128+ // Small-kh safe form (avoids sinh(kh)→0 leading to inf*0 -> NaN).
129+ // Uses the limiting behavior: cosh(k(z+h))/sinh(kh) ~ 1/(kh) and sinh(k(z+h))/sinh(kh) ~ (z+h)/h.
130+ const double omega_over_k = omega / k_mag;
131+ water_velocity[0 ] = omega_over_k * amplitude / water_depth * cos (phase_arg);
132+ water_velocity[2 ] = omega * amplitude * ((z_pos + water_depth) / water_depth) * sin (phase_arg);
133+ } else {
134+ const double denom = std::sinh (kh);
135+ water_velocity[0 ] = omega * amplitude * std::cosh (k_mag * (z_pos + water_depth)) / denom * cos (phase_arg);
136+ water_velocity[2 ] = omega * amplitude * std::sinh (k_mag * (z_pos + water_depth)) / denom * sin (phase_arg);
137+ }
89138 }
90139 return water_velocity;
91140}
@@ -102,16 +151,30 @@ Eigen::Vector3d GetWaterAcceleration(const Eigen::Vector3d& position,
102151 double z_pos = position.z () - mwl;
103152
104153 Eigen::Vector3d water_acceleration (0.0 , 0.0 , 0.0 );
105- if (2 * M_PI / wavenumber > water_depth || wavenumber * water_depth > 500.0 ) {
154+ const double k_mag = std::abs (wavenumber);
155+ if (!std::isfinite (k_mag) || k_mag == 0.0 || !std::isfinite (omega) || omega == 0.0 || !std::isfinite (amplitude) || amplitude == 0.0 ) {
156+ return water_acceleration;
157+ }
158+
159+ const double phase_arg = wavenumber * x_pos - omega * time + phase;
160+
161+ if (is_in_deep_water (wavenumber, water_depth)) {
106162 water_acceleration[0 ] =
107- omega * omega * amplitude * std::exp (wavenumber * z_pos) * sin (wavenumber * x_pos - omega * time + phase );
163+ omega * omega * amplitude * std::exp (k_mag * z_pos) * sin (phase_arg );
108164 water_acceleration[2 ] =
109- -omega * omega * amplitude * std::exp (wavenumber * z_pos) * cos (wavenumber * x_pos - omega * time + phase );
165+ -omega * omega * amplitude * std::exp (k_mag * z_pos) * cos (phase_arg );
110166 } else {
111- water_acceleration[0 ] = omega * omega * amplitude * std::cosh (wavenumber * (z_pos + water_depth)) /
112- std::sinh (wavenumber * water_depth) * sin (wavenumber * x_pos - omega * time + phase);
113- water_acceleration[2 ] = -omega * omega * amplitude * std::sinh (wavenumber * (z_pos + water_depth)) /
114- std::sinh (wavenumber * water_depth) * cos (wavenumber * x_pos - omega * time + phase);
167+ const double kh = k_mag * water_depth;
168+ if (std::abs (kh) < 1e-8 ) {
169+ // Small-kh safe form (avoids sinh(kh)→0 leading to inf*0 -> NaN).
170+ const double omega_over_k = omega / k_mag;
171+ water_acceleration[0 ] = omega * omega_over_k * amplitude / water_depth * sin (phase_arg);
172+ water_acceleration[2 ] = -omega * omega * amplitude * ((z_pos + water_depth) / water_depth) * cos (phase_arg);
173+ } else {
174+ const double denom = std::sinh (kh);
175+ water_acceleration[0 ] = omega * omega * amplitude * std::cosh (k_mag * (z_pos + water_depth)) / denom * sin (phase_arg);
176+ water_acceleration[2 ] = -omega * omega * amplitude * std::sinh (k_mag * (z_pos + water_depth)) / denom * cos (phase_arg);
177+ }
115178 }
116179 return water_acceleration;
117180}
0 commit comments