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