/home/runner/work/kynema-sgf/kynema-sgf/src/wind_energy/actuator/wing/fixed_wing_ops.H Source File

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