FhSim  3.1.0
Marine systems simulation
Loading...
Searching...
No Matches
NetElement3NForces.h
1#pragma once
2
25#include <fhsim_environment/hydrodynamics/PanelClampLog.h>
26#include <fhsim_environment/hydrodynamics/PanelLoadLaw.h>
27
28#include <sfh/linalg/matrix_ops.h>
29#include <sfh/math/array.h>
30#include <sfh/math/math.h>
31#include <sfh/sim/kinematics.h>
32#include <sfh/util/diagnostic.h>
33
34#include <cmath>
35#include <iostream>
36#include <type_traits>
37
38namespace net_element_forces
39{
40
42const double kMinMeshBarLength = 1.0e-10;
43
46const hydrodynamics::FlowReference kFlowReference = hydrodynamics::FlowReference::FreeStream;
47
48
59{
60 double nodesDistAB_mesh[2];
61 double nodesDistAC_mesh[2];
62 double nodesDistBC_mesh[2];
63 double meshDet;
64 double barL0;
65 double eATwine;
66 double barD;
67 double knotDiameter;
68 double numBarsU;
69 double numBarsV;
70 double numKnots;
71 double NodeWeight;
72 double dampingCoeff;
75 bool twineStructure = true;
76};
77
78
84template<class T>
93
94
105template<class T>
106void CalcLocalUVComponents(const PanelParams& params, const T distAB_panel[3],
107 const T distAC_panel[3], T meshBarComp[6], T meshBarLength[2])
108{
109 using std::sqrt;
110
111 meshBarComp[0] = (params.nodesDistAC_mesh[1] * distAB_panel[0] - params.nodesDistAB_mesh[1] * distAC_panel[0]) / params.meshDet;
112 meshBarComp[1] = (params.nodesDistAC_mesh[1] * distAB_panel[1] - params.nodesDistAB_mesh[1] * distAC_panel[1]) / params.meshDet;
113 meshBarComp[2] = T(0.0);
114 meshBarComp[3] = (params.nodesDistAB_mesh[0] * distAC_panel[0] - params.nodesDistAC_mesh[0] * distAB_panel[0]) / params.meshDet;
115 meshBarComp[4] = (params.nodesDistAB_mesh[0] * distAC_panel[1] - params.nodesDistAC_mesh[0] * distAB_panel[1]) / params.meshDet;
116 meshBarComp[5] = T(0.0);
117
118 meshBarLength[0] = sfh::math::Max(
119 sqrt(meshBarComp[0] * meshBarComp[0] + meshBarComp[1] * meshBarComp[1]),
120 T(kMinMeshBarLength));
121 meshBarLength[1] = sfh::math::Max(
122 sqrt(meshBarComp[3] * meshBarComp[3] + meshBarComp[4] * meshBarComp[4]),
123 T(kMinMeshBarLength));
124}
125
126
136template<class T>
137PanelFrame<T> ComputePanelFrame(const PanelParams& params, const T* const posA_ned,
138 const T* const posB_ned, const T* const posC_ned)
139{
140 using std::acos;
141 using std::sqrt;
142
143 PanelFrame<T> frame;
144 sfh::sim::RMatrixFromThreePoints(posA_ned, posB_ned, posC_ned, frame.R_ned2panel);
145
146 // The panel-frame separations. They are held as 3-vectors although the third component
147 // is always zero, so that the standard rotation and array routines apply.
148 T distAB_ned[3], distAC_ned[3];
149 T distAB_panel[3], distAC_panel[3];
150 sfh::math::ArraySubtract(posB_ned, posA_ned, distAB_ned, 3ul);
151 sfh::math::ArraySubtract(posC_ned, posA_ned, distAC_ned, 3ul);
152 sfh::sim::RotateToLocal(distAB_ned, frame.R_ned2panel, distAB_panel);
153 sfh::sim::RotateToLocal(distAC_ned, frame.R_ned2panel, distAC_panel);
154
155 CalcLocalUVComponents(params, distAB_panel, distAC_panel, frame.meshBarComp,
156 frame.meshBarLength);
157
158 // The mesh half opening angle, from the scalar product of the u- and v-bar vectors
159 // (Priour, 2001, p. 232). The positive mesh directions must be defined so that the half
160 // angle is the one between bars that would naturally tend to close.
161 T normBarCompU = T(0);
162 T normBarCompV = T(0);
163 for (int i = 0; i < 3; i++) {
164 normBarCompU += frame.meshBarComp[i] * frame.meshBarComp[i];
165 normBarCompV += frame.meshBarComp[i + 3] * frame.meshBarComp[i + 3];
166 }
167 normBarCompU = sqrt(normBarCompU);
168 normBarCompV = sqrt(normBarCompV);
169
170 frame.meshOpeningAngle = 0.5 * acos(sfh::linalg::Dot(frame.meshBarComp, frame.meshBarComp + 3, 2ul) / (normBarCompU * normBarCompV));
171 if (!(frame.meshOpeningAngle == frame.meshOpeningAngle)) frame.meshOpeningAngle = T(0);
172
173 // In the panel frame AB lies along x and the element in the xy-plane.
174 frame.area = 0.5 * sfh::math::Abs(distAB_panel[0] * distAC_panel[1] - distAB_panel[1] * distAC_panel[0]);
175 return frame;
176}
177
178
184template<class T>
185void CalcTensionForces(const PanelParams& params, const T meshBarComp[6],
186 const T meshBarLength[2], T nodeATension_panel[3], T nodeBTension_panel[3],
187 T nodeCTension_panel[3])
188{
189 // The original multiplied by the `bool` of this comparison. That promotes to 1.0 or 0.0
190 // for a built-in type but discards the derivative of a differentiable one, so the branch
191 // is written out. The retained branch is the original expression unchanged, and
192 // multiplying it by an exact 1.0 was exact, so the `double` value is unaffected.
193 T twineTension[2];
194 twineTension[0] = (meshBarLength[0] > params.barL0)
195 ? params.eATwine * (meshBarLength[0] - params.barL0) / params.barL0
196 : T(0.0);
197 twineTension[1] = (meshBarLength[1] > params.barL0)
198 ? params.eATwine * (meshBarLength[1] - params.barL0) / params.barL0
199 : T(0.0);
200 twineTension[0] = sfh::math::Max(twineTension[0], T(0.0));
201 twineTension[1] = sfh::math::Max(twineTension[1], T(0.0));
202
203 nodeATension_panel[0] = params.nodesDistBC_mesh[1] * twineTension[0] * meshBarComp[0] / (2 * meshBarLength[0]) - params.nodesDistBC_mesh[0] * twineTension[1] * meshBarComp[3] / (2 * meshBarLength[1]);
204 nodeATension_panel[1] = params.nodesDistBC_mesh[1] * twineTension[0] * meshBarComp[1] / (2 * meshBarLength[0]) - params.nodesDistBC_mesh[0] * twineTension[1] * meshBarComp[4] / (2 * meshBarLength[1]);
205 nodeATension_panel[2] = T(0.0);
206
207 nodeBTension_panel[0] = -params.nodesDistAC_mesh[1] * twineTension[0] * meshBarComp[0] / (2 * meshBarLength[0]) + params.nodesDistAC_mesh[0] * twineTension[1] * meshBarComp[3] / (2 * meshBarLength[1]);
208 nodeBTension_panel[1] = -params.nodesDistAC_mesh[1] * twineTension[0] * meshBarComp[1] / (2 * meshBarLength[0]) + params.nodesDistAC_mesh[0] * twineTension[1] * meshBarComp[4] / (2 * meshBarLength[1]);
209 nodeBTension_panel[2] = T(0.0);
210
211 nodeCTension_panel[0] = params.nodesDistAB_mesh[1] * twineTension[0] * meshBarComp[0] / (2 * meshBarLength[0]) - params.nodesDistAB_mesh[0] * twineTension[1] * meshBarComp[3] / (2 * meshBarLength[1]);
212 nodeCTension_panel[1] = params.nodesDistAB_mesh[1] * twineTension[0] * meshBarComp[1] / (2 * meshBarLength[0]) - params.nodesDistAB_mesh[0] * twineTension[1] * meshBarComp[4] / (2 * meshBarLength[1]);
213 nodeCTension_panel[2] = T(0.0);
214}
215
216
233template<class T>
234hydrodynamics::PanelLoad<T> EvaluatePanelLoad(const PanelParams& params, const PanelFrame<T>& frame,
235 const hydrodynamics::PanelLoadLaw& law, const hydrodynamics::Fluid& fluid, const T& solidity,
236 const T elementVel_ned[3], const T* const waterVel_ned)
237{
238 hydrodynamics::PanelGeometry<T> geometry;
239 geometry.area = frame.area;
240
241 hydrodynamics::Netting<T> netting;
242 netting.solidity = solidity;
243 netting.twineThickness = params.barD;
244 netting.hasBarDirections = params.meshDet != 0;
245 netting.barLength = params.barL0;
246 netting.knotDiameter = params.knotDiameter;
247 netting.numBarsU = params.numBarsU;
248 netting.numBarsV = params.numBarsV;
249 netting.numKnots = params.numKnots;
250
251 hydrodynamics::FlowSample<T> flow;
252 flow.reference = kFlowReference;
253
254 for (int i = 0; i < 3; i++) {
255 geometry.normal[i] = frame.R_ned2panel[i][2];
256 flow.relativeVelocity[i] = waterVel_ned[i] - elementVel_ned[i];
257
258 // The bars lie in the panel's xy-plane; rotate them to the global frame and normalise.
259 const T barU = frame.R_ned2panel[i][0] * frame.meshBarComp[0] + frame.R_ned2panel[i][1] * frame.meshBarComp[1];
260 const T barV = frame.R_ned2panel[i][0] * frame.meshBarComp[3] + frame.R_ned2panel[i][1] * frame.meshBarComp[4];
261 netting.barU[i] = barU / frame.meshBarLength[0];
262 netting.barV[i] = barV / frame.meshBarLength[1];
263 }
264
265 return hydrodynamics::Evaluate(law, geometry, netting, fluid, flow);
266}
267
268
270template<class T>
271void CalcDampingForces(const PanelParams& params, const T elementVel_ned[3],
272 const T* const velA_ned, const T* const velB_ned, const T* const velC_ned,
273 T dampingForcesNodeA_ned[3], T dampingForcesNodeB_ned[3],
274 T dampingForcesNodeC_ned[3], double addedLinearDrag)
275{
276 for (unsigned short i = 0; i < 3; i++) {
277 dampingForcesNodeA_ned[i] = params.dampingCoeff * (elementVel_ned[i] - velA_ned[i]);
278 dampingForcesNodeB_ned[i] = params.dampingCoeff * (elementVel_ned[i] - velB_ned[i]);
279 dampingForcesNodeC_ned[i] = params.dampingCoeff * (elementVel_ned[i] - velC_ned[i]);
280 }
281
282 if (addedLinearDrag > 0) {
283 for (unsigned short i = 0; i < 3; i++) {
284 dampingForcesNodeA_ned[i] -= addedLinearDrag / 3 * velA_ned[i];
285 dampingForcesNodeB_ned[i] -= addedLinearDrag / 3 * velB_ned[i];
286 dampingForcesNodeC_ned[i] -= addedLinearDrag / 3 * velC_ned[i];
287 }
288 }
289}
290
291
312template<class T>
313void AddNodeForces(const PanelParams& params, const PanelFrame<T>& frame,
314 const hydrodynamics::PanelLoadLaw& law, const hydrodynamics::Fluid& fluid, const T& solidity,
315 const T* const velA_ned, const T* const velB_ned, const T* const velC_ned,
316 const T* const waterVel_ned, T* const nodeAforce_ned, T* const nodeBforce_ned,
317 T* const nodeCforce_ned, double addedLinearDrag = 0.0,
318 const hydrodynamics::PanelClampLog* clampLog = nullptr)
319{
320 T nodeATension_panel[3] = {T(0), T(0), T(0)};
321 T nodeBTension_panel[3] = {T(0), T(0), T(0)};
322 T nodeCTension_panel[3] = {T(0), T(0), T(0)};
323 if (params.twineStructure)
324 CalcTensionForces(params, frame.meshBarComp, frame.meshBarLength, nodeATension_panel,
325 nodeBTension_panel, nodeCTension_panel);
326
327 T elementVel_ned[3];
328 for (int i = 0; i < 3; i++) {
329 elementVel_ned[i] = (velA_ned[i] + velB_ned[i] + velC_ned[i]) / 3.0;
330 }
331
332 const hydrodynamics::PanelLoad<T> load =
333 EvaluatePanelLoad(params, frame, law, fluid, solidity, elementVel_ned, waterVel_ned);
334 if (clampLog)
335 clampLog->Note(load.flags);
336
337 T dampingForcesNodeA_ned[3] = {T(0), T(0), T(0)};
338 T dampingForcesNodeB_ned[3] = {T(0), T(0), T(0)};
339 T dampingForcesNodeC_ned[3] = {T(0), T(0), T(0)};
340 if (params.twineStructure) {
341 CalcDampingForces(params, elementVel_ned, velA_ned, velB_ned, velC_ned,
342 dampingForcesNodeA_ned, dampingForcesNodeB_ned, dampingForcesNodeC_ned,
343 addedLinearDrag);
344
345 // Local z-components of the tension are zero, the element being plane.
346 sfh::sim::RotateFromLocalAndAdd(nodeATension_panel, frame.R_ned2panel, nodeAforce_ned);
347 sfh::sim::RotateFromLocalAndAdd(nodeBTension_panel, frame.R_ned2panel, nodeBforce_ned);
348 sfh::sim::RotateFromLocalAndAdd(nodeCTension_panel, frame.R_ned2panel, nodeCforce_ned);
349 }
350
351 // The hydrodynamic force is shared equally between the three nodes.
352 for (int i = 0; i < 3; i++) {
353 const T hydroForcePerNode = load.force[i] / 3.0;
354 nodeAforce_ned[i] += hydroForcePerNode;
355 nodeBforce_ned[i] += hydroForcePerNode;
356 nodeCforce_ned[i] += hydroForcePerNode;
357 }
358
359 // The weight in water of the netting, distributed equally between the three nodes.
360 nodeAforce_ned[2] += params.NodeWeight;
361 nodeBforce_ned[2] += params.NodeWeight;
362 nodeCforce_ned[2] += params.NodeWeight;
363
364 if (params.twineStructure) {
365 for (unsigned short i = 0; i < 3; i++) {
366 nodeAforce_ned[i] += dampingForcesNodeA_ned[i];
367 nodeBforce_ned[i] += dampingForcesNodeB_ned[i];
368 nodeCforce_ned[i] += dampingForcesNodeC_ned[i];
369 }
370 } else if (addedLinearDrag > 0) {
371 // Without twine structure only the added linear drag of CalcDampingForces remains.
372 for (unsigned short i = 0; i < 3; i++) {
373 nodeAforce_ned[i] -= addedLinearDrag / 3 * velA_ned[i];
374 nodeBforce_ned[i] -= addedLinearDrag / 3 * velB_ned[i];
375 nodeCforce_ned[i] -= addedLinearDrag / 3 * velC_ned[i];
376 }
377 }
378
379 // The diagnostic below reads raw `double` arrays, so it applies only to the `double`
380 // instantiation.
381 if constexpr (std::is_same_v<T, double>) {
382 unsigned long bad;
383 if ((bad = sfh::util::FindBadValues(nodeAforce_ned, 3)) != (unsigned long)-1)
384 std::cout << "Bad element in nodeAforce_ned: " << bad << std::endl;
385 if ((bad = sfh::util::FindBadValues(nodeBforce_ned, 3)) != (unsigned long)-1)
386 std::cout << "Bad element in nodeBforce_ned: " << bad << std::endl;
387 if ((bad = sfh::util::FindBadValues(nodeCforce_ned, 3)) != (unsigned long)-1)
388 std::cout << "Bad element in nodeCforce_ned: " << bad << std::endl;
389 }
390}
391
392} // namespace net_element_forces
Definition NetElement3NForces.h:86
T meshBarComp[6]
The u-bar in [0..2] and the v-bar in [3..5], panel frame.
Definition NetElement3NForces.h:88
T meshOpeningAngle
The mesh half opening angle, rad; 0 when undefined.
Definition NetElement3NForces.h:90
T R_ned2panel[3][3]
Columns: the panel x, y and z axes in the global frame.
Definition NetElement3NForces.h:87
T area
The element's area, m^2.
Definition NetElement3NForces.h:91
T meshBarLength[2]
The length of a u-bar and of a v-bar, bounded away from zero.
Definition NetElement3NForces.h:89
Definition NetElement3NForces.h:59
double meshDet
Mesh determinant, Priour (2005, Eq. 7); 0 for collinear corners.
Definition NetElement3NForces.h:63
double nodesDistBC_mesh[2]
Distance from node B to C in mesh (u,v) coordinates.
Definition NetElement3NForces.h:62
bool twineStructure
Definition NetElement3NForces.h:75
double knotDiameter
Diameter of the knots.
Definition NetElement3NForces.h:67
double eATwine
E-modulus times twine cross-sectional area.
Definition NetElement3NForces.h:65
double numKnots
Number of knots in the element, its number of meshes.
Definition NetElement3NForces.h:70
double nodesDistAB_mesh[2]
Distance from node A to B in mesh (u,v) coordinates.
Definition NetElement3NForces.h:60
double barD
Diameter of the twines.
Definition NetElement3NForces.h:66
double dampingCoeff
Definition NetElement3NForces.h:72
double NodeWeight
Weight in water carried by one node.
Definition NetElement3NForces.h:71
double barL0
Unstretched length of a mesh bar.
Definition NetElement3NForces.h:64
double numBarsV
Number of v-bars in the element, half its bar count.
Definition NetElement3NForces.h:69
double numBarsU
Number of u-bars in the element, half its bar count.
Definition NetElement3NForces.h:68
double nodesDistAC_mesh[2]
Distance from node A to C in mesh (u,v) coordinates.
Definition NetElement3NForces.h:61