From fd4b21279574b8f02ee286e9148987559a07476a Mon Sep 17 00:00:00 2001
From: Thomas Lambert <t.lambert@uliege.be>
Date: Fri, 28 Jul 2023 16:28:02 +0200
Subject: [PATCH] fix(solvers): true Reynolds calculation in all solvers
 (closes #13)

This completes the work started in !4 by implementing the true
Reynolds recalculation for the other three solvers.

Note that special care had to be implemented in Stahlhut solver in order
to ensure proper numerical resolution of the solution.
---
 src/configs/caradonna1981.m |  1 -
 src/solvers/indfact.m       |  4 +++-
 src/solvers/indvel.m        |  3 +--
 src/solvers/leishman.m      |  8 ++++++++
 src/solvers/stahlhut.m      | 39 ++++++++++++++++++++++++++++++++++++-
 5 files changed, 50 insertions(+), 5 deletions(-)

diff --git a/src/configs/caradonna1981.m b/src/configs/caradonna1981.m
index 017a0cb..e96cace 100644
--- a/src/configs/caradonna1981.m
+++ b/src/configs/caradonna1981.m
@@ -49,7 +49,6 @@ Sim.Misc.appli  = 'heli'; % Type of application ('helicopter', 'propeller', 'win
 
 % Solvers
 Mod.solvers = {'all'};   % BEMT Solver ('leishman', 'indfact', 'indvel', 'stahlhut', 'all')
-% Mod.solvers = {'leishman'};   % BEMT Solver ('leishman', 'indfact', 'indvel', 'stahlhut', 'all')
 
 % Extensions/corrections
 Mod.Ext.losses = 'all';  % Include losses using Prandtl formula ('none', 'hub', 'tip', 'both')
diff --git a/src/solvers/indfact.m b/src/solvers/indfact.m
index c5e19dc..54ed6d4 100644
--- a/src/solvers/indfact.m
+++ b/src/solvers/indfact.m
@@ -77,13 +77,14 @@ function indfact(OpRot, Mod)
         % [DEBUG]: SQRT(PI) GIVES BETTER MATCH?
         % relVel = sqrt(sqrt(pi)) * sqrt(v_ax.^2 + v_ang.^2);
         relVel = sqrt(v_ax.^2 + v_ang.^2);
+        reynolds = Flow.reynolds(relVel, OpRot.Rot.Bl.chord, OpRot.Op.Flow.mu, OpRot.Op.Flow.rho);
 
         % Angles
         phi = atan2(v_ax, v_ang);  % Inflow angle
         alpha = OpRot.ElPerf.truePitch - phi;  % Angle of attack
 
         % Get new estimates for the coefficients
-        [cl, cd] = OpRot.ElPerf.getclcd(alpha);
+        [cl, cd] = OpRot.ElPerf.getclcd(alpha, reynolds);
 
         % Calculate loss factor
         lossFact = prandtlloss(OpRot.Rot.nBlades, OpRot.Rot.Bl.r, OpRot.Rot.r0, phi, ...
@@ -133,5 +134,6 @@ function indfact(OpRot, Mod)
     OpRot.ElPerf.indVelTg = b .* OpRot.ElPerf.tgSpeed;
     OpRot.ElPerf.inflAngle = phi;
     OpRot.ElPerf.alpha = alpha;
+    OpRot.ElPerf.reynolds = reynolds;
 
 end
diff --git a/src/solvers/indvel.m b/src/solvers/indvel.m
index 2ac1ac1..2eff70d 100644
--- a/src/solvers/indvel.m
+++ b/src/solvers/indvel.m
@@ -68,8 +68,7 @@ function indvel(OpRot, Mod)
         % Local mass flow rate
         dmdot = 2 * pi * OpRot.Op.Flow.rho * OpRot.Rot.Bl.y * OpRot.Rot.Bl.dy .* v;
 
-        % Compute new estimates for the velocity magnitude and Reynolds
-        % number
+        % Compute new estimates for the velocity magnitude and Reynolds number
         relVel = sqrt(v.^2 + u.^2);
         reynolds = Flow.reynolds(relVel, OpRot.Rot.Bl.chord, OpRot.Op.Flow.mu, OpRot.Op.Flow.rho);
 
diff --git a/src/solvers/leishman.m b/src/solvers/leishman.m
index b783528..0160198 100644
--- a/src/solvers/leishman.m
+++ b/src/solvers/leishman.m
@@ -68,6 +68,13 @@ function leishman(OpRot, Mod)
         while ~converged
             lambda_old = lambda;
 
+            % Update Reynolds and then clSlope w.r.t. the new inflow velocity
+            axialVel = OpRot.Op.speed + lambda * OpRot.tgTipSpeed;
+            relVel = sqrt(axialVel.^2 + OpRot.ElPerf.tgSpeed.^2);
+            reynolds = Flow.reynolds(relVel, ...
+                                     OpRot.Rot.Bl.chord, OpRot.Op.Flow.mu, OpRot.Op.Flow.rho);
+            clSlope = getclslope(OpRot.Rot.Af, OpRot.Rot.Bl.iAf, reynolds);
+
             % Calculate loss factor
             lossFact = prandtlloss(OpRot.Rot.nBlades, OpRot.Rot.Bl.r, OpRot.Rot.r0, ...
                                    phi, Mod.Ext.losses);
@@ -93,6 +100,7 @@ function leishman(OpRot, Mod)
     OpRot.ElPerf.inflAngle = phi;
     OpRot.ElPerf.indVelAx = lambda * OpRot.tgTipSpeed - OpRot.Op.speed;
     OpRot.ElPerf.indVelTg = OpRot.ElPerf.tgSpeed - xi * OpRot.tgTipSpeed;
+    OpRot.ElPerf.reynolds = reynolds;
 
 end
 
diff --git a/src/solvers/stahlhut.m b/src/solvers/stahlhut.m
index 5a8a689..e79e1cb 100644
--- a/src/solvers/stahlhut.m
+++ b/src/solvers/stahlhut.m
@@ -40,6 +40,8 @@ function stahlhut(OpRot, Mod)
     % Ref: Stahlhut and Leishman, "Aerodynamic design optimization of proprotors for convertible-
     %      rotor concepts", In American Helicopter Society 68th Annual Forum. Fort Worth, TX, USA.
     % ----------------------------------------------------------------------------------------------
+    % FIXME: (L) Find a way to solve this for the whole vector at a time to improve CPU time.
+    % ----------------------------------------------------------------------------------------------
     % (c) Copyright 2022-2023 University of Liege
     % Author: Thomas Lambert <t.lambert@uliege.be>
     % ULiege - Aeroelasticity and Experimental Aerodynamics
@@ -122,6 +124,12 @@ function stahlhut(OpRot, Mod)
     OpRot.ElPerf.swirlRat = OpRot.Rot.Bl.r .* cos(OpRot.ElPerf.inflAngle) ./ b2phi;
     OpRot.ElPerf.inflowRat = OpRot.ElPerf.swirlRat .* tan(OpRot.ElPerf.inflAngle);
 
+    indVelAx = OpRot.ElPerf.inflowRat .* OpRot.tgTipSpeed;
+    indVelTg = OpRot.ElPerf.tgSpeed - OpRot.ElPerf.swirlRat .* OpRot.tgTipSpeed;
+    relVel = sqrt((OpRot.Op.speed + indVelAx).^2 + (OpRot.ElPerf.tgSpeed - indVelTg).^2);
+    OpRot.ElPerf.reynolds = Flow.reynolds(relVel, ...
+                                          OpRot.Rot.Bl.chord, OpRot.Op.Flow.mu, OpRot.Op.Flow.rho);
+
 end
 
 function [gphi, b1phi, b2phi] = stahlhuteq(i, phi, OpRot, lossType)
@@ -129,16 +137,40 @@ function [gphi, b1phi, b2phi] = stahlhuteq(i, phi, OpRot, lossType)
 
     % Defaults and constants
     SIGNUM_ZERO = 1; % Value of the signum function in 0;
+    MAX_REYNOLDS_DIFF = 50; % Maximum difference between the true the approximate Reynolds, [%]
+
+    persistent elemIdx indVelAx indVelTg
 
     % Abbreviations
     r = OpRot.Rot.Bl.r(i); % Relative position
     sol = OpRot.Rot.solidity(i); % Solidity
 
-    reynolds = OpRot.ElPerf.reynolds(i);
+    reynolds = OpRot.ElPerf.reynolds(i); % Use approximate Reynolds by default
     tgSpeed = OpRot.ElPerf.tgSpeed(i);
     pitch = OpRot.ElPerf.truePitch(i);
     airspeed = OpRot.Op.speed;
 
+    % Recalculate Reynolds based on actual inflow velocity
+    %   The first time we calculate it for a given element, we use the approximate value that
+    %   neglects the induced velocity. Afterwards, the reynolds is calculated based on the
+    %   previously found inflow velocity
+    if isempty(elemIdx) || elemIdx ~= i
+        elemIdx = i;
+    else
+        relVel = sqrt((airspeed + indVelAx)^2 + (tgSpeed - indVelTg)^2);
+        tmpReynolds = Flow.reynolds(relVel, ...
+                                    OpRot.Rot.Bl.chord(i), OpRot.Op.Flow.mu, OpRot.Op.Flow.rho);
+
+        % Stabilize first steps of numeric resolution
+        %   The main contirbutions to the Reynolds are the external velocity and the rotation speed
+        %   If the calculated value deviates too much to the approximate one, reject it and keep the
+        %   appximation. Once the solution is a bit more stable, the true Reynolds can start to be
+        %   used.
+        if abs(tmpReynolds - reynolds) / reynolds * 100 <= MAX_REYNOLDS_DIFF
+            reynolds = tmpReynolds;
+        end
+    end
+
     % Loss factor (separate swirl and axial components)
     lossFact = prandtlloss(OpRot.Rot.nBlades, r, OpRot.Rot.r0, phi, lossType);
     K_T = 1 - (1 - lossFact) .* cos(phi);
@@ -159,6 +191,11 @@ function [gphi, b1phi, b2phi] = stahlhuteq(i, phi, OpRot, lossType)
            sgn(phi, SIGNUM_ZERO) * 1 / (8 * r) * sol * cl * sec(gamma) * ...
            (tgSpeed ./ K_T .* cos(phi + gamma) + airspeed ./ K_P .* sin(phi + gamma));
 
+    % Current value for the induced velocities (needed for true Reynolds)
+    xi = r .* cos(phi) ./ b2phi;
+    lambda = xi  .* tan(phi);
+    indVelAx = lambda .* OpRot.tgTipSpeed;
+    indVelTg = tgSpeed - xi * OpRot.tgTipSpeed;
 end
 
 function plotgfunc(i, allphi, allg, unk0)
-- 
GitLab