/home/runner/work/amr-wind/amr-wind/amr-wind/wind_energy/actuator/wing/fixed_wing_ops.H Source File

AMR-Wind API: /home/runner/work/amr-wind/amr-wind/amr-wind/wind_energy/actuator/wing/fixed_wing_ops.H Source File
AMR-Wind API v0.1.0
CFD solver for wind plant simulations
Loading...
Searching...
No Matches
fixed_wing_ops.H
Go to the documentation of this file.
1#ifndef FIXED_WING_OPS_H
2#define FIXED_WING_OPS_H
3
4#include <algorithm>
5
10#include "AMReX_REAL.H"
11
12using namespace amrex::literals;
13
15
16template <>
18{
20 {
21 auto& wdata = data.meta();
22 auto& info = data.info();
23 pp.get("num_points", wdata.num_pts);
24 pp.get("start", wdata.start);
25 pp.get("end", wdata.end);
26 pp.query_either("epsilon", wdata.eps_inp);
27 pp.query_either("epsilon_chord", wdata.epsilon_chord);
28 if (!pp.contains("pitch_timetable")) {
29 // Constant pitch is required without timetable
30 pp.get("pitch", wdata.pitch);
31 }
32 pp.get("airfoil_table", wdata.airfoil_file);
33 pp.query("airfoil_type", wdata.airfoil_type);
34 pp.queryarr("span_locs", wdata.span_locs);
35 pp.queryarr("chord", wdata.chord_inp);
36 bool use_fllc = false;
37 pp.query("fllc", use_fllc);
38
39 // If spanwise components of Gaussian should be ignored
40 pp.query("disable_spanwise_gaussian", wdata.gauss_2D);
41 // If spanwise components of Gaussian and forces should be normalized
42 // (on by default, not used unless gauss_2D = true)
43 pp.query("normalize_spanwise", wdata.normalize_2D_spanwise);
44 // If certain components of force should be ignored
45 // (check that correct number of components are specified)
46 amrex::Vector<int> force_coord_flags_query_inp;
47 pp.queryarr("active_force_dirs", force_coord_flags_query_inp);
48 if (!force_coord_flags_query_inp.empty()) {
49 AMREX_ALWAYS_ASSERT(
50 force_coord_flags_query_inp.size() == AMREX_SPACEDIM);
51 wdata.force_coord_flags = force_coord_flags_query_inp;
52 }
53
54 // If velocity magnitude for force calculation should be prescribed by
55 // user, not measured from the flow
56 pp.query("prescribed_uinf", wdata.prescribed_uinf);
57
58 // Initialize tables for pitch actuation (in degrees)
59 pp.query("pitch_timetable", wdata.pitch_timetable_file);
60 if (!wdata.pitch_timetable_file.empty()) {
61 std::ifstream ifh(wdata.pitch_timetable_file, std::ios::in);
62 if (!ifh.good()) {
63 amrex::Abort(
64 "Cannot find input file: " + wdata.pitch_timetable_file);
65 }
66 amrex::Real data_time;
67 amrex::Real data_pitch_deg;
68 ifh.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
69 while (ifh >> data_time) {
70 ifh >> data_pitch_deg;
71 wdata.time_table.push_back(data_time);
72 wdata.pitch_table.push_back(data_pitch_deg);
73 }
74 }
75
76 pp.query("motion_type", wdata.motion_type);
77 {
78 // do nothing for default, "none"
79 if (amrex::toLower(wdata.motion_type) == "linear") {
80 // get velocity
81 pp.get("velocity", wdata.vel_tr);
82 } else if (amrex::toLower(wdata.motion_type) == "sine") {
83 // get parameters
84 pp.get("sine_period", wdata.s_period);
85 pp.get("sine_vector", wdata.s_vector);
86 } else if (amrex::toLower(wdata.motion_type) != "none") {
87 amrex::Abort(
88 "Invalid FixedWingLine motion type. Valid options are "
89 "none, linear, and sine.");
90 }
91 }
92 if (use_fllc) {
93 wdata.fllc = std::make_unique<FLLCData>();
94 fllc_parse(pp, *(wdata.fllc));
95 }
96
97 if (!pp.contains("epsilon") && !pp.contains("epsilon_chord")) {
98 amrex::Abort(
99 "Actuator fixed wing requires specification of one or both "
100 "of 'epsilon' or 'epsilon_chord'");
101 }
102
103 AMREX_ALWAYS_ASSERT(wdata.span_locs.size() == wdata.chord_inp.size());
104
105 amrex::Real max_chord = *std::ranges::max_element(wdata.chord_inp);
106 amrex::Real max_eps = *std::ranges::max_element(wdata.eps_inp);
107 amrex::Real max_epsc = *std::ranges::max_element(wdata.epsilon_chord);
108 amrex::Real search_radius =
109 amrex::max(max_eps, max_epsc) * max_chord * 3.0_rt;
110 const auto& p1 = wdata.start;
111 const auto& p2 = wdata.end;
112 // Bounding box quantities used for all motion types
113 amrex::Real minpx = amrex::min(p1.x(), p2.x());
114 amrex::Real maxpx = amrex::max(p1.x(), p2.x());
115 amrex::Real minpy = amrex::min(p1.y(), p2.y());
116 amrex::Real maxpy = amrex::max(p1.y(), p2.y());
117 amrex::Real minpz = amrex::min(p1.z(), p2.z());
118 amrex::Real maxpz = amrex::max(p1.z(), p2.z());
119 // Bounding box limits for 2D actuator
120 if (wdata.gauss_2D) {
121 // Assume largest component of span needs entire domain, but the
122 // others do not need the bounding box to grow
123 const auto wspan = wdata.end - wdata.start;
124 // Flag for checking periodicity in spanwise direction
125 bool warn_per{false};
126 std::string sdir;
127 auto per = data.sim().mesh().Geom()[0].periodicity();
128 if (std::abs(wspan.x()) >=
129 amrex::max<amrex::Real>(
130 std::abs(wspan.y()), std::abs(wspan.z()))) {
131 minpx = data.sim().mesh().Geom(0).ProbLoArray()[0];
132 maxpx = data.sim().mesh().Geom(0).ProbHiArray()[0];
133 warn_per = !per.isPeriodic(0);
134 sdir = "x";
135 } else if (std::abs(wspan.y()) >= std::abs(wspan.z())) {
136 minpy = data.sim().mesh().Geom(0).ProbLoArray()[1];
137 maxpy = data.sim().mesh().Geom(0).ProbHiArray()[1];
138 warn_per = !per.isPeriodic(1);
139 sdir = "y";
140 } else {
141 minpz = data.sim().mesh().Geom(0).ProbLoArray()[2];
142 maxpz = data.sim().mesh().Geom(0).ProbHiArray()[2];
143 warn_per = !per.isPeriodic(2);
144 sdir = "z";
145 }
146 if (warn_per) {
147 amrex::Print()
148 << "\nWARNING: fixed_wing_ops: Detected spanwise direction "
149 << sdir
150 << " is not periodic, though 2D Gaussian is being "
151 "used.\n\n";
152 }
153 }
154 // Set up bounding box depending on motion type
155 if (amrex::toLower(wdata.motion_type) == "none") {
156 info.bound_box = amrex::RealBox(
157 minpx - search_radius, minpy - search_radius,
158 minpz - search_radius, maxpx + search_radius,
159 maxpy + search_radius, maxpz + search_radius);
160 } else if (amrex::toLower(wdata.motion_type) == "linear") {
161 // Extend bounding box in case of velocity
162 constexpr amrex::Real tiny = std::numeric_limits<float>::epsilon();
163 // If velocity is present, assume wing can travel entire domain
164 const bool up_ = wdata.vel_tr.x() > tiny;
165 const amrex::Real up_ext =
166 data.sim().mesh().Geom(0).ProbHiArray()[0];
167 const bool um_ = wdata.vel_tr.x() < -tiny;
168 const amrex::Real um_ext =
169 data.sim().mesh().Geom(0).ProbLoArray()[0];
170 const bool vp_ = wdata.vel_tr.y() > tiny;
171 const amrex::Real vp_ext =
172 data.sim().mesh().Geom(0).ProbHiArray()[1];
173 const bool vm_ = wdata.vel_tr.y() < -tiny;
174 const amrex::Real vm_ext =
175 data.sim().mesh().Geom(0).ProbLoArray()[1];
176 const bool wp_ = wdata.vel_tr.z() > tiny;
177 const amrex::Real wp_ext =
178 data.sim().mesh().Geom(0).ProbHiArray()[2];
179 const bool wm_ = wdata.vel_tr.z() < -tiny;
180 const amrex::Real wm_ext =
181 data.sim().mesh().Geom(0).ProbLoArray()[2];
182 info.bound_box = amrex::RealBox(
183 um_ ? um_ext : minpx - search_radius,
184 vm_ ? vm_ext : minpy - search_radius,
185 wm_ ? wm_ext : minpz - search_radius,
186 up_ ? up_ext : maxpx + search_radius,
187 vp_ ? vp_ext : maxpy + search_radius,
188 wp_ ? wp_ext : maxpz + search_radius);
189 } else if (amrex::toLower(wdata.motion_type) == "sine") {
190 info.bound_box = amrex::RealBox(
191 minpx - wdata.s_vector.x() - search_radius,
192 minpy - wdata.s_vector.y() - search_radius,
193 minpz - wdata.s_vector.z() - search_radius,
194 maxpx + wdata.s_vector.x() + search_radius,
195 maxpy + wdata.s_vector.y() + search_radius,
196 maxpz + wdata.s_vector.z() + search_radius);
197 }
198 }
199};
200
201template <>
203{
205 {
206 wing::init_data_structures(data.meta(), data.grid());
207
208 auto& meta = data.meta();
209 {
210 const int npts = data.meta().num_pts;
211 auto& grid = data.grid();
212 // Process chord information
213 const auto wlen = vs::mag(grid.pos.back() - grid.pos.front());
214 RealList wx(npts);
215 for (int i = 0; i < npts; ++i) {
216 wx[i] = vs::mag(grid.pos[i] - grid.pos[0]) / wlen;
217 }
218 meta.chord.resize(npts);
220 meta.span_locs, meta.chord_inp, wx, meta.chord);
221 meta.epsilon_chord = {
222 meta.epsilon_chord.x(), meta.epsilon_chord.z(),
223 meta.epsilon_chord.y()};
224 meta.eps_inp = {
225 meta.eps_inp.x(), meta.eps_inp.z(), meta.eps_inp.y()};
226 for (int i = 0; i < npts; ++i) {
227 for (int n = 0; n < AMREX_SPACEDIM; ++n) {
228 const auto eps = meta.epsilon_chord[n] * meta.chord[i];
229 grid.epsilon[i][n] =
230 amrex::max<amrex::Real>(meta.eps_inp[n], eps);
231 }
232 }
233 // Copy Gaussian flags to grid struct if 2D
234 if (meta.gauss_2D) {
235 // Local coords are chord, span, thickness internally
236 grid.dcoord_flags = vs::Vector(1.0_rt, 0.0_rt, 1.0_rt);
237 }
238 }
239
240 meta.aflookup =
241 AirfoilLoader::load_airfoil(meta.airfoil_file, meta.airfoil_type);
242 }
243};
244
245template <>
246AMREX_INLINE const AirfoilTable&
248{
249 return *data.meta().aflookup;
250}
251
252} // namespace amr_wind::actuator::ops
253
254#endif /* FIXED_WING_OPS_H */
amrex::AmrCore & mesh()
Return the AMR mesh hierarchy.
Definition CFDSim.H:61
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
static std::unique_ptr< AirfoilTable > load_airfoil(const std::string &af_file, const std::string &type)
Definition AirfoilTable.cpp:73
Definition AirfoilTable.H:16
void query_either(const std::string &name, vs::Vector &value) const
Definition MultiParser.H:97
void get(const std::string &name, vs::Vector &value) const
Definition MultiParser.H:44
void queryarr(const std::string &name, T &value) const
Query a vector of values for the given keyword entry from either namespace.
Definition MultiParser.H:149
bool contains(const std::string &name) const
Check if the keyword is present in either namespace.
Definition MultiParser.H:34
void query(const std::string &name, vs::Vector &value) const
Definition MultiParser.H:59
Definition ActSrcLineOp.H:12
AMREX_INLINE const AirfoilTable & airfoil_lookup< FixedWing >(FixedWing::DataType &data)
Definition fixed_wing_ops.H:247
::amr_wind::utils::MultiParser ActParser
Definition ActParser.H:8
void init_data_structures(WingBaseData &wdata, ActGrid &grid)
Definition wing_ops.cpp:12
void fllc_parse(const utils::ActParser &pp, FLLCData &data)
Function to capture common parsing requirements for the filtered lifting line correction.
Definition FLLC.cpp:113
amrex::Vector< amrex::Real > RealList
Definition actuator_types.H:63
void linear_monotonic(const C1 &xinp, const C2 &yinp, const C1 &xout, C2 &yout, const int ncomp=1, const int comp=0)
Definition linear_interpolation.H:156
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE T mag(const TensorT< T > &t)
Definition tensorI.H:182
VectorT< amrex::Real > Vector
Definition vector.H:145
Definition actuator_types.H:41
std::unique_ptr< AirfoilTable > aflookup
Definition FixedWing.H:19
Definition FixedWing.H:23
ActDataHolder< FixedWing > DataType
Definition FixedWing.H:27
int num_pts
Number of points along the wing.
Definition ActuatorWing.H:23
void operator()(FixedWing::DataType &data)
Definition fixed_wing_ops.H:204
Definition actuator_ops.H:32
void operator()(FixedWing::DataType &data, const utils::ActParser &pp)
Definition fixed_wing_ops.H:19
Definition actuator_ops.H:19