From bc2c336293eab97f3d34b23544b87f8fcb24b7fe Mon Sep 17 00:00:00 2001
From: Paul Dechamps <paul.dechamps@uliege.be>
Date: Thu, 14 Dec 2023 15:03:54 +0100
Subject: [PATCH] (test) Added Agard wing test case

---
 blast/models/dart/agard445_visc.geo | 229 ++++++++++++++++++++++++++++
 blast/validation/agardValidation.py | 174 +++++++++++++++++++++
 2 files changed, 403 insertions(+)
 create mode 100644 blast/models/dart/agard445_visc.geo
 create mode 100644 blast/validation/agardValidation.py

diff --git a/blast/models/dart/agard445_visc.geo b/blast/models/dart/agard445_visc.geo
new file mode 100644
index 0000000..c657f6c
--- /dev/null
+++ b/blast/models/dart/agard445_visc.geo
@@ -0,0 +1,229 @@
+/* AGARD 445 wing */
+// Initially generated by unsRgridWingGen.m
+
+// Parameters
+// domain and mesh
+DefineConstant[ nSpan = { 20, Name "Spanwise number of elements" }  ];
+DefineConstant[ nLe = { 5, Name "LE chordwise number of elements" }  ];
+DefineConstant[ nTe = { 5, Name "TE chordwise number of elements" }  ];
+DefineConstant[ nMid = { 10, Name "Mid chordwise number of elements" }  ];
+DefineConstant[ nWake = { 15, Name "Wake number of elements" }  ];
+DefineConstant[ progSpan = { 1, Name "Spanwise progression" }  ];
+DefineConstant[ progLe = { 1, Name "LE progression" }  ];
+DefineConstant[ progTe = { 1, Name "TE progression" }  ];
+DefineConstant[ progMid = { 1, Name "Mid progression" }  ];
+DefineConstant[ progWake = { 1.0, Name "Wake progression" }  ];
+DefineConstant[ xO = { -3, Name "Box origin (x)" }  ];
+DefineConstant[ zO = { -3, Name "Box origin (z)" }  ];
+DefineConstant[ xL = { 7, Name "Box length" }  ];
+DefineConstant[ yL = { 3, Name "Box width" }  ];
+DefineConstant[ zL = { 6, Name "Box height" }  ];
+
+//// GEOMETRY
+
+
+/// Points
+
+// Airfoil 1: agard445, 51 points 
+Point(1) = {0.559000,0.000000,0.000000};
+Point(2) = {0.531050,0.000000,0.001398};
+Point(3) = {0.503100,0.000000,0.002739};
+Point(4) = {0.475150,0.000000,0.004075};
+Point(5) = {0.447200,0.000000,0.005406};
+Point(6) = {0.419250,0.000000,0.006680};
+Point(7) = {0.391300,0.000000,0.007837};
+Point(8) = {0.363350,0.000000,0.008866};
+Point(9) = {0.335400,0.000000,0.009743};
+Point(10) = {0.307450,0.000000,0.010442};
+Point(11) = {0.279500,0.000000,0.010923};
+Point(12) = {0.251550,0.000000,0.011158};
+Point(13) = {0.223600,0.000000,0.011163};
+Point(14) = {0.195650,0.000000,0.010968};
+Point(15) = {0.167700,0.000000,0.010576};
+Point(16) = {0.139750,0.000000,0.009995};
+Point(17) = {0.111800,0.000000,0.009196};
+Point(18) = {0.083850,0.000000,0.008156};
+Point(19) = {0.055900,0.000000,0.006781};
+Point(20) = {0.041925,0.000000,0.005920};
+Point(21) = {0.027950,0.000000,0.004891};
+Point(22) = {0.013975,0.000000,0.003617};
+Point(23) = {0.006988,0.000000,0.002622};
+Point(24) = {0.004193,0.000000,0.002057};
+Point(25) = {0.002795,0.000000,0.001699};
+Point(26) = {0.000000,0.000000,0.000000};
+Point(27) = {0.002795,0.000000,-0.001699};
+Point(28) = {0.004193,0.000000,-0.002057};
+Point(29) = {0.006988,0.000000,-0.002622};
+Point(30) = {0.013975,0.000000,-0.003617};
+Point(31) = {0.027950,0.000000,-0.004891};
+Point(32) = {0.041925,0.000000,-0.005920};
+Point(33) = {0.055900,0.000000,-0.006781};
+Point(34) = {0.083850,0.000000,-0.008156};
+Point(35) = {0.111800,0.000000,-0.009196};
+Point(36) = {0.139750,0.000000,-0.009995};
+Point(37) = {0.167700,0.000000,-0.010576};
+Point(38) = {0.195650,0.000000,-0.010968};
+Point(39) = {0.223600,0.000000,-0.011163};
+Point(40) = {0.251550,0.000000,-0.011158};
+Point(41) = {0.279500,0.000000,-0.010923};
+Point(42) = {0.307450,0.000000,-0.010442};
+Point(43) = {0.335400,0.000000,-0.009743};
+Point(44) = {0.363350,0.000000,-0.008866};
+Point(45) = {0.391300,0.000000,-0.007837};
+Point(46) = {0.419250,0.000000,-0.006680};
+Point(47) = {0.447200,0.000000,-0.005406};
+Point(48) = {0.475150,0.000000,-0.004075};
+Point(49) = {0.503100,0.000000,-0.002739};
+Point(50) = {0.531050,0.000000,-0.001398};
+
+// Airfoil 2: agard445, 51 points 
+Point(52) = {1.178128,0.762000,0.000000};
+Point(53) = {1.159709,0.762000,0.000921};
+Point(54) = {1.141290,0.762000,0.001805};
+Point(55) = {1.122870,0.762000,0.002685};
+Point(56) = {1.104451,0.762000,0.003562};
+Point(57) = {1.086032,0.762000,0.004402};
+Point(58) = {1.067613,0.762000,0.005165};
+Point(59) = {1.049194,0.762000,0.005843};
+Point(60) = {1.030775,0.762000,0.006421};
+Point(61) = {1.012356,0.762000,0.006881};
+Point(62) = {0.993937,0.762000,0.007198};
+Point(63) = {0.975518,0.762000,0.007353};
+Point(64) = {0.957099,0.762000,0.007357};
+Point(65) = {0.938680,0.762000,0.007228};
+Point(66) = {0.920261,0.762000,0.006970};
+Point(67) = {0.901842,0.762000,0.006587};
+Point(68) = {0.883423,0.762000,0.006060};
+Point(69) = {0.865004,0.762000,0.005375};
+Point(70) = {0.846585,0.762000,0.004468};
+Point(71) = {0.837375,0.762000,0.003901};
+Point(72) = {0.828166,0.762000,0.003223};
+Point(73) = {0.818956,0.762000,0.002383};
+Point(74) = {0.814351,0.762000,0.001728};
+Point(75) = {0.812509,0.762000,0.001356};
+Point(76) = {0.811589,0.762000,0.001120};
+Point(77) = {0.809747,0.762000,0.000000};
+Point(78) = {0.811589,0.762000,-0.001120};
+Point(79) = {0.812509,0.762000,-0.001356};
+Point(80) = {0.814351,0.762000,-0.001728};
+Point(81) = {0.818956,0.762000,-0.002383};
+Point(82) = {0.828166,0.762000,-0.003223};
+Point(83) = {0.837375,0.762000,-0.003901};
+Point(84) = {0.846585,0.762000,-0.004468};
+Point(85) = {0.865004,0.762000,-0.005375};
+Point(86) = {0.883423,0.762000,-0.006060};
+Point(87) = {0.901842,0.762000,-0.006587};
+Point(88) = {0.920261,0.762000,-0.006970};
+Point(89) = {0.938680,0.762000,-0.007228};
+Point(90) = {0.957099,0.762000,-0.007357};
+Point(91) = {0.975518,0.762000,-0.007353};
+Point(92) = {0.993937,0.762000,-0.007198};
+Point(93) = {1.012356,0.762000,-0.006881};
+Point(94) = {1.030775,0.762000,-0.006421};
+Point(95) = {1.049194,0.762000,-0.005843};
+Point(96) = {1.067613,0.762000,-0.005165};
+Point(97) = {1.086032,0.762000,-0.004402};
+Point(98) = {1.104451,0.762000,-0.003562};
+Point(99) = {1.122870,0.762000,-0.002685};
+Point(100) = {1.141290,0.762000,-0.001805};
+Point(101) = {1.159709,0.762000,-0.000921};
+
+
+// Midplane:
+Point(5351) = {xO+xL,0.000000,0.000000};
+Point(5352) = {xO+xL,0.762000,0.000000};
+
+/// Lines
+
+// Airfoil 1:
+Spline(1) = {1,2,3};
+Spline(2) = {3,4,5,6,7,8,9,10,11,12,13,14,15};
+Spline(3) = {15,16,17,18,19,20,21,22,23,24,25,26};
+Spline(4) = {26,27,28,29,30,31,32,33,34,35,36,37};
+Spline(5) = {37,38,39,40,41,42,43,44,45,46,47,48,49};
+Spline(6) = {49,50,1};
+
+// Airfoil 2:
+Spline(7) = {52,53,54};
+Spline(8) = {54,55,56,57,58,59,60,61,62,63,64,65,66};
+Spline(9) = {66,67,68,69,70,71,72,73,74,75,76,77};
+Spline(10) = {77,78,79,80,81,82,83,84,85,86,87,88};
+Spline(11) = {88,89,90,91,92,93,94,95,96,97,98,99,100};
+Spline(12) = {100,101,52};
+
+
+// Airfoil 1 to airfoil 2:
+Line(61) = {1,52};
+Line(62) = {3,54};
+Line(63) = {15,66};
+Line(64) = {26,77};
+Line(65) = {37,88};
+Line(66) = {49,100};
+
+// Midplane:
+Line(131) = {5351,5352};
+
+// Wing to midplane:
+Line(161) = {1,5351};
+Line(162) = {52,5352};
+/// Line loops & Surfaces
+
+// Wing 1:
+Line Loop(11) = {1,62,-7,-61};
+Line Loop(12) = {2,63,-8,-62};
+Line Loop(13) = {3,64,-9,-63};
+Line Loop(14) = {4,65,-10,-64};
+Line Loop(15) = {5,66,-11,-65};
+Line Loop(16) = {6,61,-12,-66};
+Surface(11) = {-11};
+Surface(12) = {-12};
+Surface(13) = {-13};
+Surface(14) = {-14};
+Surface(15) = {-15};
+Surface(16) = {-16};
+
+
+// Wake:
+Line Loop(81) = {161,131,-162,-61};
+Surface(81) = {81};
+
+/// Surface loops & Volumes
+
+
+//// MESHING ALGORITHM
+
+
+//// PHYSICAL GROUPS
+
+// Trailing edge and wake tip
+Physical Line("wakeTip") = {162};
+Physical Line("te") = {61};
+
+// Wing:
+Physical Surface("wing") = {11,12,13};
+Physical Surface("wing_") = {14,15,16};
+
+// Wake:
+Physical Surface("wake") = {81};
+
+Coherence;
+Transfinite Curve {161, 162} = nWake Using Progression progWake;
+Transfinite Curve {131, 61, 62, 66, 63, 65, 64} = nSpan Using Progression progSpan;
+Transfinite Curve {-3, 4, -9, 10} = nLe Using Progression progLe;
+Transfinite Curve {7, -12, 1, -6} = nTe Using Progression progTe;
+Transfinite Curve {2, 5, 8, 11} = nMid Using Progression progMid;
+Transfinite Surface {81};
+Transfinite Surface {12};
+Transfinite Surface {15};
+Transfinite Surface {13};
+Transfinite Surface {14};
+Transfinite Surface {11};
+Transfinite Surface {16};
+
+Recombine Surface {81};
+Recombine Surface {12};
+Recombine Surface {15};
+Recombine Surface {13};
+Recombine Surface {14};
+Recombine Surface {11};
+Recombine Surface {16};
diff --git a/blast/validation/agardValidation.py b/blast/validation/agardValidation.py
new file mode 100644
index 0000000..0b887fa
--- /dev/null
+++ b/blast/validation/agardValidation.py
@@ -0,0 +1,174 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# Copyright 2022 University of Liège
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# @author Paul Dechamps
+# @date 2022
+# Test the blast implementation. The test case is a compressible attached transitional flow.
+# Tested functionalities;
+# - Time integration.
+# - Two time-marching techniques (agressive and safe).
+# - Transition routines.
+# - Forced transition.
+# - Compressible flow routines.
+# - Laminar and turbulent flow.
+#
+# Untested functionalities.
+# - Separation routines.
+# - Multiple failure case at one iteration.
+# - Response to unconverged inviscid solver.
+
+# Imports.
+
+import blast.utils as viscUtils
+import numpy as np
+import os.path
+
+from fwk.wutils import parseargs
+import fwk
+from fwk.testing import *
+from fwk.coloring import ccolors
+
+import math
+
+def cfgInviscid(nthrds, verb):
+    # Parameters
+    return {
+    # Options
+    'scenario' : 'aerodynamic',
+    'task' : 'analysis',
+    'Threads' : nthrds, # number of threads
+    'Verb' : verb, # verbosity
+    # Model (geometry or mesh)
+    'File' : os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + '/modules/dartflo/dart/models/agard445.geo', # Input file containing the model
+    'Pars' : {'xL': 7., 'yL': 3., 'zL': 6., 'xO': -3, 'zO': -3, 'msLeRt': 0.0028, 'msTeRt': 0.0056, 'msLeTp': 0.0018, 'msTeTp': 0.0036, 'msF': 1.0}, # parameters for input file model
+    'Dim' : 3, # problem dimension
+    'Format' : 'vtk', # save format (vtk or gmsh)
+    # Markers
+    'Fluid' : 'field', # name of physical group containing the fluid
+    'Farfield' : ['upstream', 'farfield', 'downstream'], # LIST of names of physical groups containing the farfield boundaries (upstream/downstream should be first/last element)
+    'Wings' : ['wing'], # LIST of names of physical groups containing the lifting surface boundary
+    'Wakes' : ['wake'], # LIST of names of physical group containing the wake
+    'WakeTips' : ['wakeTip'], # LIST of names of physical group containing the edge of the wake
+    'Tes' : ['te'], # LIST of names of physical group containing the trailing edge
+    'dbc' : True,
+    'Upstream' : 'upstream',
+    # Freestream
+    'M_inf' : 0.954,     # freestream Mach number
+    'AoA' : 1,         # freestream angle of attack
+    # Geometry
+    'S_ref' : 0.35,    # reference surface length
+    'c_ref' : 0.47,      # reference chord length
+    'x_ref' : 0.0,       # reference point for moment computation (x)
+    'y_ref' : 0.0,       # reference point for moment computation (y)
+    'z_ref' : 0.0,       # reference point for moment computation (z)
+    # Numerical
+    'LSolver' : 'GMRES', # inner solver (Pardiso, MUMPS or GMRES)
+    'G_fill' : 2,        # fill-in factor for GMRES preconditioner
+    'G_tol' : 1e-5,      # tolerance for GMRES
+    'G_restart' : 50,    # restart for GMRES
+    'Rel_tol' : 1e-6,    # relative tolerance on solver residual
+    'Abs_tol' : 1e-8,    # absolute tolerance on solver residual
+    'Max_it' : 50        # solver maximum number of iterations
+    }
+
+def cfgBlast(verb):
+    return {
+        'Re' : 6.7e6,       # Freestream Reynolds number
+        'Minf' : 0.954,     # Freestream Mach number (used for the computation of the time step only)
+        'CFL0' : 1,         # Inital CFL number of the calculation
+
+        'sections' : np.linspace(0.026, 0.7, 15),
+        'writeSections': np.linspace(0.01, 0.76, 15),
+        'Sym':[0.],
+        'span': 0.762,
+        'interpolator': 'Rbf',
+        'rbftype': 'linear',
+        'smoothing': 1e-8,
+        'degree': 0,
+        'neighbors': 10,
+        'saveTag': 5,
+
+        'Verb': verb,       # Verbosity level of the solver
+        'couplIter': 100,   # Maximum number of iterations
+        'couplTol' : 1e-3,  # Tolerance of the VII methodology
+        'iterPrint': 1,     # int, number of iterations between outputs
+        'resetInv' : True,  # bool, flag to reset the inviscid calculation at every iteration.
+        'xtrF' : [0., 0.],  # Forced transition location
+        'nDim' : 3
+    }
+
+def main():
+    # Timer.
+    tms = fwk.Timers()
+    tms['total'].start()
+
+    args = parseargs()
+    icfg = cfgInviscid(args.k, args.verb)
+    vcfg = cfgBlast(args.verb)
+
+    parsViscous = {'nLe': 15, 'nMid': 30, 'nTe': 7, 'nSpan': 60, 'nWake': 30,
+                   'progLe': 1.07, 'progMid': 1.0,  'progTe': 1.0, 'progSpan': 1.0, 'progWake': 1.15}
+    vMsh = viscUtils.mesh(os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + '/models/dart/agard445_visc.geo', parsViscous)
+    vcfg['vMsh'] = vMsh
+
+    tms['pre'].start()
+    coupler, iSolverAPI, vSolver = viscUtils.initBlast(icfg, vcfg)
+    tms['pre'].stop()
+
+    print(ccolors.ANSI_BLUE + 'PySolving...' + ccolors.ANSI_RESET)
+    tms['solver'].start()
+    aeroCoeffs = coupler.run()
+    tms['solver'].stop()
+
+    # Display results.
+    print(ccolors.ANSI_BLUE + 'PyRes...' + ccolors.ANSI_RESET)
+    print('      Re        M    alpha       Cl       Cd      Cdp      Cdf       Cm')
+    print('{0:6.1f}e6 {1:8.2f} {2:8.1f} {3:8.4f} {4:8.4f} {5:8.4f} {6:8.4f} {7:8.4f}'.format(vcfg['Re']/1e6, iSolverAPI.getMinf(), iSolverAPI.getAoA()*180/math.pi, iSolverAPI.getCl(), vSolver.Cdt, vSolver.Cdp, vSolver.Cdf, iSolverAPI.getCm()))
+
+     # Write results to file.
+    vSolution = viscUtils.getSolution(vSolver)
+
+    # Write results to file.
+    for iSec in range(len(iSolverAPI.cfg['EffSections'])):
+        vSolution = viscUtils.getSolution(vSolver, iSec)
+        viscUtils.write(vSolution, vSolver.getRe(), sfx='slice'+str(iSec))
+    vSolution['Cdt_int'] = vSolver.Cdf + iSolverAPI.getCd()
+
+    # Save pressure coefficient
+    iSolverAPI.save(sfx='_viscous')
+    tms['total'].stop()
+
+    print(ccolors.ANSI_BLUE + 'PyTiming...' + ccolors.ANSI_RESET)
+    print('CPU statistics')
+    print(tms)
+    print('SOLVERS statistics')
+    print(coupler.tms)
+    
+    # Test solution
+    print(ccolors.ANSI_BLUE + 'PyTesting...' + ccolors.ANSI_RESET)
+    tests = CTests()
+    tests.add(CTest('Cl', iSolverAPI.getCl(), 0.069, 5e-2))
+    tests.add(CTest('Cd wake', vSolution['Cdt_int'], 0.00498, 1e-3, forceabs=True))
+    tests.add(CTest('Iterations', len(aeroCoeffs), 8, 0, forceabs=True))
+    tests.run()
+
+    # eof
+    print('')
+
+if __name__ == "__main__":
+    main()
-- 
GitLab