/home/runner/work/amr-wind/amr-wind/amr-wind/wind_energy/actuator/turbine/fast/turbine_fast_ops.H Source File

AMR-Wind API: /home/runner/work/amr-wind/amr-wind/amr-wind/wind_energy/actuator/turbine/fast/turbine_fast_ops.H Source File
AMR-Wind API v0.1.0
CFD solver for wind plant simulations
Loading...
Searching...
No Matches
turbine_fast_ops.H
Go to the documentation of this file.
1#ifndef TURBINE_FAST_OPS_H
2#define TURBINE_FAST_OPS_H
3
10
12
13template <typename SrcTrait>
14struct ReadInputsOp<TurbineFast, SrcTrait>
15{
17 {
18 // Data common to any turbine actuator simulation
19 utils::read_inputs(data.meta(), data.info(), pp);
20
21 auto& tdata = data.meta();
22
23 // Get density value for normalization
24 pp.get("density", tdata.density);
25
26 // Check if user wants to override density checks
27 bool override_density_check = false;
28 pp.query("override_density_check", override_density_check);
29 // Check if simulation is a restart
30 bool is_restart = data.sim().io_manager().is_restart();
31
32 // Initialize OpenFAST specific data
33 const auto& tinfo = data.info();
34 auto& tf = data.meta().fast_data;
35 for (int i = 0; i < AMREX_SPACEDIM; ++i) {
36 tf.base_pos[i] = static_cast<float>(tinfo.base_pos[i]);
37 }
38
39 tf.tlabel = tinfo.label;
40 tf.tid_global = tinfo.id;
41 tf.num_blades = tdata.num_blades;
42 tf.num_pts_blade = tdata.num_pts_blade;
43 tf.num_pts_tower = tdata.num_pts_tower;
44 tf.dt_cfd = data.sim().time().delta_t();
45
46 pp.get("openfast_start_time", tf.start_time);
47 pp.get("openfast_stop_time", tf.stop_time);
48
49 std::string sim_mode = (tf.start_time > 0.0) ? "restart" : "init";
50 pp.query("openfast_sim_mode", sim_mode);
51
52 if (sim_mode == "init") {
53 tf.sim_mode = ::exw_fast::SimMode::init;
54 amrex::Print() << "Initializing turbine:" << tf.tlabel << std::endl;
55 } else if (sim_mode == "replay") {
56 tf.sim_mode = ::exw_fast::SimMode::replay;
57 amrex::Print() << "Replaying turbine:" << tf.tlabel << std::endl;
58 } else if (sim_mode == "restart") {
59 tf.sim_mode = ::exw_fast::SimMode::restart;
60 amrex::Print() << "Restarting turbine:" << tf.tlabel << std::endl;
61 } else {
62 amrex::Abort(
63 "Actuator: Invalid OpenFAST simulation mode: " + sim_mode);
64 }
65
66 // If we are using OpenFAST restart file, require that the user provide
67 // the path to the checkpoint file.
68 if (tf.sim_mode == ::exw_fast::SimMode::restart) {
69 pp.get("openfast_restart_file", tf.checkpoint_file);
70 } else {
71 pp.get("openfast_input_file", tf.input_file);
72 }
73
74 const auto& time = data.sim().time();
75 tf.chkpt_interval = time.chkpt_interval();
76
77 perform_checks(data);
78
79 perform_density_checks(
80 tdata.density, override_density_check, is_restart);
81 }
82
84 {
85 const auto& time = data.sim().time();
86 // Ensure that we are using fixed timestepping scheme
87 AMREX_ALWAYS_ASSERT(!time.adaptive_timestep());
88 }
89
90 // Check other density parsing arguments against turbine air density
92 const amrex::Real rho_tdef, const bool no_check, const bool is_rst)
93 {
94 // Check if other arguments are in input file
95 amrex::ParmParse pp_incflo("incflo");
96 amrex::ParmParse pp_cstdns("ConstValue.density");
97 bool is_incflo = pp_incflo.contains("density");
98 bool is_cstdns = pp_cstdns.contains("value");
99 // Flags for detecting conflicts
100 bool cft_incflo = false;
101 bool cft_cstdns = false;
102
103 // Do warnings or aborts depending on values
104 const amrex::Real tiny = std::numeric_limits<amrex::Real>::epsilon();
105 if (is_incflo) {
106 amrex::Real rho_incflo = 1.0;
107 pp_incflo.query("density", rho_incflo);
108 if (std::abs(rho_incflo - rho_tdef) > tiny) {
109 cft_incflo = true;
110 if (!no_check) {
111 amrex::Abort(
112 "Density conflict detected between TurbineFast "
113 "density and incflo.density.\n----- Values are " +
114 std::to_string(rho_tdef) + " and " +
115 std::to_string(rho_incflo) +
116 ", respectively. Check the problem setup and the "
117 "turbine definition.\n----- If this difference is "
118 "intended, set the override_density_check flag to "
119 "true.\n");
120 } else {
121 amrex::Print()
122 << "WARNING: Density conflict detected between FAST "
123 "Turbine density and incflo.density.\n----- Values "
124 "are " +
125 std::to_string(rho_tdef) + " and " +
126 std::to_string(rho_incflo) +
127 ", respectively. Abort overridden by flag.\n";
128 }
129 }
130 }
131 if (is_cstdns) {
132 amrex::Real rho_cstdns = 1.0;
133 pp_cstdns.query("value", rho_cstdns);
134 if (std::abs(rho_cstdns - rho_tdef) > tiny) {
135 cft_cstdns = true;
136 if (!no_check) {
137 amrex::Abort(
138 "Density conflict detected between TurbineFast "
139 "density and ConstValue.density.value.\n----- Values "
140 "are " +
141 std::to_string(rho_tdef) + " and " +
142 std::to_string(rho_cstdns) +
143 ", respectively. Check the problem setup and the "
144 "turbine definition.\n----- If this difference is "
145 "intended, set the override_density_check flag to "
146 "true.\n");
147 } else {
148 amrex::Print()
149 << "WARNING: Density conflict detected between "
150 "TurbineFast "
151 "density and "
152 "ConstValue.density.value.\n----- Values are " +
153 std::to_string(rho_tdef) + " and " +
154 std::to_string(rho_cstdns) +
155 ", respectively. Abort overridden by flag.\n";
156 }
157 }
158 }
159 if (!is_incflo && !is_cstdns) {
160 amrex::Print()
161 << "TurbineFast: no density conflict detected, but no density "
162 "arguments found to check against.\n";
163 } else if (!cft_incflo && !cft_cstdns) {
164 amrex::Print() << "TurbineFast: no density conflict detected.\n";
165 }
166 if (is_rst) {
167 amrex::Print()
168 << "TurbineFast: simulation begins using a checkpoint file, "
169 "density compatibility must be manually confirmed between "
170 "the precursor simulation and the specified TurbineFast "
171 "density, " +
172 std::to_string(rho_tdef)
173 << ".\n";
174 }
175 }
176};
177
178template <>
179inline void
181{
182 auto& info = data.info();
183 info.procs =
184 utils::determine_influenced_procs(data.sim().mesh(), info.bound_box);
185
186 AMREX_ALWAYS_ASSERT(info.root_proc > -1);
187 // During regrid, the influenced processes might have changed and might
188 // no longer include the root proc. We insert it back to ensure that it
189 // is always present on the list.
190 info.procs.insert(info.root_proc);
191
192 const int iproc = amrex::ParallelDescriptor::MyProc();
193 auto in_proc = info.procs.find(iproc);
194 info.actuator_in_proc = (in_proc != info.procs.end());
195 info.sample_vel_in_proc = info.is_root_proc;
196}
197
198template <>
200 typename TurbineFast::DataType& data, amrex::Vector<int>& act_proc_count)
201{
202 namespace utils = ::amr_wind::actuator::utils;
203 auto& info = data.info();
204 info.procs =
205 utils::determine_influenced_procs(data.sim().mesh(), info.bound_box);
206
207 utils::determine_root_proc(info, act_proc_count);
208
209 // TODO: This function is doing a lot more than advertised by the name.
210 // Should figure out a better way to perform the extra work.
211
212 // For OpenFAST we only need velocities sampled in root process
213 info.sample_vel_in_proc = info.is_root_proc;
214
215 // Initialize the OpenFAST object and register this turbine in the root
216 // process
217 if (info.is_root_proc) {
218 auto& tdata = data.meta();
219 auto& ext_mgr = data.sim().ext_solver_manager();
220 ext_mgr.create("OpenFAST", data.sim());
221 tdata.fast = &(ext_mgr.get<::exw_fast::FastIface>());
222 tdata.fast->register_turbine(tdata.fast_data);
223 }
224}
225
226template <typename SrcTrait>
227struct InitDataOp<TurbineFast, SrcTrait>
228{
230 {
231 BL_PROFILE("amr-wind::InitDataOp<TurbineFast>");
232
233 // Ensure that FAST simulation time is set properly before doing any
234 // initialization tasks. We perform this check here to account for
235 // restart which is only known after reading the checkpoint file.
236 check_fast_sim_time(data);
237
238 const auto& info = data.info();
239 auto& tdata = data.meta();
240
241 // Initialize our communicator for broadcasting data
242 amrex::ParallelDescriptor::Comm_dup(
243 amrex::ParallelDescriptor::Communicator(), tdata.tcomm);
244
245 amrex::Array<int, 5> sz_info = {0, 0, 0, 0, 0};
246 if (info.is_root_proc) {
247 tdata.fast->init_turbine(tdata.fast_data.tid_local);
248
249 const auto& tf = tdata.fast_data;
250 sz_info[0] = tf.num_blades;
251 sz_info[1] = tf.to_cfd.fx_Len;
252 sz_info[2] = tf.from_cfd.u_Len;
253 sz_info[3] = tf.num_pts_tower;
254 sz_info[4] = tf.num_blade_elem;
255 }
256
257 // Broadcast data to everyone
258 amrex::ParallelDescriptor::Bcast(
259 sz_info.begin(), sz_info.size(), info.root_proc, tdata.tcomm);
260
261 {
262 tdata.num_blades = sz_info[0];
263 // back calculate what the value per blade for number of points in
264 // the openfast data structure
265 tdata.num_vel_pts_blade = sz_info[4];
266 data.grid().resize(sz_info[1], sz_info[2]);
267 tdata.chord.resize(sz_info[1]);
268 tdata.num_pts_tower = sz_info[3];
269 }
270
271 tdata.vel_rel.assign(sz_info[1], vs::Vector::zero());
272
273 if (info.is_root_proc) {
274 // copy chord data
275 int npts = sz_info[1];
276 const auto& fchord = tdata.fast_data.to_cfd.forceNodesChord;
277 for (int i = 0; i < npts; ++i) {
278 tdata.chord[i] = static_cast<amrex::Real>(fchord[i]);
279 }
280 }
281
282 amrex::ParallelDescriptor::Bcast(
283 tdata.chord.data(), tdata.chord.size(), info.root_proc,
284 tdata.tcomm);
285
286 make_component_views(data);
287 init_epsilon(data);
288 }
289
291 {
292 auto& grid = data.grid();
293 auto& tdata = data.meta();
294 const int num_blades = tdata.num_blades;
295 const int num_pts_blade = tdata.num_pts_blade;
296 const int num_vel_pts_blade = tdata.num_vel_pts_blade;
297
298 for (int ib = 0; ib < num_blades; ++ib) {
299 ComponentView cv;
300
301 const auto start = ib * num_pts_blade + 1;
302 const auto start_vel = ib * num_vel_pts_blade;
303 // clang-format off
305 grid.pos, start, num_pts_blade);
307 grid.force, start, num_pts_blade);
309 grid.epsilon, start, num_pts_blade);
311 grid.orientation, start, num_pts_blade);
313 tdata.chord, start, num_pts_blade);
315 tdata.vel_rel, start, num_pts_blade);
317 grid.vel, start_vel, num_vel_pts_blade);
319 grid.vel_pos, start_vel, num_vel_pts_blade);
320 // clang-format on
321
322 tdata.blades.emplace_back(cv);
323 }
324 if (tdata.num_pts_tower > 0) {
325 const int num_pts_tower = tdata.num_pts_tower;
326 const int ntwr_start = num_blades * num_pts_blade + 1;
327 auto& cv = tdata.tower;
328
329 cv.pos =
330 ::amr_wind::utils::slice(grid.pos, ntwr_start, num_pts_tower);
331 cv.force =
332 ::amr_wind::utils::slice(grid.force, ntwr_start, num_pts_tower);
333 cv.epsilon = ::amr_wind::utils::slice(
334 grid.epsilon, ntwr_start, num_pts_tower);
335 cv.orientation = ::amr_wind::utils::slice(
336 grid.orientation, ntwr_start, num_pts_tower);
337 cv.chord = ::amr_wind::utils::slice(
338 tdata.chord, ntwr_start, num_pts_tower);
339 }
340 {
341 auto& cv = tdata.hub;
342 cv.pos = ::amr_wind::utils::slice(grid.pos, 0, 1);
343 cv.force = ::amr_wind::utils::slice(grid.force, 0, 1);
344 cv.epsilon = ::amr_wind::utils::slice(grid.epsilon, 0, 1);
345 cv.orientation = ::amr_wind::utils::slice(grid.orientation, 0, 1);
346 cv.chord = ::amr_wind::utils::slice(tdata.chord, 0, 1);
347 }
348 }
349
351 {
352 auto& tdata = data.meta();
353
354 // Swap order of epsilon based on FAST turbine orientation
355 swap_epsilon(tdata.eps_inp);
356 swap_epsilon(tdata.eps_min);
357 swap_epsilon(tdata.eps_chord);
358 swap_epsilon(tdata.eps_tower);
359
360 {
361 const auto& cd = tdata.nacelle_cd;
362 const auto& area = tdata.nacelle_area;
363 const auto eps =
364 std::sqrt(2.0 / ::amr_wind::utils::pi() * cd * area);
365
366 auto& nac_eps = data.grid().epsilon[0];
367 nac_eps.x() = amrex::max(eps, tdata.eps_min.x());
368 nac_eps.y() = amrex::max(eps, tdata.eps_min.y());
369 nac_eps.z() = amrex::max(eps, tdata.eps_min.z());
370 }
371
372 for (int ib = 0; ib < tdata.num_blades; ++ib) {
373 auto& cv = tdata.blades[ib];
374
375 for (int i = 0; i < tdata.num_pts_blade; ++i) {
376 const auto eps_crd = tdata.eps_chord * cv.chord[i];
377
378 for (int n = 0; n < AMREX_SPACEDIM; ++n) {
379 cv.epsilon[i][n] = amrex::max(
380 tdata.eps_min[n], tdata.eps_inp[n], eps_crd[n]);
381 }
382 }
383 }
384 {
385 auto& cv = tdata.tower;
386 for (int i = 0; i < tdata.num_pts_tower; ++i) {
387 for (int n = 0; n < AMREX_SPACEDIM; ++n) {
388 cv.epsilon[i][n] = amrex::max(
389 tdata.eps_min[n], tdata.eps_inp[n], tdata.eps_tower[n]);
390 }
391 }
392 }
393 }
394
395 inline void swap_epsilon(vs::Vector& eps)
396 {
397 const auto x = eps.x();
398 const auto y = eps.y();
399 eps.x() = y;
400 eps.y() = x;
401 }
402
404 {
405 const auto& time = data.sim().time();
406
407 // Set OpenFAST end time to be at least as long as the CFD time. User
408 // can choose a longer duration in input file.
409 const amrex::Real stop1 = time.stop_time() > 0.0
410 ? time.stop_time()
411 : std::numeric_limits<amrex::Real>::max();
412 const amrex::Real stop2 = time.stop_time_index() > -1
413 ? time.stop_time_index() * time.delta_t()
414 : std::numeric_limits<amrex::Real>::max();
415 const amrex::Real cfd_stop = amrex::min(stop1, stop2);
416 const amrex::Real cfd_start = time.current_time();
417 const amrex::Real cfd_sim = cfd_stop - cfd_start - 1.0e-6;
418
419 // Ensure that the user specified stop_time is not shorter than CFD sim
420 const auto& tf = data.meta().fast_data;
421 const amrex::Real fast_sim = tf.stop_time - tf.start_time;
422 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(
423 fast_sim > cfd_sim,
424 "OpenFAST simulation time is shorter than AMR-Wind duration");
425 }
426};
427
428template <typename SrcTrait>
429struct UpdatePosOp<TurbineFast, SrcTrait>
430{
432 {
433 // Return early if this is not the root process for this turbine
434 //
435 // This is handled in Actuator class, but we add a check here just as a
436 // safeguard
437 if (!data.info().is_root_proc) {
438 return;
439 }
440 BL_PROFILE("amr-wind::actuator::UpdatePosOp<TurbineFast>");
441
442 const auto& tdata = data.meta();
443 const auto& bp = data.info().base_pos;
444 const auto& pxvel = tdata.fast_data.to_cfd.pxVel;
445 const auto& pyvel = tdata.fast_data.to_cfd.pyVel;
446 const auto& pzvel = tdata.fast_data.to_cfd.pzVel;
447 auto& vel_pos = data.grid().vel_pos;
448 for (int i = 0; i < vel_pos.size(); ++i) {
449 vel_pos[i].x() = static_cast<amrex::Real>(pxvel[i]) + bp.x();
450 vel_pos[i].y() = static_cast<amrex::Real>(pyvel[i]) + bp.y();
451 vel_pos[i].z() = static_cast<amrex::Real>(pzvel[i]) + bp.z();
452 }
453 }
454};
455
456template <typename SrcTrait>
457struct UpdateVelOp<TurbineFast, SrcTrait>
458{
460 {
461 // Return early if this is not the root process for this turbine
462 //
463 // This is handled in Actuator class, but we add a check here just as a
464 // safeguard
465 if (!data.info().is_root_proc) {
466 return;
467 }
468 BL_PROFILE("amr-wind::actuator::UpdateVelOp<TurbineFast>");
469 auto& tdata = data.meta();
470
471 auto& from_cfd = tdata.fast_data.from_cfd;
472 auto& uvel = from_cfd.u;
473 auto& vvel = from_cfd.v;
474 auto& wvel = from_cfd.w;
475 const auto& vel = data.grid().vel;
476
477 if (!tdata.fllc.empty()) {
478 // Compute the relative velocity needed for the FLLC
479 for (int i = 0; i < tdata.vel_rel.size(); ++i) {
480 tdata.vel_rel[i][0] =
481 uvel[i] - static_cast<amrex::Real>(
482 tdata.fast_data.to_cfd.xdotForce[i]);
483 tdata.vel_rel[i][1] =
484 vvel[i] - static_cast<amrex::Real>(
485 tdata.fast_data.to_cfd.ydotForce[i]);
486 tdata.vel_rel[i][2] =
487 wvel[i] - static_cast<amrex::Real>(
488 tdata.fast_data.to_cfd.zdotForce[i]);
489 }
490 // Loop through each blade and apply the FLLC
491 for (int i = 0; i < tdata.num_blades; ++i) {
492 FLLCOp()(tdata.blades[i], tdata.fllc[i]);
493 }
494 }
495
496 for (int i = 0; i < from_cfd.u_Len; ++i) {
497 uvel[i] = static_cast<float>(vel[i].x());
498 vvel[i] = static_cast<float>(vel[i].y());
499 wvel[i] = static_cast<float>(vel[i].z());
500 }
501 }
502};
503
504template <typename SrcTrait>
506{
508 {
509 BL_PROFILE("amr-wind::actuator::ComputeForceOp<TurbineFast>");
510 // Advance OpenFAST by specified number of sub-steps
511 fast_step(data);
512 // Broadcast data to all the processes that contain patches influenced
513 // by this turbine
514 scatter_data(data);
515
516 const auto& time = data.sim().time();
517
518 auto& tdata = data.meta();
519 if (!tdata.fllc.empty()) {
520 for (int i = 0; i < tdata.num_blades; ++i) {
521 if (!(tdata.fllc[i].initialized) &&
522 (time.current_time() > tdata.fllc[i].fllc_start_time)) {
523 fllc_init(
524 tdata.fllc[i], tdata.blades[i], tdata.eps_chord[0]);
525 }
526 }
527 }
528 }
529
530 void fast_step(typename TurbineFast::DataType& data)
531 {
532 if (!data.info().is_root_proc) {
533 return;
534 }
535
536 auto& meta = data.meta();
537 auto& tf = data.meta().fast_data;
538 if (tf.is_solution0) {
539 meta.fast->init_solution(tf.tid_local);
540 } else {
541 meta.fast->advance_turbine(tf.tid_local);
542 }
543
544 meta.fast->get_hub_stats(tf.tid_local);
545
546 // Populate nacelle force into the OpenFAST data structure so that it
547 // gets broadcasted to all influenced processes in subsequent scattering
548 // of data.
549 compute_nacelle_force(data);
550 }
551
553 {
554 if (!data.info().is_root_proc) {
555 return;
556 }
557
558 const auto& cd = data.meta().nacelle_cd;
559 const auto& area = data.meta().nacelle_area;
560 const auto& cd_area = cd * area;
561 const auto& fcfd = data.meta().fast_data.from_cfd;
562 const auto& tcfd = data.meta().fast_data.to_cfd;
563 const auto& rho = data.meta().density;
564
565 const auto& eps = data.grid().epsilon[0].x();
566 vs::Vector vel{fcfd.u[0], fcfd.v[0], fcfd.w[0]};
567 amrex::Real correction = 0.0;
568 if (eps > 0.0) {
569 amrex::Real fac =
570 1.0 -
571 (cd_area) / (2.0 * ::amr_wind::utils::two_pi() * eps * eps);
572 correction = 1.0 / fac;
573 }
574 amrex::Real coeff =
575 0.5 * rho * cd_area * vs::mag(vel) * correction * correction;
576
577 tcfd.fx[0] = static_cast<float>(coeff * fcfd.u[0]);
578 tcfd.fy[0] = static_cast<float>(coeff * fcfd.v[0]);
579 tcfd.fz[0] = static_cast<float>(coeff * fcfd.w[0]);
580 }
581
583 {
584 if (!data.info().actuator_in_proc) {
585 return;
586 }
587
588 // Create an MPI transfer buffer that packs all data in one contiguous
589 // array. 3 floats for the position vector, 3 floats for the force
590 // vector, and 9 floats for the orientation matrix = 15 floats per
591 // actuator node.
592 const auto dsize = data.grid().pos.size() * 15;
593 amrex::Vector<float> buf(dsize);
594
595 // Copy data into MPI send/recv buffer from the OpenFAST data structure.
596 // Note, other procs do not have a valid data in those pointers.
597 if (data.info().is_root_proc) {
598 BL_PROFILE(
599 "amr-wind::actuator::ComputeForceOp<TurbineFast>::scatter1");
600 const auto& tocfd = data.meta().fast_data.to_cfd;
601 auto it = buf.begin();
602 std::copy(tocfd.fx, tocfd.fx + tocfd.fx_Len, it);
603 std::advance(it, tocfd.fx_Len);
604 std::copy(tocfd.fy, tocfd.fy + tocfd.fy_Len, it);
605 std::advance(it, tocfd.fy_Len);
606 std::copy(tocfd.fz, tocfd.fz + tocfd.fz_Len, it);
607 std::advance(it, tocfd.fz_Len);
608
609 std::copy(tocfd.pxForce, tocfd.pxForce + tocfd.pxForce_Len, it);
610 std::advance(it, tocfd.pxForce_Len);
611 std::copy(tocfd.pyForce, tocfd.pyForce + tocfd.pyForce_Len, it);
612 std::advance(it, tocfd.pyForce_Len);
613 std::copy(tocfd.pzForce, tocfd.pzForce + tocfd.pzForce_Len, it);
614 std::advance(it, tocfd.pzForce_Len);
615
616 // clang-format off
617 std::copy(tocfd.pOrientation,
618 tocfd.pOrientation + tocfd.pOrientation_Len, it);
619 // clang-format on
620 }
621
622 // Broadcast data to all influenced procs from the root process
623 const auto& procs = data.info().procs;
624 const int tag = 1001;
625 if (data.info().is_root_proc) {
626 BL_PROFILE(
627 "amr-wind::actuator::ComputeForceOp<TurbineFast>::scatter2");
628 for (const int ip : procs) {
629 if (ip == data.info().root_proc) {
630 continue;
631 }
632
633 amrex::ParallelDescriptor::Send(
634 buf.data(), dsize, ip, tag, data.meta().tcomm);
635 }
636 } else {
637 BL_PROFILE(
638 "amr-wind::actuator::ComputeForceOp<TurbineFast>::scatter2");
639 amrex::ParallelDescriptor::Recv(
640 buf.data(), dsize, data.info().root_proc, tag,
641 data.meta().tcomm);
642 }
643
644 // Populate the actuator grid data structures with data from the MPI
645 // send/recv buffer.
646 {
647 BL_PROFILE(
648 "amr-wind::actuator::ComputeForceOp<TurbineFast>::scatter3");
649 const auto& bp = data.info().base_pos;
650 auto& grid = data.grid();
651 const auto& npts = grid.pos.size();
652 const auto& rho = data.meta().density;
653 const size_t ifx = 0;
654 const size_t ify = ifx + npts;
655 const size_t ifz = ify + npts;
656 const size_t ipx = ifz + npts;
657 const size_t ipy = ipx + npts;
658 const size_t ipz = ipy + npts;
659 const size_t iori = ipz + npts;
660
661 for (int i = 0; i < npts; ++i) {
662 // Aerodynamic force vectors. Flip sign to get force on fluid.
663 // Divide by density as the source term computation will
664 // multiply by density before adding to momentum equation.
665 //
666 grid.force[i].x() =
667 -static_cast<amrex::Real>(buf[ifx + i]) / rho;
668 grid.force[i].y() =
669 -static_cast<amrex::Real>(buf[ify + i]) / rho;
670 grid.force[i].z() =
671 -static_cast<amrex::Real>(buf[ifz + i]) / rho;
672
673 // Position vectors of the actuator nodes. Add shift to base
674 // locations.
675 grid.pos[i].x() =
676 static_cast<amrex::Real>(buf[ipx + i]) + bp.x();
677 grid.pos[i].y() =
678 static_cast<amrex::Real>(buf[ipy + i]) + bp.y();
679 grid.pos[i].z() =
680 static_cast<amrex::Real>(buf[ipz + i]) + bp.z();
681
682 // Copy over the orientation matrix
683 //
684 // Note that we transpose the orientation matrix when copying
685 // from OpenFAST to AMR-Wind Tensor data structure. This is done
686 // so that post-multiplication of vector transforms from global
687 // to local reference frame.
688 const auto off = static_cast<int>(iori) +
689 i * AMREX_SPACEDIM * AMREX_SPACEDIM;
690 for (int j = 0; j < AMREX_SPACEDIM; ++j) {
691 for (int k = 0; k < AMREX_SPACEDIM; ++k) {
692 grid.orientation[i][j * AMREX_SPACEDIM + k] =
693 static_cast<amrex::Real>(
694 buf[off + j + k * AMREX_SPACEDIM]);
695 }
696 }
697 }
698
699 // Extract the rotor center of rotation
700 auto& meta = data.meta();
701 meta.rot_center = grid.pos[0];
702
703 // Rotor non-rotating reference frame
704 const auto xvec = grid.orientation[0].x().unit();
705 const auto yvec = vs::Vector::khat() ^ xvec;
706 const auto zvec = xvec ^ yvec;
707 meta.rotor_frame.rows(xvec, yvec.unit(), zvec.unit());
708 }
709 }
710};
711
712template <typename SrcTrait>
714{
715private:
717
719 std::string m_out_dir;
720
722 std::string m_nc_filename;
723
725 int m_out_freq{10};
726
727public:
729 : m_data(data)
730 {}
731
733 {
734 pp.query("output_frequency", m_out_freq);
735 }
736
737 void prepare_outputs(const std::string& out_dir)
738 {
739 m_nc_filename = out_dir + "/" + m_data.info().label + ".nc";
741 m_nc_filename, m_data.meta(), m_data.info(), m_data.grid());
742 }
743
745 {
746 const auto& time = m_data.sim().time();
747 const int tidx = time.time_index();
748 if ((m_out_freq > 0) && (tidx % m_out_freq != 0)) {
749 return;
750 }
751
753 m_nc_filename, m_data.meta(), m_data.info(), m_data.grid(),
754 time.new_time());
755 }
756};
757
758} // namespace amr_wind::actuator::ops
759
760#endif /* TURBINE_FAST_OPS_H */
amrex::AmrCore & mesh()
Return the AMR mesh hierarchy.
Definition CFDSim.H:54
SimTime & time()
Return simulation time control.
Definition CFDSim.H:58
ExtSolverMgr & ext_solver_manager()
Definition CFDSim.H:98
IOManager & io_manager()
Definition CFDSim.H:83
Type & create(const std::string &key, Args &&... args)
Definition CollMgr.H:38
bool is_restart() const
Definition IOManager.H:85
AMREX_FORCE_INLINE int time_index() const
Definition SimTime.H:114
AMREX_FORCE_INLINE amrex::Real stop_time() const
Definition SimTime.H:132
AMREX_FORCE_INLINE amrex::Real delta_t() const
Definition SimTime.H:84
AMREX_FORCE_INLINE int chkpt_interval() const
Definition SimTime.H:141
Definition actuator_types.H:184
CFDSim & sim()
Definition actuator_types.H:211
ActTrait::GridType & grid()
Definition actuator_types.H:217
ActTrait::InfoType & info()
Definition actuator_types.H:214
ActTrait::MetaType & meta()
Definition actuator_types.H:220
Definition MultiParser.H:18
void get(const std::string &name, vs::Vector &value) const
Definition MultiParser.H:42
void query(const std::string &name, vs::Vector &value) const
Definition MultiParser.H:57
Definition FastIface.H:21
Definition ActSrcLineOp.H:9
void determine_influenced_procs< TurbineFast >(typename TurbineFast::DataType &data)
Definition turbine_fast_ops.H:180
void determine_root_proc< TurbineFast >(typename TurbineFast::DataType &data, amrex::Vector< int > &act_proc_count)
Definition turbine_fast_ops.H:199
std::set< int > determine_influenced_procs(const amrex::AmrCore &mesh, const amrex::RealBox &rbx)
Definition actuator_utils.cpp:6
void write_netcdf(const std::string &ncfile, const TurbineBaseData &meta, const TurbineInfo &info, const ActGrid &grid, const amrex::Real time)
Definition turbine_utils.cpp:118
void read_inputs(TurbineBaseData &tdata, TurbineInfo &tinfo, const utils::ActParser &pp)
Definition turbine_utils.cpp:8
void prepare_netcdf_file(const std::string &ncfile, const TurbineBaseData &meta, const TurbineInfo &info, const ActGrid &grid)
Definition turbine_utils.cpp:52
void determine_root_proc(ActInfo &info, amrex::Vector< int > &act_proc_count)
Definition actuator_utils.cpp:34
void fllc_init(FLLCData &data, const ComponentView &view, const amrex::Real eps_chord)
Initialize FLLC data structure. This should be called at the end of the first ComputeForceOp to ensur...
Definition FLLC.cpp:6
Slice< T > slice(std::vector< T > &vec, const size_t start, const size_t count)
Definition Slice.H:66
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE constexpr amrex::Real two_pi()
Return .
Definition trig_ops.H:22
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE constexpr amrex::Real pi()
Return as an amrex::Real.
Definition trig_ops.H:16
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE T mag(const TensorT< T > &t)
Definition tensorI.H:182
@ restart
Restart using FAST checkpoint files.
@ replay
Replay using velocities stored in file.
@ init
Clean start.
Definition actuator_types.H:126
VecSlice vel
Definition actuator_types.H:133
VecSlice epsilon
Definition actuator_types.H:129
VecSlice vel_rel
Definition actuator_types.H:134
VecSlice force
Definition actuator_types.H:128
TensorSlice orientation
Definition actuator_types.H:130
RealSlice chord
Definition actuator_types.H:136
VecSlice pos
Definition actuator_types.H:127
VecSlice vel_pos
Definition actuator_types.H:132
This struct will operate on a blade/wing. The velocity from the simulation is corrected using the Fil...
Definition FLLCOp.H:17
Definition TurbineFast.H:22
void compute_nacelle_force(typename TurbineFast::DataType &data)
Definition turbine_fast_ops.H:552
void operator()(typename TurbineFast::DataType &data)
Definition turbine_fast_ops.H:507
void fast_step(typename TurbineFast::DataType &data)
Definition turbine_fast_ops.H:530
void scatter_data(typename TurbineFast::DataType &data)
Definition turbine_fast_ops.H:582
Definition actuator_ops.H:61
void check_fast_sim_time(typename TurbineFast::DataType &data)
Definition turbine_fast_ops.H:403
void swap_epsilon(vs::Vector &eps)
Definition turbine_fast_ops.H:395
void make_component_views(typename TurbineFast::DataType &data)
Definition turbine_fast_ops.H:290
void operator()(TurbineFast::DataType &data)
Definition turbine_fast_ops.H:229
void init_epsilon(typename TurbineFast::DataType &data)
Definition turbine_fast_ops.H:350
Definition actuator_ops.H:32
ProcessOutputsOp(typename TurbineFast::DataType &data)
Definition turbine_fast_ops.H:728
std::string m_nc_filename
NetCDF output filename for this turbine.
Definition turbine_fast_ops.H:722
TurbineFast::DataType & m_data
Definition turbine_fast_ops.H:716
void prepare_outputs(const std::string &out_dir)
Definition turbine_fast_ops.H:737
std::string m_out_dir
Path to the output directory (specified by Actuator physics class)
Definition turbine_fast_ops.H:719
void read_io_options(const utils::ActParser &pp)
Definition turbine_fast_ops.H:732
Definition actuator_ops.H:71
void perform_checks(typename TurbineFast::DataType &data)
Definition turbine_fast_ops.H:83
void perform_density_checks(const amrex::Real rho_tdef, const bool no_check, const bool is_rst)
Definition turbine_fast_ops.H:91
void operator()(TurbineFast::DataType &data, const utils::ActParser &pp)
Definition turbine_fast_ops.H:16
Definition actuator_ops.H:19
void operator()(typename TurbineFast::DataType &data)
Definition turbine_fast_ops.H:431
Definition actuator_ops.H:43
void operator()(typename TurbineFast::DataType &data)
Definition turbine_fast_ops.H:459
Definition actuator_ops.H:54
Definition vector.H:13
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE T & x() &noexcept
Definition vector.H:97
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE T & y() &noexcept
Definition vector.H:98
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE constexpr VectorT< T > zero()
Zero vector.
Definition vector.H:45
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE constexpr VectorT< T > khat(const T &z=Traits::one())
Definition vector.H:80