/home/runner/work/kynema-sgf/kynema-sgf/src/wind_energy/actuator/turbine/external/turbine_external_ops.H Source File

Kynema-SGF API: /home/runner/work/kynema-sgf/kynema-sgf/src/wind_energy/actuator/turbine/external/turbine_external_ops.H Source File
Kynema-SGF API v0.1.0
CFD solver for wind plant simulations
Loading...
Searching...
No Matches
turbine_external_ops.H
Go to the documentation of this file.
1#ifndef TURBINE_EXTERNAL_OPS_H
2#define TURBINE_EXTERNAL_OPS_H
3
10
11using namespace kynema_sgf::actuator::external;
12
14
15template <typename datatype>
16void read_ops(datatype& data, const utils::ActParser& pp)
17{
18 // Tell reading utility the name of the solver
19 data.meta().solver_name = data.meta().ext_ptr->identifier();
20 // Data common to any turbine actuator simulation
21 utils::read_inputs(data.meta(), data.info(), pp);
22
23 auto& tdata = data.meta();
24
25 // Get density value for normalization
26 pp.get("density", tdata.density);
27
28 // Copy data to external data holder
29 const auto& tinfo = data.info();
30 auto& tf = data.meta().ext_data;
31 for (int i = 0; i < AMREX_SPACEDIM; ++i) {
32 tf.base_pos[i] = static_cast<float>(tinfo.base_pos[i]);
33 }
34
35 tf.tlabel = tinfo.label;
36 tf.tid_global = tinfo.id;
37 tf.num_blades = tdata.num_blades;
38 tf.num_pts_blade = tdata.num_pts_blade;
39 tf.num_pts_tower = tdata.num_pts_tower;
40 const auto& time = data.sim().time();
41 tf.dt_cfd = time.delta_t();
42 tf.chkpt_interval = time.chkpt_interval();
43}
44
45template <typename datatype>
47{
48 auto& info = data.info();
49 info.procs =
50 utils::determine_influenced_procs(data.sim().mesh(), info.bound_box);
51
52 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(
53 info.root_proc > -1,
54 "info.root_proc = " + std::to_string(info.root_proc));
55 // During regrid, the influenced processes might have changed and might
56 // no longer include the root proc. We insert it back to ensure that it
57 // is always present on the list.
58 info.procs.insert(info.root_proc);
59
60 const int iproc = amrex::ParallelDescriptor::MyProc();
61 auto in_proc = info.procs.find(iproc);
62 info.actuator_in_proc = (in_proc != info.procs.end());
63 info.sample_vel_in_proc = info.is_root_proc;
64}
65
66template <typename datatype, typename SolverTurbine, typename SolverData>
68 datatype& data, amrex::Vector<int>& act_proc_count)
69{
71 auto& info = data.info();
72 info.procs =
73 utils::determine_influenced_procs(data.sim().mesh(), info.bound_box);
74
75 utils::determine_root_proc(info, act_proc_count);
76
77 // TODO: This function is doing a lot more than advertised by the name.
78 // Should figure out a better way to perform the extra work.
79
80 // For OpenFAST we only need velocities sampled in root process
81 info.sample_vel_in_proc = info.is_root_proc;
82
83 // Initialize the OpenFAST object and register this turbine in the root
84 // process
85 if (info.is_root_proc) {
86 auto& tdata = data.meta();
87 auto& ext_mgr = data.sim().ext_solver_manager();
88 ext_mgr.create(::ext_turb::ext_id<SolverData>(), data.sim());
89 tdata.ext_ptr =
90 &(ext_mgr.template get<
92 tdata.ext_ptr->register_turbine(tdata.ext_data);
93 }
94}
95
96template <typename datatype>
97void init_data_op(datatype& data)
98{
99
100 const auto& info = data.info();
101 auto& tdata = data.meta();
102
103 // Initialize our communicator for broadcasting data
104 amrex::ParallelDescriptor::Comm_dup(
105 amrex::ParallelDescriptor::Communicator(), tdata.tcomm);
106
107 amrex::Array<int, 6> sz_info = {0, 0, 0, 0, 0, 0};
108 if (info.is_root_proc) {
109 tdata.ext_ptr->init_turbine(tdata.ext_data.tid_local);
110
111 const auto& tf = tdata.ext_data;
112 sz_info[0] = tf.num_blades;
113 sz_info[1] = tf.length_force(0);
114 sz_info[2] = tf.length_fluid_velocity(0);
115 sz_info[3] = tf.num_pts_tower;
116 sz_info[4] = tf.num_vel_pts_blade();
117 sz_info[5] = tdata.ext_ptr->offset_velocity_points();
118 }
119
120 // Broadcast data to everyone
121 amrex::ParallelDescriptor::Bcast(
122 sz_info.begin(), sz_info.size(), info.root_proc, tdata.tcomm);
123
124 {
125 tdata.num_blades = sz_info[0];
126 // back calculate what the value per blade for number of points in
127 // the openfast data structure
128 tdata.num_vel_pts_blade = sz_info[4];
129 data.grid().resize(sz_info[1], sz_info[2]);
130 tdata.chord.resize(sz_info[1]);
131 tdata.num_pts_tower = sz_info[3];
132 tdata.velocity_points_offset = sz_info[5];
133 }
134
135 tdata.vel_rel.assign(sz_info[1], vs::Vector::zero());
136
137 if (info.is_root_proc) {
138 // copy chord data
139 int npts = sz_info[1];
140 for (int i = 0; i < npts; ++i) {
141 tdata.chord[i] =
142 static_cast<amrex::Real>(tdata.ext_data.chord_at_force()[i]);
143 }
144 }
145
146 amrex::ParallelDescriptor::Bcast(
147 tdata.chord.data(), tdata.chord.size(), info.root_proc, tdata.tcomm);
148
151}
152
153template <typename datatype>
154void update_pos_op(datatype& data)
155{
156 // Return early if this is not the root process for this turbine
157 //
158 // This is handled in Actuator class, but we add a check here just as a
159 // safeguard
160 if (!data.info().is_root_proc) {
161 return;
162 }
163
164 const auto& tdata = data.meta();
165 const auto& bp = data.info().base_pos;
166 const auto& pxvel = tdata.ext_data.position_at_vel(0);
167 const auto& pyvel = tdata.ext_data.position_at_vel(1);
168 const auto& pzvel = tdata.ext_data.position_at_vel(2);
169 auto& vel_pos = data.grid().vel_pos;
170 for (int i = 0; i < vel_pos.size(); ++i) {
171 vel_pos[i].x() = static_cast<amrex::Real>(pxvel[i]) + bp.x();
172 vel_pos[i].y() = static_cast<amrex::Real>(pyvel[i]) + bp.y();
173 vel_pos[i].z() = static_cast<amrex::Real>(pzvel[i]) + bp.z();
174 }
175}
176
177template <typename datatype>
178void update_vel_op(datatype& data)
179{
180 // Return early if this is not the root process for this turbine
181 //
182 // This is handled in Actuator class, but we add a check here just as a
183 // safeguard
184 if (!data.info().is_root_proc) {
185 return;
186 }
187 auto& tdata = data.meta();
188
189 const auto& uvel = tdata.ext_data.fluid_velocity(0);
190 const auto& vvel = tdata.ext_data.fluid_velocity(1);
191 const auto& wvel = tdata.ext_data.fluid_velocity(2);
192 const auto& vel = data.grid().vel;
193
194 if (!tdata.fllc.empty()) {
195 // Compute the relative velocity needed for the FLLC
196 const auto& xdot = tdata.ext_data.solid_velocity(0);
197 const auto& ydot = tdata.ext_data.solid_velocity(1);
198 const auto& zdot = tdata.ext_data.solid_velocity(2);
199 for (int i = 0; i < tdata.vel_rel.size(); ++i) {
200 tdata.vel_rel[i][0] = uvel[i] - static_cast<amrex::Real>(xdot[i]);
201 tdata.vel_rel[i][1] = vvel[i] - static_cast<amrex::Real>(ydot[i]);
202 tdata.vel_rel[i][2] = wvel[i] - static_cast<amrex::Real>(zdot[i]);
203 }
204 // Loop through each blade and apply the FLLC
205 for (int i = 0; i < tdata.num_blades; ++i) {
206 FLLCOp()(tdata.blades[i], tdata.fllc[i]);
207 }
208 }
209
210 for (int i = 0; i < tdata.ext_data.length_fluid_velocity(0); ++i) {
211 uvel[i] = static_cast<float>(vel[i].x());
212 vvel[i] = static_cast<float>(vel[i].y());
213 wvel[i] = static_cast<float>(vel[i].z());
214 }
215}
216
217template <typename datatype>
218void compute_force_op(datatype& data)
219{
220 // Advance external solver by specified number of sub-steps
221 ext_step<datatype>(data);
222 // Broadcast data to all the processes that contain patches influenced
223 // by this turbine
225
226 const auto& time = data.sim().time();
227
228 auto& tdata = data.meta();
229 if (!tdata.fllc.empty()) {
230 for (int i = 0; i < tdata.num_blades; ++i) {
231 if (!(tdata.fllc[i].initialized) &&
232 (time.current_time() > tdata.fllc[i].fllc_start_time)) {
233 fllc_init(tdata.fllc[i], tdata.blades[i], tdata.eps_chord[0]);
234 }
235 }
236 }
237}
238
239} // namespace kynema_sgf::actuator::ops
240
241#endif /* TURBINE_EXTERNAL_OPS_H */
Definition ExtTurbIface.H:26
void get(const std::string &name, vs::Vector &value) const
Definition MultiParser.H:44
std::string ext_id()
Definition turbine_external_utils.H:15
void ext_step(datatype &data)
Definition turbine_external_utils.H:173
void make_component_views(datatype &data)
Definition turbine_external_utils.H:27
void scatter_data(datatype &data)
Definition turbine_external_utils.H:196
void init_epsilon(datatype &data)
Definition turbine_external_utils.H:89
Definition ActSrcLineOp.H:12
void external_determine_root_proc(datatype &data, amrex::Vector< int > &act_proc_count)
Definition turbine_external_ops.H:67
void read_ops(datatype &data, const utils::ActParser &pp)
Definition turbine_external_ops.H:16
void update_pos_op(datatype &data)
Definition turbine_external_ops.H:154
void update_vel_op(datatype &data)
Definition turbine_external_ops.H:178
void external_determine_influenced_procs(datatype &data)
Definition turbine_external_ops.H:46
void init_data_op(datatype &data)
Definition turbine_external_ops.H:97
void compute_force_op(datatype &data)
Definition turbine_external_ops.H:218
Definition ActParser.H:6
::kynema_sgf::utils::MultiParser ActParser
Definition ActParser.H:8
std::set< int > determine_influenced_procs(const amrex::AmrCore &mesh, const amrex::RealBox &rbx)
Definition actuator_utils.cpp:8
void read_inputs(TurbineBaseData &tdata, TurbineInfo &tinfo, const utils::ActParser &pp)
Definition turbine_utils.cpp:11
void determine_root_proc(ActInfo &info, amrex::Vector< int > &act_proc_count)
Definition actuator_utils.cpp:36
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:9
This struct will operate on a blade/wing. The velocity from the simulation is corrected using the Fil...
Definition FLLCOp.H:21
AMREX_GPU_HOST_DEVICE static AMREX_FORCE_INLINE constexpr VectorT< amrex::Real > zero()
Definition vector.H:45