Virtual Testbed
Ship dynamics simulator for extreme conditions
gerstner_opencl.cc
1 #include <array>
2 #include <atomic>
3 #include <chrono>
4 #include <cstdio>
5 
6 #include <vtestbed/base/constants.hh>
7 #include <vtestbed/base/for_loop.hh>
8 #include <vtestbed/config/openmp.hh>
9 #include <vtestbed/config/real_type.hh>
10 #include <vtestbed/core/bisection.hh>
11 #include <vtestbed/core/gerstner.hh>
12 #include <vtestbed/core/linear_wave.hh>
13 #include <vtestbed/core/ship_hull_panel.hh>
14 #include <vtestbed/core/types.hh>
15 #include <vtestbed/geometry/cubic_spline.hh>
16 #include <vtestbed/geometry/spline_surface.hh>
17 #include <vtestbed/geometry/tetrahedron.hh>
18 #include <vtestbed/opencl/opencl.hh>
19 #include <vtestbed/opencl/pipeline.hh>
20 #include <vtestbed/opencl/vector.hh>
21 
25 using vtb::opencl::make_vector;
26 
27 namespace vtb {
28 
29  namespace core {
30 
31  template <class T>
33  public Gerstner_solver<T, 3>,
34  public Context_base {
35 
36  private:
38  using typename base_type::vertex_field_3d;
39  using typename base_type::scalar_field_3d;
40  using typename base_type::panel_array;
41  using typename base_type::panel_type;
42  using typename base_type::vertex_type;
43  using typename base_type::grid4;
44  using typename base_type::grid3;
45  using typename base_type::wave_type;
46  using typename base_type::ship_type;
47 
48  struct small_panel {
49  vertex_type centre{T{}}, normal{T{}};
50  inline small_panel(const vertex_type& c, const vertex_type& n):
51  centre(c), normal(n) {}
52  };
54 
55  static_assert(sizeof(small_panel) == sizeof(vertex_type)*2, "bad small_panel");
56  static_assert(sizeof(wave_type) == 4*sizeof(T), "bad wave_type");
57 
58  private:
59  clx::kernel _compute_forces;
60  std::array<clx::kernel,(1<<5)> _generate_field;
61  Buffer<wave_type> _d_waves;
62  Buffer<panel_type> _d_panels;
63  Buffer<small_panel> _d_small_panels;
64  Buffer<vertex_type> _d_surface;
65 
66  public:
67 
68  using Context_base::context;
69 
70  void context(Context* rhs) override {
71  Context_base::context(rhs);
72  using clock = std::chrono::system_clock;
74  auto t0 = clock::now();
75  size_t nkernels = this->_generate_field.size();
76  std::atomic<int> count{};
77  #if defined(VTB_WITH_OPENMP)
78  #pragma omp parallel for schedule(dynamic,1)
79  #endif
80  for (size_t i=0; i<nkernels; ++i) {
81  bool diffraction = i & 1,
82  radiation = i & 2,
83  position = i & 4,
84  velocity_and_potential = i & 8,
85  finite_depth = i & 16;
86  auto cc = context()->compiler();
87  auto options = cc.options();
88  if (diffraction) { options += " -DVTB_DIFFRACTION"; }
89  if (radiation) { options += " -DVTB_RADIATION"; }
90  if (position) { options += " -DVTB_POSITION"; }
91  if (velocity_and_potential) {
92  options += " -DVTB_VELOCITY -DVTB_POTENTIAL";
93  }
94  if (finite_depth) { options += " -DVTB_FINITE_DEPTH"; }
95  else { options += " -DVTB_INFINITE_DEPTH"; }
96  cc.options(options);
97  auto prog = cc.compile("gerstner.cl");
98  this->_generate_field[i] = prog.kernel("generate_field");
99  auto cnt = ++count;
100  if (clock::now()-t0 > seconds(1)) {
101  std::fprintf(stderr, "%5d/%-5lu compile gerstner\n", cnt, nkernels);
102  }
103  }
104  auto& cc = context()->compiler();
105  auto prog = cc.compile("gerstner.cl");
106  this->_compute_forces = prog.kernel("compute_forces");
107  }
108 
109  inline clx::kernel& generate_field_kernel(
110  bool position,
111  bool velocity_and_potential,
112  bool finite_depth
113  ) {
114  size_t i = 0;
115  if (this->diffraction()) { i |= 1; }
116  if (this->radiation()) { i |= 2; }
117  if (position) { i |= 4; }
118  if (velocity_and_potential) { i |= 8; }
119  if (finite_depth) { i |= 16; }
120  return this->_generate_field[i];
121  }
122 
123  void compute_forces(
124  const ship_type& ship,
125  const grid4& grid_tzxy,
126  panel_array & wetted_panels
127  ) override;
128 
129  void compute_positions(
130  const ship_type& ship,
131  const panel_array& panels,
132  const grid3& grid_txy,
133  vertex_field_3d& result
134  ) override;
135 
136  void generate_field(
137  const grid3& grid_zxy,
138  T t,
139  const panel_array& all_panels,
140  const ship_type& ship,
141  vertex_field_3d* position,
142  vertex_field_3d* velocity=nullptr,
143  scalar_field_3d* potential=nullptr
144  );
145 
146  };
147 
148  }
149 
150 }
151 
152 
153 
154 template <class T>
155 void
158  const ship_type& ship,
159  const grid4 & grid_tzxy,
160  panel_array & wetted_panels
161 ) {
162  // clamp grid to panels
163  grid3 grid_zxy = grid_tzxy.select(1,2,3);
164  if (this->clip()) { grid_zxy = clamp(grid_zxy, wetted_panels); }
165  if (grid_zxy.ubound(2) > 0) { grid_zxy.ubound(2) = 0; }
166  if (grid_zxy.empty()) { return; }
167  grid_zxy.compact();
168  this->_velocity_grid_zxy = grid_zxy;
169  // compute velocity at grid points
170  const T t = grid_tzxy.ubound(0);
171  auto& velocity = this->_velocity;
172  auto& potential = this->_potential;
173  velocity.resize(grid_zxy.shape());
174  potential.resize(grid_zxy.shape());
175  generate_field(grid_zxy, t, wetted_panels, ship, nullptr, &velocity, &potential);
176  auto& ppl = context()->pipeline();
177  auto& kernel = this->_compute_forces;
178  Buffer<panel_type> d_wetted_panels;
179  Buffer<vertex_type> d_velocity;
180  ppl.copy(wetted_panels, d_wetted_panels);
181  ppl.copy(velocity, d_velocity);
182  kernel.arguments(make_vector(grid_zxy.lbound()), make_vector(grid_zxy.ubound()),
183  make_vector(grid_zxy.shape()), d_velocity, d_wetted_panels);
184  ppl.step();
185  ppl.kernel(kernel, clx::range(wetted_panels.size()));
186  ppl.step();
187  ppl.copy(d_wetted_panels, wetted_panels);
188  ppl.wait();
189 }
190 
191 template <class T>
192 void
195  const ship_type& ship,
196  const panel_array& panels,
197  const grid3 & grid_txy,
198  vertex_field_3d& surface
199 ) {
200  const auto& _ = blitz::Range::all();
201  const auto& grid_xy = grid_txy.select(1, 2);
202  const T t = grid_txy.ubound(0);
203  grid3 grid_zxy{Grid<T,1>{T{},T{},1}, grid_txy.select(1), grid_txy.select(2)};
204  Array<vertex_type,3> position(grid_zxy.shape());
205  generate_field(grid_zxy, t, panels, ship, &position, nullptr, nullptr);
206  surface(grid_txy.end(0)-1,_,_) = position(0,_,_);
207 }
208 
209 template <class T> void
211  const grid3& grid_zxy,
212  T t,
213  const panel_array& all_panels,
214  const ship_type& ship,
215  vertex_field_3d* position,
216  vertex_field_3d* velocity,
217  scalar_field_3d* potential
218 ) {
219  const auto h = this->depth();
220  const bool infinite_depth = is_positive_infinity(h);
221  const bool waterline_only = this->waterline_only();
222  auto& ppl = context()->pipeline();
223  const auto& cc = context()->compiler();
224  auto& waves = this->_waves;
225  auto& d_waves = this->_d_waves;
226  ppl.copy(waves, d_waves);
227  auto& kernel = generate_field_kernel(position, velocity && potential, !infinite_depth);
228  kernel.arguments(t, h, d_waves, int(waves.size()), make_vector(grid_zxy.lbound()),
229  make_vector(grid_zxy.delta()), make_vector(grid_zxy.shape()));
230  size_t argument_index = 7;
231  if (this->diffraction() || this->radiation()) {
232  small_panel_array panels;
233  panels.reserve(all_panels.size());
234  for (const auto& panel : all_panels) {
235  if ((waterline_only && panel.waterline()) || (!waterline_only && panel.wetted())) {
236  panels.emplace_back(panel.centre(), panel.normal());
237  }
238  }
239  auto& d_panels = this->_d_small_panels;
240  int npanels = panels.size();
241  if (npanels != 0) {
242  ppl.copy(panels, d_panels);
243  }
244  kernel.argument(argument_index++, npanels);
245  kernel.argument(argument_index++, d_panels);
246  auto num_local_panels = std::numeric_limits<size_t>::max();
247  const auto& device = cc.devices().front();
248  auto wg = kernel.work_group(device);
249  auto local_memory_size = device.local_memory_size();
250  num_local_panels = std::max(size_t(1),
251  std::min({wg.size, local_memory_size / sizeof(small_panel), panels.size()}));
252  kernel.argument(argument_index++, int(num_local_panels));
253  kernel.argument(argument_index++, clx::local<small_panel>(num_local_panels));
254  }
255  if (position && !velocity && !potential) {
256  Buffer<vertex_type> d_result;
257  ppl.allocate(position->shape(), d_result);
258  kernel.argument(argument_index++, d_result);
259  ppl.step();
260  ppl.kernel(kernel, grid_zxy.shape());
261  ppl.step();
262  ppl.copy(d_result, *position);
263  ppl.wait();
264  } else if (!position && velocity && potential) {
265  Array<Vector<T,4>,3> velocity_and_potential(velocity->shape());
266  Buffer<Vector<T,4>> d_velocity_and_potential;
267  ppl.allocate(velocity->shape(), d_velocity_and_potential);
268  kernel.argument(argument_index++, d_velocity_and_potential);
269  ppl.step();
270  ppl.kernel(kernel, grid_zxy.shape());
271  ppl.step();
272  ppl.copy(d_velocity_and_potential, velocity_and_potential);
273  ppl.wait();
274  auto first = velocity->data();
275  for (const auto& v : velocity_and_potential) {
276  *first++ = vertex_type(v(0),v(1),v(2));
277  }
278  auto first2 = potential->data();
279  for (const auto& v : velocity_and_potential) { *first2++ = v(3); }
280  } else {
281  throw std::invalid_argument("this combination of position, velocity, potential "
282  "is not compiled");
283  }
284 }
285 
286 template <>
288 vtb::core::make_gerstner_solver<VTB_REAL_TYPE,3,vtb::core::Policy::OpenCL>() {
290  new Gerstner_solver_opencl<VTB_REAL_TYPE>
291  );
292 }
Trochoidal irrotational waves solves named after Gerstner.
Definition: gerstner.hh:31
Rigid ship with a mass and translational and angular velocity.
Definition: core/ship.hh:186
Triangular ship hull panel (face).
bool radiation() const
Calculate radiation forces?
Definition: gerstner.hh:77
T max(T... args)
Main namespace.
Definition: convert.hh:9
bool diffraction() const
Calculate diffraction forces?
Definition: gerstner.hh:72