From 1f8b1200432c56bc89047dbb6ed1ceac6cc8acff Mon Sep 17 00:00:00 2001 From: Paul Dechamps <paul.dechamps@uliege.be> Date: Fri, 21 Apr 2023 14:33:55 +0200 Subject: [PATCH] Added forced transition capability in vii. --- vii/api/core.py | 9 +++++++- vii/src/DBoundaryLayer.cpp | 1 + vii/src/DBoundaryLayer.h | 1 + vii/src/DDiscretization.cpp | 22 +++++++++++++++++++ vii/src/DDiscretization.h | 5 +++++ vii/src/DDriver.cpp | 44 +++++++++++++++++++++++++++++-------- vii/src/DDriver.h | 4 +++- vii/tests/bli2D.py | 10 +++++---- vii/utils.py | 10 +++++++-- 9 files changed, 89 insertions(+), 17 deletions(-) diff --git a/vii/api/core.py b/vii/api/core.py index af26fbf..31bda72 100644 --- a/vii/api/core.py +++ b/vii/api/core.py @@ -28,6 +28,7 @@ def initVII(cfg, icfg, iSolverName='DART'): - Minf (float): Freestream Mach number (only used for time step computation). - CFL0 (float): Initial CFL number. - Verb (int): Verbosity level of the viscous solver. + - xtrF ([float, float]): Forced transition location [upper, lower]. - couplIter (int): Maximum number of coupling iterations. - couplTol (float): Tolerance to end computation (drag count between 2 iterations). @@ -76,6 +77,12 @@ def initVII(cfg, icfg, iSolverName='DART'): _verbose = cfg['Verb'] else: _verbose = 1 + _xtrF = [-1, -1] + if 'xtrF' in cfg: + for i, xtr in enumerate(cfg['xtrF']): + if not(0 <= xtr <= 1) and xtr != -1: + raise RuntimeError("Incorrect forced transition location.") + _xtrF[i] = xtr _span = 0 _nSections = 1 @@ -98,7 +105,7 @@ def initVII(cfg, icfg, iSolverName='DART'): __resetInv = False # Viscous solver object. - vSolver = vii.Driver(_Re, _Minf, _CFL0, _nSections, _span, verbose =_verbose) + vSolver = vii.Driver(_Re, _Minf, _CFL0, _nSections, _xtrF[0], _xtrF[1], _span, verbose =_verbose) # Solvers interface. solversAPI = interface(iSolverObjects['sol'], vSolver, [iSolverObjects['blwb'], iSolverObjects['blww']]) # Coupler diff --git a/vii/src/DBoundaryLayer.cpp b/vii/src/DBoundaryLayer.cpp index 6301a13..a690d71 100644 --- a/vii/src/DBoundaryLayer.cpp +++ b/vii/src/DBoundaryLayer.cpp @@ -19,6 +19,7 @@ BoundaryLayer::BoundaryLayer(double _xtrF) xtrF = _xtrF; xtr = 1.0; + xoctr = 1.0; closSolver = new Closures(); mesh = new Discretization(); blEdge = new Edge(); diff --git a/vii/src/DBoundaryLayer.h b/vii/src/DBoundaryLayer.h index 7c8e223..5d3853c 100644 --- a/vii/src/DBoundaryLayer.h +++ b/vii/src/DBoundaryLayer.h @@ -45,6 +45,7 @@ public: size_t transMarker; ///< Marker id of the transition location. double xtrF; ///< Forced transition location in the global frame of reference. double xtr; ///< Transition location in the global frame of reference. + double xoctr; ///< Transition location in % of the chord. BoundaryLayer(double _xtrF = -1.0); ~BoundaryLayer(); diff --git a/vii/src/DDiscretization.cpp b/vii/src/DDiscretization.cpp index 29b7033..8f4e561 100644 --- a/vii/src/DDiscretization.cpp +++ b/vii/src/DDiscretization.cpp @@ -1,5 +1,6 @@ #include "DDiscretization.h" #include <cmath> +#include <algorithm> using namespace vii; @@ -19,6 +20,7 @@ void Discretization::setGlob(std::vector<double> &_x, std::vector<double> &_y, s nMarkers = _x.size(); x.resize(nMarkers, 0.); + xoc.resize(nMarkers, 0.); y.resize(nMarkers, 0.); z.resize(nMarkers, 0.); nElm = nMarkers - 1; @@ -27,6 +29,26 @@ void Discretization::setGlob(std::vector<double> &_x, std::vector<double> &_y, s z = _z; computeBLcoord(); + computeOCcoord(); +} + +/** + * @brief Compute x/chord coordinate. +*/ +void Discretization::computeOCcoord() +{ + // Find min and max of array x + auto min_it = std::min_element(x.begin(), x.end()); + double xMin = *min_it; + auto max_it = std::max_element(x.begin(), x.end()); + double xMax = *max_it; + + // Compute xoc + chord = xMax - xMin; + if (chord <= 0) + throw; + for (size_t iPoint = 0; iPoint < x.size(); ++iPoint) + xoc[iPoint] = (x[iPoint] - xMin) / chord; } /** diff --git a/vii/src/DDiscretization.h b/vii/src/DDiscretization.h index 1f9855b..1f40cb8 100644 --- a/vii/src/DDiscretization.h +++ b/vii/src/DDiscretization.h @@ -15,6 +15,7 @@ class VII_API Discretization { private: std::vector<double> x; ///< x coordinate in the global frame of reference. + std::vector<double> xoc; ///< x/c coordinate in the global frame of reference. std::vector<double> y; ///< y coordinate in the global frame of reference. std::vector<double> z; ///< z coordinate in the global frame of reference. std::vector<double> loc; ///< xi coordinate in the local frame of reference. @@ -23,6 +24,7 @@ private: std::vector<double> alpha; ///< Angle of the cell wrt the x axis of the global frame of reference. size_t nMarkers; ///< Number of points in the domain. size_t nElm; ///< Number of cells in the domain. + double chord; ///< Chord of the section. public: size_t prevnMarkers; ///< Number of points in the domain at the previous iteration. @@ -34,16 +36,19 @@ public: size_t getnMarkers() const { return nMarkers; }; double getLoc(size_t iPoint) const { return loc[iPoint]; }; double getx(size_t iPoint) const { return x[iPoint]; }; + double getxoc(size_t iPoint) const { return xoc[iPoint]; }; double gety(size_t iPoint) const { return y[iPoint]; }; double getz(size_t iPoint) const { return z[iPoint]; }; double getExt(size_t iPoint) const { return ext[iPoint]; }; double getAlpha(size_t iElm) const { return alpha[iElm]; }; size_t getDiffnElm() const { return nMarkers - prevnMarkers; }; + double getChord() const { return chord; }; // Setters. void setGlob(std::vector<double> &_x, std::vector<double> &_y, std::vector<double> &_z); void setExt(std::vector<double> extM); void computeBLcoord(); + void computeOCcoord(); }; } // namespace vii #endif // WDISCRETIZATION_H diff --git a/vii/src/DDriver.cpp b/vii/src/DDriver.cpp index f226c03..8000696 100644 --- a/vii/src/DDriver.cpp +++ b/vii/src/DDriver.cpp @@ -17,16 +17,22 @@ using namespace vii; * @param _Minf Freestream Mach number. * @param _CFL0 Initial CFL number for pseudo-time integration. * @param nSections Number of sections in the computation (1 for 2D cases). + * @param xtrF_top Forced transition location on the upper side. + * @param xtrF_bot Forced transition location on the lower side. * @param _Span Span of the considered wing (only used for drag computation). * @param _verbose Verbosity level of the solver 0<=verbose<=1. */ -Driver::Driver(double _Re, double _Minf, double _CFL0, size_t nSections, double _Span, unsigned int _verbose) +Driver::Driver(double _Re, double _Minf, double _CFL0, size_t nSections, + double xtrF_top, double xtrF_bot, double _span, unsigned int _verbose) { // Freestream parameters. Re = _Re; CFL0 = _CFL0; verbose = _verbose; + // Vector of forced transition {upper, lower, wake}. + std::vector<double> xtrF = {xtrF_top, xtrF_bot, 0.}; + // Initialzing boundary layer sections.resize(nSections); convergenceStatus.resize(nSections); @@ -35,7 +41,7 @@ Driver::Driver(double _Re, double _Minf, double _CFL0, size_t nSections, double convergenceStatus[iSec].resize(3); for (size_t i = 0; i < 3; ++i) { - BoundaryLayer *region = new BoundaryLayer(); + BoundaryLayer *region = new BoundaryLayer(xtrF[i]); sections[iSec].push_back(region); } sections[iSec][0]->name = "upper"; @@ -49,7 +55,7 @@ Driver::Driver(double _Re, double _Minf, double _CFL0, size_t nSections, double Cdt = 0.; Cdf = 0.; Cdp = 0.; - span = _Span; + span = _span; // Pre/post processing flags. stagPtMvmt.resize(sections.size(), false); @@ -199,7 +205,9 @@ int Driver::run(unsigned int const couplIter) // Solve equations tms["1-Solver"].start(); pointExitCode = tSolver->integration(iPoint, sections[iSec][iRegion]); - // sections[iSec][iRegion]->u[iPoint * sections[iSec][iRegion]->getnVar()+2] = 9.; + // Force transition if required by the user + if (sections[iSec][iRegion]->xtrF != -1 && sections[iSec][iRegion]->mesh->getxoc(iPoint) >= sections[iSec][iRegion]->xtrF) + sections[iSec][iRegion]->u[iPoint * sections[iSec][iRegion]->getnVar()+2] = 9.; tms["1-Solver"].stop(); if (pointExitCode != 0) @@ -210,7 +218,9 @@ int Driver::run(unsigned int const couplIter) if (!lockTrans) { // Check if perturbation waves are growing or not. - checkWaves(iPoint, sections[iSec][iRegion]); + // Avoided in the case of a forced transition. + if (sections[iSec][iRegion]->xtrF != -1 && sections[iSec][iRegion]->mesh->getxoc(iPoint) <= sections[iSec][iRegion]->xtrF) + checkWaves(iPoint, sections[iSec][iRegion]); // Amplification factor is compared to critical amplification factor. if (sections[iSec][iRegion]->u[iPoint * sections[iSec][iRegion]->getnVar() + 2] >= sections[iSec][iRegion]->getnCrit()) @@ -220,7 +230,7 @@ int Driver::run(unsigned int const couplIter) { std::cout << std::fixed; std::cout << std::setprecision(2); - std::cout << sections[iSec][iRegion]->xtr + std::cout << sections[iSec][iRegion]->xoctr << " (" << sections[iSec][iRegion]->transMarker << ") "; } lockTrans = true; @@ -292,6 +302,7 @@ void Driver::averageTransition(size_t iPoint, BoundaryLayer *bl, int forced) // Compute transition location. bl->xtr = (bl->getnCrit() - bl->u[(iPoint - 1) * nVar + 2]) * ((bl->mesh->getx(iPoint) - bl->mesh->getx(iPoint - 1)) / (bl->u[iPoint * nVar + 2] - bl->u[(iPoint - 1) * nVar + 2])) + bl->mesh->getx(iPoint - 1); + bl->xoctr = (bl->getnCrit() - bl->u[(iPoint - 1) * nVar + 2]) * ((bl->mesh->getxoc(iPoint) - bl->mesh->getxoc(iPoint - 1)) / (bl->u[iPoint * nVar + 2] - bl->u[(iPoint - 1) * nVar + 2])) + bl->mesh->getxoc(iPoint - 1); // Percentage of the subsection corresponding to a turbulent flow. double avTurb = (bl->mesh->getx(iPoint) - bl->xtr) / (bl->mesh->getx(iPoint) - bl->mesh->getx(iPoint - 1)); @@ -471,7 +482,7 @@ int Driver::outputStatus(double maxMach) const std::vector<double> meanTransitions(2, 0.); for (size_t iSec = 0; iSec < sections.size(); ++iSec) for (size_t iReg = 0; iReg < 2; ++iReg) - meanTransitions[iReg] += sections[iSec][iReg]->xtr; + meanTransitions[iReg] += sections[iSec][iReg]->xoctr; for (size_t iReg = 0; iReg < meanTransitions.size(); ++iReg) meanTransitions[iReg] /= sections.size(); @@ -694,10 +705,10 @@ void Driver::setUeOld(size_t iSec, size_t iRegion, std::vector<double> _ue) std::vector<std::vector<double>> Driver::getSolution(size_t iSec) { size_t nMarkersUp = sections[iSec][0]->mesh->getnMarkers(); - size_t nMarkersLw = sections[iSec][1]->mesh->getnMarkers() - 1; // No stagnation point doublon. + size_t nMarkersLw = sections[iSec][1]->mesh->getnMarkers(); // No stagnation point doublon. size_t nVar = sections[iSec][0]->getnVar(); - std::vector<std::vector<double>> Sln(16); + std::vector<std::vector<double>> Sln(20); for (size_t iVector = 0; iVector < Sln.size(); ++iVector) Sln[iVector].resize(nMarkersUp + nMarkersLw + sections[iSec][2]->mesh->getnMarkers(), 0.); // Upper side. @@ -722,6 +733,11 @@ std::vector<std::vector<double>> Driver::getSolution(size_t iSec) Sln[14][iPoint] = sections[iSec][0]->delta[revPoint]; Sln[15][iPoint] = sections[iSec][0]->mesh->getx(revPoint); + Sln[16][iPoint] = sections[iSec][0]->mesh->getxoc(revPoint); + Sln[17][iPoint] = sections[iSec][0]->mesh->gety(revPoint); + Sln[18][iPoint] = sections[iSec][0]->mesh->getz(revPoint); + Sln[19][iPoint] = sections[iSec][0]->blEdge->getVt(revPoint); + --revPoint; } // Lower side. @@ -745,6 +761,12 @@ std::vector<std::vector<double>> Driver::getSolution(size_t iSec) Sln[14][nMarkersUp + iPoint] = sections[iSec][1]->delta[iPoint]; Sln[15][nMarkersUp + iPoint] = sections[iSec][1]->mesh->getx(iPoint); + Sln[16][nMarkersUp + iPoint] = sections[iSec][1]->mesh->getxoc(iPoint); + Sln[17][nMarkersUp + iPoint] = sections[iSec][1]->mesh->gety(iPoint); + Sln[18][nMarkersUp + iPoint] = sections[iSec][1]->mesh->getz(iPoint); + Sln[19][nMarkersUp + iPoint] = sections[iSec][1]->blEdge->getVt(iPoint); + + } // Wake. for (size_t iPoint = 0; iPoint < sections[iSec][2]->mesh->getnMarkers(); ++iPoint) @@ -767,6 +789,10 @@ std::vector<std::vector<double>> Driver::getSolution(size_t iSec) Sln[14][nMarkersUp + nMarkersLw + iPoint] = sections[iSec][2]->delta[iPoint]; Sln[15][nMarkersUp + nMarkersLw + iPoint] = sections[iSec][2]->mesh->getx(iPoint); + Sln[16][nMarkersUp + nMarkersLw + iPoint] = sections[iSec][2]->mesh->getxoc(iPoint); + Sln[17][nMarkersUp + nMarkersLw + iPoint] = sections[iSec][2]->mesh->gety(iPoint); + Sln[18][nMarkersUp + nMarkersLw + iPoint] = sections[iSec][2]->mesh->getz(iPoint); + Sln[19][nMarkersUp + nMarkersLw + iPoint] = sections[iSec][2]->blEdge->getVt(iPoint); } if (verbose > 2) diff --git a/vii/src/DDriver.h b/vii/src/DDriver.h index 3651560..3690a68 100644 --- a/vii/src/DDriver.h +++ b/vii/src/DDriver.h @@ -37,13 +37,15 @@ protected: fwk::Timers tms; ///< Internal timers public: - Driver(double _Re, double _Minf, double _CFL0, size_t nSections, double _span = 0., unsigned int verbose = 1); + Driver(double _Re, double _Minf, double _CFL0, size_t nSections, + double xtrF_top=-1., double xtrF_bot=-1., double _span = 0., unsigned int verbose = 1); ~Driver(); int run(unsigned int const couplIter); // Getters. double getxtr(size_t iSec, size_t iRegion) const { return sections[iSec][iRegion]->xtr; }; double getRe() const { return Re; }; + double getxoctr(size_t iSec, size_t iRegion) const { return sections[iSec][iRegion]->xoctr; }; std::vector<std::vector<double>> getSolution(size_t iSec); // Setters. diff --git a/vii/tests/bli2D.py b/vii/tests/bli2D.py index e661a8b..f6b29a2 100644 --- a/vii/tests/bli2D.py +++ b/vii/tests/bli2D.py @@ -25,6 +25,7 @@ # - Transition routines. # - Compressible flow routines. # - Laminar and turbulent flow. +# - Forced transition. # # Untested functionalities. # - Separation routines. @@ -54,6 +55,7 @@ def main(): Re = 1e7 alpha = 2.*math.pi/180 M_inf = 0.2 + xtrF = [-1, 0.45] # Numerical parameters. CFL0 = 1 @@ -74,7 +76,7 @@ def main(): print(ccolors.ANSI_BLUE + 'PySolving...' + ccolors.ANSI_RESET) tms['solver'].start() - vSolver = viscUtils.initBL(Re, M_inf, CFL0, 1) + vSolver = viscUtils.initBL(Re, M_inf, CFL0, 1, xtrF=xtrF) iSolverAPI = viscUtils.initDart(pbl, blw, vSolver) Coupler = viiCoupler.Coupler(iSolverAPI, vSolver, _maxCouplIter=20, _couplTol=1e-4, _iterPrint=1, _resetInv=False) Coupler.run() @@ -110,11 +112,11 @@ def main(): # Test for n0012 (le=0.01, te=0.01), alpha=2, M=.2, Re=1e7. print(ccolors.ANSI_BLUE + 'PyTesting...' + ccolors.ANSI_RESET) tests = CTests() - tests.add(CTest('Cl', iSolverAPI.getCl(), 0.234, 5e-3)) # XFOIL 0.2325 + tests.add(CTest('Cl', iSolverAPI.getCl(), 0.2344, 5e-3)) # XFOIL 0.2325 tests.add(CTest('Cd', vSolution['Cdt'], 0.0055, 1e-3, forceabs=True)) # XFOIL 0.00531 - tests.add(CTest('Cdp', vSolution['Cdp'], 0.0012, 1e-3, forceabs=True)) # XFOIL 0.00084, Cdf = 0.00447 + tests.add(CTest('Cdp', vSolution['Cdp'], 0.0010, 1e-3, forceabs=True)) # XFOIL 0.00084, Cdf = 0.00447 tests.add(CTest('xtr_top', vSolution['xtrT'], 0.21, 0.03, forceabs=True)) # XFOIL 0.1877 - tests.add(CTest('xtr_bot', vSolution['xtrB'], 0.50, 0.03, forceabs=True)) # XFOIL 0.4998 + tests.add(CTest('xtr_bot', vSolution['xtrB'], 0.45, 0.01, forceabs=True)) # XFOIL 0.4998 tests.run() # eof diff --git a/vii/utils.py b/vii/utils.py index 04a1178..c090dc2 100644 --- a/vii/utils.py +++ b/vii/utils.py @@ -3,7 +3,7 @@ from fwk.wutils import parseargs from fwk.coloring import ccolors import numpy as np -def initBL(Re, Minf, CFL0, nSections, span=0, verb=None): +def initBL(Re, Minf, CFL0, nSections, xtrF=[-1, -1], span=0, verb=None): """ Initialize boundary layer solver. Params @@ -12,6 +12,7 @@ def initBL(Re, Minf, CFL0, nSections, span=0, verb=None): - Minf: Freestream Mach number. - CFL0: Initial CFL number for time integration. - nSections: Number of sections in the domain. + - xtrF: Forced transition location [upper, lower]. - span: Wing span (not used for 2D computations. - verb: Verbosity level of the solver. @@ -39,7 +40,12 @@ def initBL(Re, Minf, CFL0, nSections, span=0, verb=None): raise RuntimeError("Invalid parameter") else: _verbose = verb - solver = vii.Driver(Re, Minf, CFL0, nSections, span, verbose=_verbose) + _xtrF = [-1, -1] + for i, xtr in enumerate(xtrF): + if not(0 <= xtr <= 1) and xtr != -1: + raise RuntimeError("Incorrect forced transition location.") + _xtrF[i] = xtr + solver = vii.Driver(Re, Minf, CFL0, nSections, xtrF[0], xtrF[1], span, verbose=_verbose) return solver def initDart(pbl, blw, vSolver, iters = 25): -- GitLab