/home/runner/work/amr-wind/amr-wind/amr-wind/wind_energy/actuator/disk/Joukowsky_ops.H Source File

AMR-Wind API: /home/runner/work/amr-wind/amr-wind/amr-wind/wind_energy/actuator/disk/Joukowsky_ops.H Source File
AMR-Wind API v0.1.0
CFD solver for wind plant simulations
Loading...
Searching...
No Matches
Joukowsky_ops.H
Go to the documentation of this file.
1#ifndef JOUKOWSKY_OPS_H
2#define JOUKOWSKY_OPS_H
3
4#include <algorithm>
5
10#include "AMReX_REAL.H"
11
12using namespace amrex::literals;
13
15
16namespace joukowsky {
17void parse_and_gather_params(const utils::ActParser& pp, JoukowskyData& data);
18AMREX_INLINE void
19set_current_angular_velocity(JoukowskyData& data, const amrex::Real uInfSqr)
20{
21 const amrex::Real uInfMag = std::sqrt(uInfSqr);
23 data.table_velocity, data.angular_velocity, uInfMag);
24}
26 const std::string& name,
27 const JoukowskyData& data,
28 const ActInfo& info,
29 const ActGrid& grid);
30
31void write_netcdf(
32 const std::string& name,
33 const JoukowskyData& data,
34 const ActInfo& info,
35 const ActGrid& /*unused*/,
36 amrex::Real time);
37
39
40} // namespace joukowsky
41
42template <>
52
53template <>
55{
56 void operator()(typename Joukowsky::DataType& data)
57 {
58
59 if (!data.sim().helics().is_activated()) {
60 return;
61 }
62
63#ifdef AMR_WIND_USE_HELICS
64
65 auto& meta = data.meta();
66
67 const amrex::Real turbine_direction =
68 -data.sim().helics().m_turbine_yaw_to_amrwind[data.info().id] +
69 90.0_rt;
70 const amrex::Real turbine_direction_radian =
71 amr_wind::utils::radians(turbine_direction);
72
73 meta.normal_vec[0] = std::cos(turbine_direction_radian);
74 meta.normal_vec[1] = std::sin(turbine_direction_radian);
75 meta.normal_vec[2] = 0.0_rt;
76
77 meta.sample_vec[0] = meta.normal_vec[0];
78 meta.sample_vec[1] = meta.normal_vec[1];
79 meta.sample_vec[2] = meta.normal_vec[2];
80
82#endif
83 }
84};
85
86template <>
88{
89 void operator()(typename Joukowsky::DataType& data)
90 {
92
93 auto& meta = data.meta();
94 meta.tip_correction.resize(meta.num_vel_pts_r);
95 meta.root_correction.resize(meta.num_vel_pts_r);
96 meta.f_normal.resize(meta.num_vel_pts_r);
97 meta.f_theta.resize(meta.num_force_pts);
98
99 std::ranges::fill(meta.tip_correction, 1.0_rt);
100
101 if (meta.use_root_correction) {
102 const amrex::Real dx = 1.0_rt / meta.num_vel_pts_r;
103
104 const auto a = meta.root_correction_coefficient;
105 const auto b = meta.root_correction_exponent;
106
107 const auto delta = amrex::max<amrex::Real>(
108 meta.vortex_core_size / meta.radius(),
110
111 const auto factor = dx / delta;
112
113 for (int i = 0; i < meta.root_correction.size(); ++i) {
114 meta.root_correction[i] =
115 1.0_rt - std::exp(-a * std::pow((i + 0.5_rt) * factor, b));
116 }
117 } else {
118 std::ranges::fill(meta.root_correction, 1.0_rt);
119 }
120
122 }
123};
124
140template <>
142{
144 {
145 // Equation comments refer to Sorenson 2020 (full reference above)
146 auto& grid = data.grid();
147 auto& ddata = data.meta();
148 const amrex::Real machine_eps = vs::DTraits<amrex::Real>::eps();
149 ddata.disk_force *= 0.0_rt;
150
151 const amrex::Real uInfSqr = base::compute_reference_velocity_sqr(ddata);
152 const amrex::Real U_ref =
153 amrex::max<amrex::Real>(std::sqrt(uInfSqr), machine_eps);
154 base::set_thrust_coefficient(ddata, uInfSqr);
156
157 const amrex::Real Ct = ddata.current_ct;
158 const amrex::Real dx = ddata.dr / ddata.radius();
159
160 const ops::base::AreaComputer area(
161 ddata.radius(), ddata.num_vel_pts_r, ddata.num_vel_pts_t);
162
163 const amrex::Real lambda =
164 ddata.radius() * ddata.current_angular_velocity / U_ref;
165
166 ddata.current_tip_speed_ratio = lambda;
167
168 const amrex::Real lambda_2 = lambda * lambda;
169
170 // step 0: compute tip correction based on current wind speed (eq 9)
171 if (ddata.use_tip_correction) {
172 auto& f = ddata.tip_correction;
173 for (int i = 0; i < f.size(); ++i) {
174 const auto x = (i + 0.5_rt) * dx;
175 f[i] = 2.0_rt / std::numbers::pi_v<amrex::Real> *
176 std::acos(
177 std::exp(
178 -0.5_rt * ddata.num_blades *
179 std::sqrt(1.0_rt + lambda_2) * (1.0_rt - x)));
180 }
181 }
182
183 // Compute the solid body term S0 (Sorensen 2022, eq. 27)
184 amrex::Real S0 = 0.0_rt;
185 const amrex::Real alpha1 = ddata.S0_alpha1;
186 const amrex::Real alpha2 = ddata.S0_alpha2;
187
188 if (ddata.Ct_rated > 0.0_rt) {
189 if (Ct < ddata.Ct_rated) {
190 S0 = alpha1 *
191 std::pow((ddata.Ct_rated - Ct) / ddata.Ct_rated, 3.0_rt);
192 } else {
193 S0 = alpha2 * (ddata.Ct_rated - Ct) / ddata.Ct_rated;
194 }
195 }
196
197 // step 1: compute Ct, a1 and a2 coefficients (eq 16)
198 // a3, a4, a5 coefficients are from Sorensen, 2022
199 amrex::Real a1 = 0.0_rt;
200 amrex::Real a2 = 0.0_rt;
201 amrex::Real a3 = 0.0_rt;
202 amrex::Real a4 = 0.0_rt;
203 amrex::Real a5 = 0.0_rt;
204
205 for (int ip = 0; ip < ddata.num_vel_pts_r; ip++) {
206 const amrex::Real x =
207 amrex::max<amrex::Real>((ip + 0.5_rt) * dx, machine_eps);
208
209 a1 += std::pow(ddata.tip_correction[ip], 2.0_rt) *
210 std::pow(ddata.root_correction[ip], 2.0_rt) / x * dx;
211
212 a2 += ddata.tip_correction[ip] * ddata.root_correction[ip] * x * dx;
213
214 a3 += std::pow(ddata.tip_correction[ip], 2.0_rt) *
215 std::pow(ddata.root_correction[ip], 2.0_rt) * x * dx;
216
217 a4 += ddata.tip_correction[ip] * ddata.root_correction[ip] *
218 std::pow(x, 3.0_rt) * dx;
219
220 a5 += std::pow(ddata.tip_correction[ip], 2.0_rt) *
221 std::pow(ddata.root_correction[ip], 2.0_rt) *
222 std::pow(x, 3.0_rt) * dx;
223 }
224
225 // step 2: determine the circulation (q0) from a1, a2 and Ct
226 // (eq. 18, Sorensen, 2022)
227
228 // sqrt[(a2*L+a3*S0)^2+a1*(0.5_rt*Ct-2*a4*L*S0-a5*S0^2)]-(a2*L+a3*S0)
229 // q0 = --------------------------------------------------------------
230 // a1
231
232 const amrex::Real term1 = std::pow((a2 * lambda) + (a3 * S0), 2.0_rt);
233 const amrex::Real term2 =
234 a1 * (0.5_rt * Ct - 2.0_rt * a4 * lambda * S0 - a5 * S0 * S0);
235 const amrex::Real term3 = (a2 * lambda) + (a3 * S0);
236
237 const amrex::Real numerator = std::sqrt(term1 + term2) - term3;
238
239 const amrex::Real denominator =
240 amrex::max<amrex::Real>(a1, machine_eps);
241
242 const amrex::Real q0 = numerator / denominator;
243
244 // step 3: compute normal force (fz) and azimuthal force (f_theta) (eq
245 // 13) and cp (eq 20)
246
247 VecSlice disk_velocity = ::amr_wind::utils::slice(
248 grid.vel, ddata.num_force_pts, ddata.num_force_pts);
249
250 amrex::Real moment = 0.0_rt;
251
252 int ip = 0;
253
254 const auto dTheta = amr_wind::utils::two_pi() / ddata.num_vel_pts_t;
255 const auto current_time = data.sim().time().current_time();
256
257 for (int i = 0; i < ddata.num_vel_pts_r; i++) {
258 const amrex::Real& F = ddata.tip_correction[i];
259 const amrex::Real& g = ddata.root_correction[i];
260
261 const amrex::Real x =
262 amrex::max<amrex::Real>((i + 0.5_rt) * dx, machine_eps);
263
264 const amrex::Real f_z =
265 (q0 / x + S0 * x) * g * F *
266 (lambda * x + 0.5_rt * (q0 / x + S0 * x) * g * F);
267
268 ddata.f_normal[i] = f_z;
269
270 const auto dArea = area.area_section(i);
271
272 for (int j = 0; j < ddata.num_vel_pts_t; j++, ip++) {
273 const auto point_current = grid.pos[ip];
274 const auto theta_angle = j * dTheta;
275
276 // TODO add sign convention for rotation (+/- RPM)
277 // Negative sign on theta_vec means turbine is rotating
278 // clockwise when standing upstream looking downstream
279 const auto theta_vec = -utils::compute_tangential_vector(
280 ddata.center, ddata.normal_vec, point_current);
281
282 // normal vec by definition is opposite of the wind direction
283 // so we need to flip the sign to give the actual disk velocity
284 const amrex::Real u_disk_ij =
285 -ddata.normal_vec & disk_velocity[ip];
286
287 const amrex::Real f_theta =
288 u_disk_ij / U_ref * (q0 / x + S0 * x) * g * F;
289 ddata.f_theta[ip] = f_theta;
290
291 // eq 18
292 moment += x * ddata.radius() * f_theta * dArea;
293
294 const amrex::Real f_awc =
295 ddata.awc_amplitude *
296 std::cos(
297 (ddata.awc_angular_frequency * current_time) -
298 (ddata.awc_azimuthal_mode * theta_angle) +
299 ddata.awc_clocking_angle);
300
301 grid.force[ip] = ((1 + f_awc) * f_z * ddata.normal_vec +
302 f_theta * theta_vec) *
303 ddata.density * uInfSqr * dArea;
304
305 ddata.disk_force = ddata.disk_force + grid.force[ip];
306 }
307 }
308
309 // equation 20
310 const amrex::Real eq_20_denominator = amrex::max<amrex::Real>(
311 0.5_rt * std::numbers::pi_v<amrex::Real> * ddata.density *
312 std::pow(ddata.radius(), 2.0_rt) * std::pow(U_ref, 3.0_rt),
313 machine_eps);
314
315 ddata.current_power =
316 ddata.density * uInfSqr * moment * ddata.current_angular_velocity;
317
318 ddata.current_cp = ddata.density * uInfSqr * moment *
319 ddata.current_angular_velocity / eq_20_denominator;
320#ifdef AMR_WIND_USE_HELICS
321 if (data.info().is_root_proc && data.sim().helics().is_activated()) {
322
323 std::cout << "turbine: " << data.info().id
324 << " jouk power: " << ddata.current_power
325 << " uinfsqr: " << uInfSqr << " moment: " << moment
326 << " ang vel: " << ddata.current_angular_velocity
327 << " normal vec: " << ddata.normal_vec[0] << ' '
328 << ddata.normal_vec[1] << std::endl;
329
331 ddata.current_power;
332 const amrex::Real turbine_angle = std::atan2(
333 ddata.reference_velocity[1], ddata.reference_velocity[0]);
334 data.sim()
335 .helics()
337 270.0_rt - amr_wind::utils::degrees(turbine_angle);
338 }
339#endif
340 }
341};
342
343template <>
345{
346private:
347 // cppcheck-suppress uninitMemberVarPrivate
350 std::string m_out_dir;
351
353 std::string m_nc_filename;
354
356 int m_out_freq{10};
357
358public:
359 // cppcheck-suppress constParameter
361 const Joukowsky::DataType& data)
362 : m_data(data)
363 {}
366 {
367 pp.query("output_frequency", m_out_freq);
368 }
369 void prepare_outputs(const std::string& out_dir)
370 {
371 m_nc_filename = out_dir + "/" + m_data.info().label + ".nc";
373 m_nc_filename, m_data.meta(), m_data.info(), m_data.grid());
374 }
376 {
377 const auto& time = m_data.sim().time();
378 const int tidx = time.time_index();
379 if (tidx % m_out_freq != 0) {
380 return;
381 }
382
384 m_nc_filename, m_data.meta(), m_data.info(), m_data.grid(),
385 time.new_time());
386 }
387};
388
389} // namespace amr_wind::actuator::ops
390
391#endif /* Joukowsky_OPS_H */
HelicsStorage & helics()
Definition CFDSim.H:120
SimTime & time()
Return simulation time control.
Definition CFDSim.H:65
amrex::Vector< amrex::Real > m_turbine_wind_direction_to_controller
Definition helics.H:39
amrex::Vector< amrex::Real > m_turbine_yaw_to_amrwind
Definition helics.H:40
amrex::Vector< amrex::Real > m_turbine_power_to_controller
Definition helics.H:38
bool is_activated() const
Definition helics.H:26
AMREX_FORCE_INLINE amrex::Real current_time() const
Definition SimTime.H:108
CFDSim & sim()
Definition actuator_types.H:214
ActTrait::GridType & grid()
Definition actuator_types.H:223
ActTrait::InfoType & info()
Definition actuator_types.H:217
ActTrait::MetaType & meta()
Definition actuator_types.H:229
amrex::Real area_section(int iRadius) const
Definition disk_ops.cpp:113
void query(const std::string &name, vs::Vector &value) const
Definition MultiParser.H:59
AMREX_INLINE void set_thrust_coefficient(DiskBaseData &data, const amrex::Real &uInfSqr)
Definition disk_ops.H:129
AMREX_INLINE amrex::Real compute_reference_velocity_sqr(DiskBaseData &data)
Definition disk_ops.H:116
void allocate_basic_grid_quantities(typename T::DataType &data)
Definition disk_ops.H:79
void do_parse_based_computations(ActDataHolder< T > &data)
Definition disk_ops.H:92
Definition Joukowsky_ops.cpp:9
AMREX_INLINE void set_current_angular_velocity(JoukowskyData &data, const amrex::Real uInfSqr)
Definition Joukowsky_ops.H:19
void write_netcdf(const std::string &name, const JoukowskyData &data, const ActInfo &info, const ActGrid &, const amrex::Real time)
Definition Joukowsky_ops.cpp:159
void update_disk_points(Joukowsky::DataType &data)
Definition Joukowsky_ops.cpp:65
void prepare_netcdf_file(const std::string &name, const JoukowskyData &data, const ActInfo &info, const ActGrid &grid)
Definition Joukowsky_ops.cpp:86
void parse_and_gather_params(const utils::ActParser &pp, JoukowskyData &data)
Definition Joukowsky_ops.cpp:54
Definition ActSrcLineOp.H:12
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE vs::Vector compute_tangential_vector(const vs::Vector &center, const vs::Vector &normal, const vs::Vector &point)
Definition actuator_utils.H:134
::amr_wind::utils::MultiParser ActParser
Definition ActParser.H:8
::amr_wind::utils::Slice< amr_wind::vs::Vector > VecSlice
Definition actuator_types.H:66
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE std::iterator_traits< C2 >::value_type linear(const C1 xbegin, const C1 xend, const C2 yinp, const typename std::iterator_traits< C1 >::value_type &xout, const int ncomp=1, const int comp=0)
Definition linear_interpolation.H:130
Slice< T > slice(std::vector< T > &vec, const size_t start, const size_t count)
Definition Slice.H:71
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE constexpr amrex::Real two_pi()
Return .
Definition trig_ops.H:19
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real degrees(const amrex::Real rad_val)
Convert from radians to degrees.
Definition trig_ops.H:39
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real radians(const amrex::Real deg_val)
Convert from degrees to radians.
Definition trig_ops.H:32
Definition actuator_types.H:77
Definition actuator_types.H:147
int id
Unique integer identifier for the turbine.
Definition actuator_types.H:159
bool is_root_proc
Flag indicating whether this is root proc.
Definition actuator_types.H:165
Definition actuator_types.H:56
RealList table_velocity
Definition ActuatorDisk.H:32
Definition Joukowsky.H:14
RealList angular_velocity
Definition Joukowsky.H:15
amrex::Real current_angular_velocity
Definition Joukowsky.H:20
RealList tip_correction
Definition Joukowsky.H:16
Definition Joukowsky.H:50
ActDataHolder< Joukowsky > DataType
Definition Joukowsky.H:54
void operator()(Joukowsky::DataType &data)
Definition Joukowsky_ops.H:143
Definition actuator_ops.H:61
void operator()(typename Joukowsky::DataType &data)
Definition Joukowsky_ops.H:89
Definition actuator_ops.H:32
int m_out_freq
Output frequency (specified in input file)
Definition Joukowsky_ops.H:356
void operator()(Joukowsky::DataType &)
Definition Joukowsky_ops.H:364
void read_io_options(const utils::ActParser &pp)
Definition Joukowsky_ops.H:365
ProcessOutputsOp(const Joukowsky::DataType &data)
Definition Joukowsky_ops.H:360
const Joukowsky::DataType & m_data
Definition Joukowsky_ops.H:348
void prepare_outputs(const std::string &out_dir)
Definition Joukowsky_ops.H:369
std::string m_out_dir
Path to the output directory (specified by Actuator physics class)
Definition Joukowsky_ops.H:350
std::string m_nc_filename
NetCDF output filename for this turbine.
Definition Joukowsky_ops.H:353
Definition actuator_ops.H:71
void operator()(Joukowsky::DataType &data, const utils::ActParser &pp)
Definition Joukowsky_ops.H:45
Definition actuator_ops.H:19
void operator()(typename Joukowsky::DataType &data)
Definition Joukowsky_ops.H:56
Definition actuator_ops.H:43
Definition vstraits.H:10