29 auto& grid = data.grid();
30 auto& tdata = data.meta();
31 const int num_blades = tdata.num_blades;
32 const int num_pts_blade = tdata.num_pts_blade;
33 const int num_vel_pts_blade = tdata.num_vel_pts_blade;
34 const int offset_vel = tdata.velocity_points_offset;
36 for (
int ib = 0;
ib < num_blades; ++
ib) {
39 const auto start =
ib * num_pts_blade + 1;
40 const auto start_vel =
ib * num_vel_pts_blade + offset_vel;
43 grid.pos, start, num_pts_blade);
45 grid.force, start, num_pts_blade);
47 grid.epsilon, start, num_pts_blade);
49 grid.orientation, start, num_pts_blade);
51 tdata.chord, start, num_pts_blade);
53 tdata.vel_rel, start, num_pts_blade);
55 grid.vel, start_vel, num_vel_pts_blade);
57 grid.vel_pos, start_vel, num_vel_pts_blade);
60 tdata.blades.emplace_back(cv);
62 if (tdata.num_pts_tower > 0) {
63 const int num_pts_tower = tdata.num_pts_tower;
64 const int ntwr_start = num_blades * num_pts_blade + 1;
65 auto& cv = tdata.tower;
74 grid.orientation, ntwr_start, num_pts_tower);
91 auto& tdata = data.meta();
105 const auto& cd = tdata.nacelle_cd;
106 const auto& area = tdata.nacelle_area;
108 std::sqrt(2.0_rt / std::numbers::pi_v<amrex::Real> * cd * area);
110 auto& nac_eps = data.grid().epsilon[0];
111 nac_eps.x() = amrex::max<amrex::Real>(eps, tdata.eps_min.x());
112 nac_eps.y() = amrex::max<amrex::Real>(eps, tdata.eps_min.y());
113 nac_eps.z() = amrex::max<amrex::Real>(eps, tdata.eps_min.z());
116 for (
int ib = 0;
ib < tdata.num_blades; ++
ib) {
117 auto& cv = tdata.blades[
ib];
119 for (
int i = 0; i < tdata.num_pts_blade; ++i) {
120 const auto eps_crd = tdata.eps_chord * cv.chord[i];
122 for (
int n = 0; n < AMREX_SPACEDIM; ++n) {
123 cv.epsilon[i][n] = amrex::max<amrex::Real>(
124 tdata.eps_min[n], tdata.eps_inp[n], eps_crd[n]);
129 auto& cv = tdata.tower;
130 for (
int i = 0; i < tdata.num_pts_tower; ++i) {
131 for (
int n = 0; n < AMREX_SPACEDIM; ++n) {
132 cv.epsilon[i][n] = amrex::max<amrex::Real>(
133 tdata.eps_min[n], tdata.eps_inp[n], tdata.eps_tower[n]);
142 if (!data.info().is_root_proc) {
146 const auto& cd = data.meta().nacelle_cd;
147 const auto& area = data.meta().nacelle_area;
148 const auto& cd_area = cd * area;
149 const auto& ext_tdata = data.meta().ext_data;
150 const auto& rho = data.meta().density;
152 const auto& eps = data.grid().epsilon[0].x();
155 ext_tdata.fluid_velocity(0)[0], ext_tdata.fluid_velocity(1)[0],
156 ext_tdata.fluid_velocity(2)[0]};
157 amrex::Real correction = 0.0_rt;
162 correction = 1.0_rt / fac;
165 0.5_rt * rho * cd_area *
vs::mag(vel) * correction * correction;
167 for (
int dir = 0; dir < AMREX_SPACEDIM; ++dir) {
168 ext_tdata.force(dir)[0] =
static_cast<float>(coeff * vel[dir]);
198 if (!data.info().actuator_in_proc) {
206 const auto dsize = data.grid().pos.size() * 15;
207 amrex::Vector<float> buf(dsize);
211 if (data.info().is_root_proc) {
213 "kynema-sgf::actuator::external::compute_force_op::scatter1");
214 const auto& ext_tdata = data.meta().ext_data;
215 auto it = buf.begin();
216 for (
int dir = 0; dir < AMREX_SPACEDIM; ++dir) {
218 ext_tdata.force(dir),
219 ext_tdata.force(dir) + ext_tdata.length_force(dir), it);
220 std::advance(it, ext_tdata.length_force(dir));
222 for (
int dir = 0; dir < AMREX_SPACEDIM; ++dir) {
224 ext_tdata.position_at_force(dir),
225 ext_tdata.position_at_force(dir) +
226 ext_tdata.length_position_at_force(dir),
228 std::advance(it, ext_tdata.length_position_at_force(dir));
232 std::copy(ext_tdata.orientation(),
233 ext_tdata.orientation() + ext_tdata.length_orientation(), it);
238 const auto& procs = data.info().procs;
239 const int tag = 1001;
240 if (data.info().is_root_proc) {
242 "kynema-sgf::actuator::external::compute_force_op::scatter2");
243 for (
const int ip : procs) {
244 if (ip == data.info().root_proc) {
248 amrex::ParallelDescriptor::Send(
249 buf.data(), dsize, ip, tag, data.meta().tcomm);
253 "kynema-sgf::actuator::external::compute_force_op::scatter2");
254 amrex::ParallelDescriptor::Recv(
255 buf.data(), dsize, data.info().root_proc, tag, data.meta().tcomm);
262 "kynema-sgf::actuator::external::compute_force_op::scatter3");
263 const auto& bp = data.info().base_pos;
264 auto& grid = data.grid();
265 const auto& npts = grid.pos.size();
266 const auto& rho = data.meta().density;
267 const size_t ifx = 0;
268 const size_t ify = ifx + npts;
269 const size_t ifz = ify + npts;
270 const size_t ipx = ifz + npts;
271 const size_t ipy = ipx + npts;
272 const size_t ipz = ipy + npts;
273 const size_t iori = ipz + npts;
275 for (
int i = 0; i < npts; ++i) {
280 grid.force[i].x() = -
static_cast<amrex::Real
>(buf[ifx + i]) / rho;
281 grid.force[i].y() = -
static_cast<amrex::Real
>(buf[ify + i]) / rho;
282 grid.force[i].z() = -
static_cast<amrex::Real
>(buf[ifz + i]) / rho;
286 grid.pos[i].x() =
static_cast<amrex::Real
>(buf[ipx + i]) + bp.x();
287 grid.pos[i].y() =
static_cast<amrex::Real
>(buf[ipy + i]) + bp.y();
288 grid.pos[i].z() =
static_cast<amrex::Real
>(buf[ipz + i]) + bp.z();
297 static_cast<int>(iori) + i * AMREX_SPACEDIM * AMREX_SPACEDIM;
298 for (
int j = 0; j < AMREX_SPACEDIM; ++j) {
299 for (
int k = 0; k < AMREX_SPACEDIM; ++k) {
300 grid.orientation[i][j * AMREX_SPACEDIM + k] =
301 static_cast<amrex::Real
>(
302 buf[off + j + k * AMREX_SPACEDIM]);
308 auto& meta = data.meta();
309 meta.rot_center = grid.pos[0];
312 const auto xvec = grid.orientation[0].x().unit();
314 const auto zvec = xvec ^ yvec;
315 meta.rotor_frame.rows(xvec, yvec.unit(), zvec.unit());