FhSim  3.1.0
Marine systems simulation
Loading...
Searching...
No Matches
TwineCrossFlow.h
1#pragma once
2
65#include "PanelFormulas.h"
66#include "PanelLoadTypes.h"
67
68#include <sfh/constants.h>
69#include <sfh/math/math.h>
70
71#include <cmath>
72
73namespace hydrodynamics
74{
75
80{
81 double ct = 0.01;
82 double cnKnots = 0.4;
83};
84
85namespace twine_cross_flow_detail
86{
87
89const double kMinSpeed = 1.0e-10;
91const double kMaxSpeed = 1.0e10;
93const double kMaxDrag = 1.0e100;
95const double kMinSolidity = 1.0e-4;
97const double kMaxSolidity = 0.5;
99const double kMinLiftDirectionNorm = 1.0e-10;
100
102const double kMinReynolds = 32.0;
104const double kMaxReynolds = 1.0e4;
105
106const unsigned kFlagSolidityClamped = 1u << 0;
107const unsigned kFlagReynoldsClamped = 1u << 1;
108const unsigned kFlagSpeedBelowEpsilon = 1u << 2;
109const unsigned kFlagVelocityRatioClamped = kPanelFlagVelocityRatioClamped;
110
111template<class T>
112T Dot(const T a[3], const T b[3])
113{
114 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
115}
116
117using panel_formulas::CylinderDragCoefficient;
118
125template<class T>
126T PolynomialReynoldsNumber(const T& reynoldsNumber, unsigned& flags)
127{
128 if (reynoldsNumber < kMinReynolds) {
129 flags |= kFlagReynoldsClamped;
130 return T(kMinReynolds);
131 }
132 if (reynoldsNumber > kMaxReynolds) {
133 flags |= kFlagReynoldsClamped;
134 return T(kMaxReynolds);
135 }
136 return reynoldsNumber;
137}
138
149template<class T>
150void BarFamilyDrag(const TwineCrossFlow& law, double dragFactor, const T barDirection[3],
151 const T effVel[3], const T& effSpeed, const T& cylinderCd, T force[3])
152{
153 const T alongBar = Dot(effVel, barDirection);
154 T tangentialVel[3];
155 T normalVel[3];
156 for (int i = 0; i < 3; i++) {
157 tangentialVel[i] = alongBar * barDirection[i];
158 normalVel[i] = effVel[i] - tangentialVel[i];
159 }
160 const T normalSpeed = sfh::math::Max(T(kMinSpeed), sfh::math::Norm(normalVel, 3));
161
162 // The angle of attack between the effective velocity and the bars.
163 T sinAngle = normalSpeed / effSpeed;
164 if (sinAngle > 1) {
165 sinAngle = T(1);
166 }
167
168 const T cn = cylinderCd * sinAngle;
169
170 const T normalDrag = sfh::math::Bound(T(dragFactor * effSpeed * cn), T(-kMaxDrag), T(kMaxDrag));
171 const T tangentialDrag =
172 sfh::math::Bound(T(dragFactor * effSpeed * (law.ct * sfh::pi)), T(-kMaxDrag), T(kMaxDrag));
173
174 for (int i = 0; i < 3; i++) {
175 force[i] = normalDrag * normalVel[i] + tangentialDrag * tangentialVel[i];
176 }
177}
178
180template<class T>
181PanelLoad<T> ZeroLoad(unsigned flags)
182{
183 PanelLoad<T> load {};
184 for (int i = 0; i < 3; i++) {
185 load.force[i] = T(0);
186 }
187 load.drag = T(0);
188 load.lift = T(0);
189 load.theta = T(0);
190 load.reynolds = T(0);
191 load.momentumDeficit = T(0);
192 load.throughFlowSpeed = T(0);
193 load.localCd = T(0);
194 load.flags = flags;
195 return load;
196}
197
198} // namespace twine_cross_flow_detail
199
223template<class T>
224PanelLoad<T> Evaluate(const TwineCrossFlow& law, const PanelGeometry<T>& geometry,
225 const Netting<T>& netting, const Fluid& fluid, const FlowSample<T>& flow)
226{
227 using namespace twine_cross_flow_detail;
228 using std::acos;
229 using std::sqrt;
230
231 const T* const velocity = flow.relativeVelocity;
232 const T* const normal = geometry.normal;
233
234 unsigned flags = 0;
235 const T speed = sfh::math::Norm(velocity, 3);
236 if (speed < kMinSpeed) {
237 flags |= kFlagSpeedBelowEpsilon;
238 }
239
240 T solidity = netting.solidity;
241 if (!(solidity > kMinSolidity)) { // Also catches a NaN.
242 solidity = T(kMinSolidity);
243 flags |= kFlagSolidityClamped;
244 } else if (solidity > kMaxSolidity) {
245 solidity = T(kMaxSolidity);
246 flags |= kFlagSolidityClamped;
247 }
248
249 if (!netting.hasBarDirections) {
250 return ZeroLoad<T>(flags);
251 }
252
253 // The speed-up of Kristiansen and Faltinsen (2012), on the normal component only.
254 const T speedUp = sqrt(2 - solidity) / (sqrt(2.0) * (1 - solidity));
255 const T normalSpeed = Dot(velocity, normal);
256 T effVel[3];
257 for (int i = 0; i < 3; i++) {
258 effVel[i] = velocity[i] + (speedUp - 1) * normalSpeed * normal[i];
259 }
260 const T effSpeed = sfh::math::Bound(sfh::math::Norm(effVel, 3), T(kMinSpeed), T(kMaxSpeed));
261
262 // Reynolds number from the flow through the meshes and the twine diameter (MARE-0085).
263 const T meshFlowSpeed = effSpeed * sqrt(2.0) / sqrt(2 - solidity);
264 const T reynoldsNumber = meshFlowSpeed * netting.twineThickness / fluid.nu;
265
266 const double twineLength = sfh::math::Max(netting.barLength - netting.knotDiameter, 0.0);
267 const double barDragFactor = 0.5 * fluid.rho * netting.twineThickness * twineLength;
268 const double dragFactorU = barDragFactor * netting.numBarsU;
269 const double dragFactorV = barDragFactor * netting.numBarsV;
270
271 T forceU[3];
272 T forceV[3];
273 const T cylinderCd = CylinderDragCoefficient(PolynomialReynoldsNumber(reynoldsNumber, flags));
274 BarFamilyDrag(law, dragFactorU, netting.barU, effVel, effSpeed, cylinderCd, forceU);
275 BarFamilyDrag(law, dragFactorV, netting.barV, effVel, effSpeed, cylinderCd, forceV);
276
277 // Drag on the knots acts along the effective velocity, with no tangential component.
278 const double knotArea = sfh::pi * netting.knotDiameter * netting.knotDiameter / 4;
279 const double knotDragFactor = 0.5 * fluid.rho * netting.numKnots * law.cnKnots * knotArea;
280 const T knotDrag = knotDragFactor * effSpeed * effSpeed;
281
282 PanelLoad<T> load {};
283 for (int i = 0; i < 3; i++) {
284 load.force[i] = forceU[i] + forceV[i] + knotDrag * effVel[i] / effSpeed;
285 }
286
287 // Drag and lift directions, and the angle between U and the normal folded into [0, pi/2].
288 const T boundedSpeed = sfh::math::Max(speed, T(kMinSpeed));
289 T flowDirection[3];
290 for (int i = 0; i < 3; i++) {
291 flowDirection[i] = velocity[i] / boundedSpeed;
292 }
293 T cosTheta = Dot(flowDirection, normal);
294 T normalDownstream[3];
295 for (int i = 0; i < 3; i++) {
296 normalDownstream[i] = (cosTheta < 0) ? -normal[i] : normal[i];
297 }
298 if (cosTheta < 0) {
299 cosTheta = -cosTheta;
300 }
301 // (U-hat x n) x U-hat = n - (n . U-hat) U-hat, for a unit U-hat.
302 T liftDirection[3];
303 for (int i = 0; i < 3; i++) {
304 liftDirection[i] = normalDownstream[i] - cosTheta * flowDirection[i];
305 }
306 const T liftDirectionNorm =
307 sfh::math::Max(sfh::math::Norm(liftDirection, 3), T(kMinLiftDirectionNorm));
308
309 load.drag = Dot(load.force, flowDirection);
310 load.lift = Dot(load.force, liftDirection) / liftDirectionNorm;
311 load.theta = acos(sfh::math::Min(cosTheta, T(1)));
312
313 const T dynamicPressure = 0.5 * fluid.rho * boundedSpeed * boundedSpeed;
314 load.reynolds = reynoldsNumber;
315 load.throughFlowSpeed = meshFlowSpeed;
316 load.momentumDeficit = load.drag / dynamicPressure;
317
318 // MF2022 Eq. 9, with the velocity ratio r = min(1.08 - 0.97 Sn, 1) of MF2022 Eq. 11 (MARE-0109).
319 const T dragCoefficient = load.momentumDeficit / geometry.area;
320 const T reductionFactor = panel_formulas::VelocityReductionFactor(solidity, flags);
321 load.localCd = dragCoefficient * 4 * (1 - solidity) * (1 - solidity) / (solidity * (1 + reductionFactor) * (1 + reductionFactor));
322 load.flags = flags;
323 return load;
324}
325
326} // namespace hydrodynamics
Definition TwineCrossFlow.h:80
double ct
Nominal tangential drag coefficient; multiplied by pi.
Definition TwineCrossFlow.h:81
double cnKnots
Knot drag coefficient on the knot's projected area.
Definition TwineCrossFlow.h:82