FhSim  3.1.0
Marine systems simulation
Loading...
Searching...
No Matches
ScreenMF2022.h
1#pragma once
2
17#include "PanelFormulas.h"
18#include "PanelLoadTypes.h"
19#include "TwineFriction.h"
20
21#include <cmath>
22
23namespace hydrodynamics
24{
25
75{
77 static constexpr bool acceptsAtPanel = false;
78
79 static constexpr double kMinValidSolidity = 0.18;
80 static constexpr double kMaxValidSolidity = 0.36;
81
87 static constexpr bool InValidSolidityRange(double solidity)
88 {
89 return kMinValidSolidity <= solidity && solidity <= kMaxValidSolidity;
90 }
91};
92
93namespace screen_mf2022
94{
95
97constexpr double kMaxSolidity = 0.5;
98
108constexpr double kMinSolidity = 0.10050014863866528;
109
111constexpr double kSpeedEpsilon = 1e-9;
112
113constexpr unsigned kFlagSolidityClamped = 1u;
114constexpr unsigned kFlagSpeedBelowEpsilon = 4u;
115constexpr unsigned kFlagVelocityRatioClamped = kPanelFlagVelocityRatioClamped;
116
118template<class T>
119T DragCoefficientNormal(const T& solidity)
120{
121 return 1.782 * solidity * solidity + 1.057 * solidity - 0.053;
122}
123
125template<class T>
126T DragCoefficient45(const T& solidity)
127{
128 return 1.165 * solidity - 0.0919;
129}
130
132template<class T>
133T LiftCoefficient45(const T& solidity)
134{
135 return 1.693 * solidity * solidity - 0.217 * solidity + 0.022;
136}
137
138using panel_formulas::VelocityReductionFactor;
139
149template<class T>
150T DragCoefficient(const T& cosTheta, const T& solidity)
151{
152 const T cd0 = DragCoefficientNormal(solidity);
153 const T cd45 = DragCoefficient45(solidity);
154 const T harmonic3 = 0.5 * (cd0 - std::sqrt(2.0) * cd45); // CD0 a3
155 const T harmonic1 = cd0 - harmonic3; // CD0 (1 − a3)
156 const T cosTripleArg = 4.0 * cosTheta * cosTheta * cosTheta - 3.0 * cosTheta;
157 return harmonic1 * cosTheta + harmonic3 * cosTripleArg;
158}
159
161template<class T>
162T ClampedSolidity(const T& solidity, unsigned& flags)
163{
164 if (solidity > kMaxSolidity) {
165 flags |= kFlagSolidityClamped;
166 return T(kMaxSolidity);
167 }
168 if (!(solidity >= kMinSolidity)) { // catches solidity < kMinSolidity and a NaN solidity (MARE-0145), as TwineCrossFlow's own floor does
169 flags |= kFlagSolidityClamped;
170 return T(kMinSolidity);
171 }
172 return solidity;
173}
174
176template<class T>
177T SinDoubleAngle(const T& cosTheta)
178{
179 using std::sqrt;
180 const T sinSquared = 1.0 - cosTheta * cosTheta;
181 if (sinSquared > 0.0) {
182 return 2.0 * cosTheta * sqrt(sinSquared);
183 }
184 return T(0.0);
185}
186
188template<class T>
189T AngleFromCosine(const T& cosTheta)
190{
191 using std::acos;
192 if (cosTheta < 1.0) {
193 return acos(cosTheta);
194 }
195 return T(0.0);
196}
197
198} // namespace screen_mf2022
199
216template<class T>
217PanelLoad<T> Evaluate([[maybe_unused]] const ScreenMF2022& law, const PanelGeometry<T>& panel,
218 const Netting<T>& netting, const Fluid& fluid, const FlowSample<T>& flow)
219{
220 using std::abs;
221 using std::sqrt;
222 namespace mf = screen_mf2022;
223
224 PanelLoad<T> load {};
225 load.flags = 0;
226
227 const T* u = flow.relativeVelocity;
228 const T* n = panel.normal;
229 const T speedSq = u[0] * u[0] + u[1] * u[1] + u[2] * u[2];
230 const T solidity = mf::ClampedSolidity(netting.solidity, load.flags);
231
232 T speed = T(mf::kSpeedEpsilon);
233 if (speedSq > mf::kSpeedEpsilon * mf::kSpeedEpsilon) {
234 speed = sqrt(speedSq);
235 } else {
236 load.flags |= mf::kFlagSpeedBelowEpsilon;
237 }
238
239 const T normalFlow = u[0] * n[0] + u[1] * n[1] + u[2] * n[2];
240 const T cosSigned = normalFlow / speed; // c = Û·n̂, either sign
241 const T cosTheta = abs(cosSigned);
242
243 const T dragCoefficient = mf::DragCoefficient(cosTheta, solidity);
244 const T lift45 = mf::LiftCoefficient45(solidity);
245 const T pressureArea = 0.5 * fluid.rho * speedSq * panel.area; // ½ ρ |U|² A
246
247 // F = ½ρA|U|² (C_D Û + 2 CL45 (c n̂ − c² Û)).
248 const T alongFlow = pressureArea * (dragCoefficient - 2.0 * lift45 * cosSigned * cosSigned) / speed;
249 const T alongNormal = pressureArea * 2.0 * lift45 * cosSigned;
250 for (int i = 0; i < 3; ++i) {
251 load.force[i] = alongFlow * u[i] + alongNormal * n[i];
252 }
253
254 const twine_friction::InPlaneFriction<T> friction = twine_friction::EvaluateTwineFriction(netting, fluid, solidity, u, n);
255 for (int i = 0; i < 3; ++i) {
256 load.force[i] = load.force[i] + friction.force[i];
257 }
258 const T frictionDragPerQ = friction.drag / (0.5 * fluid.rho * speed * speed);
259 const T totalDragCoefficient = dragCoefficient + frictionDragPerQ / panel.area;
260
261 const T r = mf::VelocityReductionFactor(solidity, load.flags);
262 const T openRatio = 1.0 - solidity;
263
264 load.drag = pressureArea * dragCoefficient + friction.drag;
265 load.lift = pressureArea * lift45 * mf::SinDoubleAngle(cosTheta) + friction.lift;
266 load.theta = mf::AngleFromCosine(cosTheta);
267 load.reynolds = speed * netting.twineThickness / fluid.nu;
268 load.momentumDeficit = dragCoefficient * panel.area + frictionDragPerQ;
269 load.throughFlowSpeed = speed * (1.0 + r) / (2.0 * openRatio);
270 load.localCd = totalDragCoefficient * 4.0 * openRatio * openRatio / (solidity * (1.0 + r) * (1.0 + r));
271 return load;
272}
273
274} // namespace hydrodynamics
Definition ScreenMF2022.h:75
static constexpr double kMaxValidSolidity
Upper end of the Eq. 10 fit, MF2022 p. 5.
Definition ScreenMF2022.h:80
static constexpr bool acceptsAtPanel
MF2022's coefficients include the panel's own induction.
Definition ScreenMF2022.h:77
static constexpr bool InValidSolidityRange(double solidity)
Definition ScreenMF2022.h:87
static constexpr double kMinValidSolidity
Lower end of the Eq. 10 fit, MF2022 p. 5.
Definition ScreenMF2022.h:79