Virtual Testbed
Ship dynamics simulator for extreme conditions
wave_numbers.cc
1 #include <vtestbed/config/real_type.hh>
2 #include <vtestbed/core/math.hh>
3 #include <vtestbed/core/wave_numbers.hh>
4 
5 namespace {
6 
7  template <class T>
8  inline bool
9  same_sign(T lhs, T rhs) {
10  return (lhs > T(0) && rhs >= T(0)) || (lhs < T(0) && rhs <= T(0));
11  }
12 
13  template <class T>
14  inline void
15  update(
18  ) {
19  for (const auto& half : halves) {
20  stats.length.update(half.length);
21  stats.number.update(half.number);
22  stats.height.update(half.height);
23  }
24  }
25 
26 }
27 
28 template <class T>
30 vtb::core::operator<<(std::ostream& out, const Wave_half<T>& rhs) {
31  return out
32  << "length=" << rhs.length
33  << ",wavenumber=" << rhs.number
34  << ",height=" << rhs.height;
35 }
36 
37 template <class T>
39 vtb::core::operator<<(std::ostream& out, const Wave_statistics<T>& rhs) {
40  out << "length: " << rhs.length << '\n';
41  out << "number: " << rhs.number << '\n';
42  out << "height: " << rhs.height << '\n';
43  return out;
44 }
45 
46 template <class T>
48 vtb::core::find_extrema(blitz::Array<T,1> func, const Grid<T, 1>& grid) {
50  const int n = func.extent(0);
51  if (n < 3) {
52  return extrema;
53  }
54  const T dx = grid.delta(0);
55  for (int i=1; i<n-1; ++i) {
56  const T y0 = func(i-1);
57  const T y1 = func(i);
58  const T y2 = func(i+1);
59  // If (x1,y1) is local extrema.
60  if (same_sign(y1-y0, y1-y2)) {
61  // Use parabola to find precise vertex coordinate.
62  // Formula was computed in Mathematica.
63  const T x1 = grid(i, 0);
64  const T k = dx*(dx + T(2)*x1);
65  const T a = (y0 - T(2)*y1 + y2) / (T(2)*dx*dx);
66  const T b = (-a*k - y1 + y2) / dx;
67  const T c = -a*(k + x1*x1) - b*(x1 + dx) + y2;
68  const T vx = -b / (T(2)*a);
69  const T vy = c - b*b / (T(4)*a);
70  extrema.emplace_back(vx, vy);
71  }
72  }
73  return extrema;
74 }
75 
76 template <class T>
78 vtb::core::find_wave_halves(const std::vector<blitz::TinyVector<T,2>>& extrema) {
79  constexpr auto pi_times_2 = base::constants<T>::pi(2);
80  using std::abs;
82  const int n = extrema.size();
83  if (n <= 1) {
84  return halves;
85  }
86  blitz::TinyVector<T,2> p;
87  p(0) = extrema[0](0);
88  p(1) = extrema[0](1);
89  for (int i=1; i<n; ++i) {
90  const T y0 = extrema[i-1](1);
91  const T x1 = extrema[i](0);
92  const T y1 = extrema[i](1);
93  // We care only about big waves with trough and crest
94  // located in different planes (positive and negative).
95  if (same_sign(y0, y1)) {
96  if (abs(p(1)) < abs(y1)) {
97  p(0) = x1;
98  p(1) = y1;
99  }
100  } else {
101  const T length = T(2)*(x1 - p(0));
102  const T number = pi_times_2 / length;
103  const T height = abs(y1 - p(1));
104  halves.emplace_back(length, number, height);
105  p(0) = x1;
106  p(1) = y1;
107  }
108  }
109  return halves;
110 }
111 
112 template <class T>
113 void
115  const std::vector<Wave_half<T>>& halves,
116  T& min_wave_number,
117  T& max_wave_number
118 ) {
119  const int n = halves.size();
120  for (int i=0; i<n; ++i) {
121  const T wave_number = halves[i].number;
122  if (wave_number < min_wave_number) {
123  min_wave_number = wave_number;
124  }
125  if (max_wave_number < wave_number) {
126  max_wave_number = wave_number;
127  }
128  }
129 }
130 
131 template <class T, int N>
133 vtb::core::wave_number_grid(const Wave_statistics_vector<T,N>& waves) {
134  Grid<T,N> wngrid;
135  for (int i=0; i<N; ++i) {
136  wngrid.lbound(i) = waves(i).number.min();
137  wngrid.ubound(i) = waves(i).number.max();
138  }
139  wngrid.begin() = 0;
140  wngrid.end() = 2;
141  for (int i=0; i<N; ++i) {
142  if (!(wngrid.lbound(i) < wngrid.ubound(i))) {
143  wngrid.lbound(i) = 0, wngrid.ubound(i) = 0;
144  }
145  }
146  return wngrid;
147 }
148 
149 template <class T>
150 vtb::core::Wave_statistics_vector<T,2>
151 vtb::core::wave_statistics(blitz::Array<T,2> surface, const Grid<T,2>& grid) {
152  Wave_statistics_vector<T,2> waves;
153  const auto& _ = blitz::Range::all();
154  const int n1 = surface.extent(0);
155  const int n2 = surface.extent(1);
156  // compute first dimension
157  for (int i=0; i<n2; ++i) {
158  const auto& extrema = find_extrema(surface(_,i), grid.select(0));
159  update(waves(0), find_wave_halves(extrema));
160  }
161  // compute second dimension
162  for (int i=0; i<n1; ++i) {
163  const auto& extrema = find_extrema(surface(i,_), grid.select(1));
164  update(waves(1), find_wave_halves(extrema));
165  }
166  return waves;
167 }
168 
169 template <class T>
171 vtb::core::temporal_wave_statistics(
172  blitz::Array<T,1> surface,
173  const Grid<T,1>& grid
174 ) {
175  Wave_statistics<T> waves;
176  update(waves, find_wave_halves(find_extrema(surface, grid)));
177  return waves;
178 }
179 
180 template <class T>
182 vtb::core::find_wave_number_range(blitz::Array<T,2> surface, const Grid<T,2>& grid) {
183  using blitz::Range;
184  Grid<T,2> wngrid{
187  {2,2}
188  };
189  const int n1 = surface.extent(0);
190  const int n2 = surface.extent(1);
191  // compute first dimension
192  for (int i=0; i<n2; ++i) {
194  find_wave_halves(find_extrema(surface(Range::all(), i), grid.select(0))),
195  wngrid.lbound(0),
196  wngrid.ubound(0)
197  );
198  }
199  // compute second dimension
200  for (int i=0; i<n1; ++i) {
202  find_wave_halves(find_extrema(surface(i, Range::all()), grid.select(1))),
203  wngrid.lbound(1),
204  wngrid.ubound(1)
205  );
206  }
207  for (int i=0; i<2; ++i) {
208  if (wngrid.lbound(i) > wngrid.ubound(i)) {
209  wngrid.lbound(i) = T(0);
210  wngrid.ubound(i) = T(0);
211  }
212  }
213  return wngrid;
214 }
215 
218  blitz::Array<VTB_REAL_TYPE, 1> func,
219  const Grid<VTB_REAL_TYPE, 1>& grid
220 );
221 
224  const std::vector<blitz::TinyVector<VTB_REAL_TYPE, 2>>& extrema
225 );
226 
227 template void
229  const std::vector<Wave_half<VTB_REAL_TYPE>>& halves,
230  VTB_REAL_TYPE& min_wave_number,
231  VTB_REAL_TYPE& max_wave_number
232 );
233 
234 
237  blitz::Array<VTB_REAL_TYPE, 2> surface,
238  const Grid<VTB_REAL_TYPE, 2>& grid
239 );
240 
242 vtb::core::wave_number_grid(const Wave_statistics_vector<VTB_REAL_TYPE,2>&);
243 
245 vtb::core::wave_number_grid(const Wave_statistics_vector<VTB_REAL_TYPE,3>&);
246 
247 template blitz::TinyVector<vtb::core::Wave_statistics<VTB_REAL_TYPE>,2>
248 vtb::core::wave_statistics(
249  blitz::Array<VTB_REAL_TYPE,2> surface,
250  const Grid<VTB_REAL_TYPE,2>& grid
251 );
252 
254 vtb::core::temporal_wave_statistics(
255  blitz::Array<VTB_REAL_TYPE,1> surface,
256  const Grid<VTB_REAL_TYPE,1>& grid
257 );
258 
259 template std::ostream&
260 vtb::core::operator<<(std::ostream&, const Wave_half<VTB_REAL_TYPE>&);
261 
262 template std::ostream&
263 vtb::core::operator<<(std::ostream&, const Wave_statistics<VTB_REAL_TYPE>&);
A half of the wave with length, number and height of the full wave.
Definition: wave_numbers.hh:45
std::vector< blitz::TinyVector< T, 2 > > find_extrema(blitz::Array< T, 1 > func, const Grid< T, 1 > &grid)
Find coordinates of minima and maxima of a function.
Definition: wave_numbers.cc:48
std::vector< Wave_half< T > > find_wave_halves(const std::vector< blitz::TinyVector< T, 2 >> &extrema)
Find wave halves from function extrema computed previously.
T min(T... args)
const T delta(int i) const
The size of the patch (interval) for dimension i.
Definition: core/grid.hh:239
A region defined by start and end index and lower and upper bound for each dimension.
Definition: core/grid.hh:25
T max(T... args)
T length(const blitz::TinyVector< T, N > &x)
Computes vector length without overflow.
Definition: blitz.hh:471
void find_wave_number_range(const std::vector< Wave_half< T >> &halves, T &min_wave_number, T &max_wave_number)
Find minimum and maximum wave number from wave halves computed previously.