From 4981213b5fa06cd4cc0bbdea46ea3410a0b8fd3e Mon Sep 17 00:00:00 2001
From: Paul Dechamps <paul.dechamps@uliege.be>
Date: Sat, 17 Feb 2024 12:30:58 +0100
Subject: [PATCH] (feat) First part for coupled adjoint VII

---
 blast/_src/blastw.h                    |   3 +-
 blast/_src/blastw.i                    |   5 +-
 blast/interfaces/dart/DartInterface.py |  10 +-
 blast/src/DCoupledAdjoint.cpp          | 277 +++++++++++++++++++++++++
 blast/src/DCoupledAdjoint.h            |  94 +++++++++
 blast/src/blast.h                      |   3 +
 blast/tests/dart/adjointVII.py         | 218 +++++++++++++++++++
 7 files changed, 606 insertions(+), 4 deletions(-)
 create mode 100644 blast/src/DCoupledAdjoint.cpp
 create mode 100644 blast/src/DCoupledAdjoint.h
 create mode 100644 blast/tests/dart/adjointVII.py

diff --git a/blast/_src/blastw.h b/blast/_src/blastw.h
index ec45463..b1c27f1 100644
--- a/blast/_src/blastw.h
+++ b/blast/_src/blastw.h
@@ -14,4 +14,5 @@
  * limitations under the License.
  */
 
-#include "DDriver.h"
\ No newline at end of file
+#include "DDriver.h"
+#include "DCoupledAdjoint.h"
\ No newline at end of file
diff --git a/blast/_src/blastw.i b/blast/_src/blastw.i
index ab103f1..ac1b2e6 100644
--- a/blast/_src/blastw.i
+++ b/blast/_src/blastw.i
@@ -54,4 +54,7 @@ threads="1"
 %immutable blast::Driver::Cdp_sec;
 %immutable blast::Driver::CFL0;
 %immutable blast::Driver::verbose;
-%include "DDriver.h"
\ No newline at end of file
+%include "DDriver.h"
+
+%shared_ptr(blast::CoupledAdjoint);
+%include "DCoupledAdjoint.h"
diff --git a/blast/interfaces/dart/DartInterface.py b/blast/interfaces/dart/DartInterface.py
index da0809b..a52b126 100644
--- a/blast/interfaces/dart/DartInterface.py
+++ b/blast/interfaces/dart/DartInterface.py
@@ -28,10 +28,16 @@ class DartInterface(SolversInterface):
     def __init__(self, dartCfg, vSolver, _cfg):
         try:
             from modules.dartflo.dart.api.core import initDart
-            self.argOut = initDart(dartCfg, viscous=True)
+            if 'task' not in dartCfg:
+                dartCfg['task'] = 'analysis'
+            self.argOut = initDart(dartCfg, task=dartCfg['task'], viscous=True)
             self.solver = self.argOut.get('sol') # Solver
             self.blw = [self.argOut.get('blwb'), self.argOut.get('blww')]
-            print('Dart successfully loaded.')
+            if dartCfg['task'] == 'optimization':
+                self.adjointSolver = self.argOut.get('adj')
+                print('Dart successfully loaded in optimization mode.')
+            else:
+                print('Dart successfully loaded in analysis mode.')
         except:
             print(ccolors.ANSI_RED, 'Could not load DART. Make sure it is installed.', ccolors.ANSI_RESET)
             raise RuntimeError('Import error')
diff --git a/blast/src/DCoupledAdjoint.cpp b/blast/src/DCoupledAdjoint.cpp
new file mode 100644
index 0000000..34db14b
--- /dev/null
+++ b/blast/src/DCoupledAdjoint.cpp
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#include "DCoupledAdjoint.h"
+#include "../../modules/dartflo/dart/src/wAdjoint.h"
+#include "../../modules/dartflo/dart/src/wNewton.h"
+#include "../../modules/dartflo/dart/src/wSolver.h"
+#include "../../modules/dartflo/dart/src/wProblem.h"
+#include "../../modules/dartflo/dart/src/wBlowing.h"
+#include "../../modules/dartflo/dart/src/wFluid.h"
+#include "wMshData.h"
+#include "wNode.h"
+
+#include "wLinearSolver.h"
+#include "wGmres.h"
+
+#include <Eigen/Sparse>
+#include <deque>
+#include <algorithm>
+
+using namespace blast;
+
+CoupledAdjoint::CoupledAdjoint(std::shared_ptr<dart::Adjoint> _iadjoint, std::shared_ptr<blast::Driver> vSolver) : iadjoint(_iadjoint), vsol(vSolver)
+{
+    isol = iadjoint->sol;
+    // Linear solvers (if GMRES, use the same, but with a thighter tolerance)
+    if (isol->linsol->type() == tbox::LSolType::GMRES_ILUT)
+    {
+        std::shared_ptr<tbox::Gmres> gmres = std::make_shared<tbox::Gmres>(*dynamic_cast<tbox::Gmres *>(isol->linsol.get()));
+        gmres->setTolerance(1e-8);
+        alinsol = gmres;
+    }
+    else
+        alinsol = isol->linsol;
+    
+    sizeBlowing = iadjoint->getSizeBlowing();
+    
+    dCldAoA = 0.0;
+    dCddAoA = 0.0;
+}
+
+void CoupledAdjoint::buildAdjointMatrix(std::vector<std::vector<Eigen::SparseMatrix<double, Eigen::RowMajor>*>> &matrices, Eigen::SparseMatrix<double, Eigen::RowMajor> &matrixAdjoint)
+{
+    // Determine the sizes of the smaller matrices
+    int rows = matrixAdjoint.rows();
+    int cols = matrixAdjoint.cols();
+
+    // Create a list of triplets for the larger matrix
+    std::vector<Eigen::Triplet<double>> triplets;
+
+    // Iterate over the rows and columns of the vector
+    int rowOffset = 0;
+    for (const auto& row : matrices) {
+        int colOffset = 0;
+        for (const auto& matrix : row) {
+            // Add the triplets of the matrix to the list of triplets for the larger matrix
+            for (int k = 0; k < matrix->outerSize(); ++k) {
+                for (Eigen::SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(*matrix, k); it; ++it) {
+                    triplets.push_back(Eigen::Triplet<double>(it.row() + rowOffset, it.col() + colOffset, it.value()));
+                }
+            }
+            colOffset += matrix->cols();
+        }
+        rowOffset += row[0]->rows();
+    }
+
+    // Create a new matrix from the deque of triplets
+    matrixAdjoint.setFromTriplets(triplets.begin(), triplets.end());
+}
+
+void CoupledAdjoint::run()
+{   
+    Eigen::SparseMatrix<double, Eigen::RowMajor> A = Eigen::SparseMatrix<double, Eigen::RowMajor>(3,3);
+    Eigen::SparseMatrix<double, Eigen::RowMajor> B = Eigen::SparseMatrix<double, Eigen::RowMajor>(3,3);
+    Eigen::SparseMatrix<double, Eigen::RowMajor> C = Eigen::SparseMatrix<double, Eigen::RowMajor>(3,3);
+    Eigen::SparseMatrix<double, Eigen::RowMajor> D = Eigen::SparseMatrix<double, Eigen::RowMajor>(3,3);
+    Eigen::SparseMatrix<double, Eigen::RowMajor> E = Eigen::SparseMatrix<double, Eigen::RowMajor>(3,3);
+    Eigen::SparseMatrix<double, Eigen::RowMajor> F = Eigen::SparseMatrix<double, Eigen::RowMajor>(3,3);
+    Eigen::SparseMatrix<double, Eigen::RowMajor> G = Eigen::SparseMatrix<double, Eigen::RowMajor>(3,3);
+    Eigen::SparseMatrix<double, Eigen::RowMajor> H = Eigen::SparseMatrix<double, Eigen::RowMajor>(3,3);
+    Eigen::SparseMatrix<double, Eigen::RowMajor> I = Eigen::SparseMatrix<double, Eigen::RowMajor>(3,3);
+
+    std::deque<Eigen::Triplet<double>> T;
+    T.push_back(Eigen::Triplet<double>(0, 0, 1.0));
+    T.push_back(Eigen::Triplet<double>(1, 1, 2.0));
+    A.setFromTriplets(T.begin(), T.end());
+    //std::cout <<  "A\n" <<A << std::endl;
+    T.clear();
+    T.push_back(Eigen::Triplet<double>(0, 0, 3.0));
+    T.push_back(Eigen::Triplet<double>(0, 1, 2.0));
+    B.setFromTriplets(T.begin(), T.end());
+    //std::cout <<  "B\n" <<B << std::endl;
+    /*T.clear();
+    T.push_back(Eigen::Triplet<double>(0, 0, 0.0));
+    T.push_back(Eigen::Triplet<double>(1, 1, 0.0));
+    C.setFromTriplets(T.begin(), T.end());*/
+    std::cout << "C\n" << C << std::endl;
+    T.clear();
+    T.push_back(Eigen::Triplet<double>(0, 0, 2.0));
+    T.push_back(Eigen::Triplet<double>(2, 1, 1.0));
+    D.setFromTriplets(T.begin(), T.end());
+    //std::cout << "D\n" << D << std::endl;
+
+    T.clear();
+    T.push_back(Eigen::Triplet<double>(0, 0, 2.0));
+    T.push_back(Eigen::Triplet<double>(1, 1, 1.0));
+    E.setFromTriplets(T.begin(), T.end());
+    //std::cout << "E\n" << E << std::endl;
+
+    T.clear();
+    T.push_back(Eigen::Triplet<double>(0, 0, 2.0));
+    T.push_back(Eigen::Triplet<double>(1, 1, 1.0));
+    F.setFromTriplets(T.begin(), T.end());
+    //std::cout << "F\n" << F << std::endl;
+
+    T.clear();
+    T.push_back(Eigen::Triplet<double>(0, 0, 2.0));
+    T.push_back(Eigen::Triplet<double>(1, 1, 4.0));
+    G.setFromTriplets(T.begin(), T.end());
+    //std::cout << "G\n" << G << std::endl;
+
+    T.clear();
+    T.push_back(Eigen::Triplet<double>(0, 0, 2.0));
+    T.push_back(Eigen::Triplet<double>(1, 1, 1.0));
+    H.setFromTriplets(T.begin(), T.end());
+    //std::cout << "H\n" << H << std::endl;
+
+    T.clear();
+    T.push_back(Eigen::Triplet<double>(0, 0, 5.0));
+    T.push_back(Eigen::Triplet<double>(1, 2, 1.0));
+    I.setFromTriplets(T.begin(), T.end());
+    //std::cout << "I\n" << I << std::endl;
+
+    std::cout << "gradientswrtInviscidFlow\n";
+    dRM_phi = Eigen::SparseMatrix<double, Eigen::RowMajor>(isol->pbl->bBCs[0]->nodes.size(), isol->pbl->msh->nodes.size());
+    dRrho_phi = Eigen::SparseMatrix<double, Eigen::RowMajor>(isol->pbl->bBCs[0]->nodes.size(), isol->pbl->msh->nodes.size());
+    gradientswrtInviscidFlow();
+    std::cout << " coucou " << std::endl;
+    std::cout << "dRM_phi\n" << dRM_phi.norm() << std::endl;
+    std::cout << "dRrho_phi\n" << dRrho_phi.norm() << std::endl;
+
+    // Store pointers to the matrices in a 2D vector
+    std::vector<std::vector<Eigen::SparseMatrix<double, Eigen::RowMajor>*>> matrices = {
+        {&A, &B, &C, &D, &E},
+        {&D, &E, &F, &I, &G},
+        {&G, &H, &I, &A, &B},
+        {&B, &E, &F, &I, &G},
+        {&B, &A, &I, &C, &B}
+    };
+
+    // Create a larger sparse matrix and set it from the list of triplets
+    int rows = 0; int cols = 0;
+    for (const auto& row : matrices) {
+        rows += row[0]->rows();  // All matrices in a row have the same number of rows
+        cols = std::max(cols, static_cast<int>(row.size() * row[0]->cols()));  // All matrices in a column have the same number of columns
+    }
+    Eigen::SparseMatrix<double, Eigen::RowMajor> A_adjoint(rows, cols);
+
+    buildAdjointMatrix(matrices, A_adjoint);
+
+    std::cout << "largeMatrix\n" << A_adjoint << std::endl;
+
+    // Solve coupled system
+    // alinsol->compute(AdjointMatrix.transpose(), Eigen::Map<Eigen::VectorXd>(dU_.data(), dU_.size()), lambdas);
+}
+
+void CoupledAdjoint::gradientswrtInviscidFlow()
+{
+    //**** dRphi_phi ****//
+    dRphi_phi = iadjoint->getRu_U();
+
+    //**** dRM_phi, dRrho_phi, dRv_phi ****//
+    auto pbl = isol->pbl;
+    //dRM_dPhi = iadjoint->getRM_U();
+    // Finite difference
+    double deltaPhi = 1e-4; // perturbation 
+    std::vector<double> Phi_save = isol->phi; // Differential of the independant variable (phi)
+    std::vector<double> dM = std::vector<double>(pbl->bBCs[0]->nodes.size(), 0.0); // Differential of the Mach number
+    std::vector<double> drho = std::vector<double>(pbl->bBCs[0]->nodes.size(), 0.0); // Differential of the density
+
+    std::vector<Eigen::Triplet<double>> tripletsdM;
+    std::vector<Eigen::Triplet<double>> tripletsdrho;
+
+    for (auto n : pbl->msh->nodes){
+        Phi_save[n->row] = isol->phi[n->row] + deltaPhi;
+
+        std::fill(dM.begin(), dM.end(), 0.0);
+        std::fill(drho.begin(), drho.end(), 0.0);
+
+        // Recompute Mach number on surface nodes.
+        size_t jj = 0;
+        for (auto srfNode : pbl->bBCs[0]->nodes){
+            // Average of the Mach on the elements adjacent to the considered node.
+            for (auto e : pbl->fluid->neMap[srfNode]){
+                dM[jj] += pbl->fluid->mach->eval(*e, Phi_save, 0);
+                drho[jj] += pbl->fluid->rho->eval(*e, Phi_save, 0);
+            }
+            dM[jj] /= pbl->fluid->neMap[srfNode].size();
+            drho[jj] /= pbl->fluid->neMap[srfNode].size();
+            // Form triplets
+            tripletsdM.push_back(Eigen::Triplet<double>(jj, n->row, (dM[jj] - isol->M[srfNode->row])/deltaPhi));
+            tripletsdrho.push_back(Eigen::Triplet<double>(jj, n->row, (drho[jj] - isol->rho[srfNode->row])/deltaPhi));
+            jj++;
+        }
+        // Reset phi
+        Phi_save[n->row] = isol->phi[n->row];
+    }
+
+    // Fill matrices with gradients
+    dRM_phi.setFromTriplets(tripletsdM.begin(), tripletsdM.end());
+    dRrho_phi.setFromTriplets(tripletsdrho.begin(), tripletsdrho.end());
+    // Remove zeros
+    dRM_phi.prune(0.);
+    dRrho_phi.prune(0.);
+
+    // // Objective functionss
+    // dCl_dPhi = 
+    // dCd_dPhi = 
+}
+
+void CoupledAdjoint::gradientswrtMachNumber(){
+    // dRphi_dM = 0
+    // dRM_dM = 
+    // dRrho_dM = 0
+    // dRv_dM = 0
+    // dRblw_dM = 
+    
+    // // Objective functions
+    // dCl_dM = 
+    // dCd_dM = 
+}
+
+void CoupledAdjoint::gradientswrtDensity(){
+    // dRphi_dRho = 0
+    // dRM_dRho = 0
+    // dRrho_dRho = 
+    // dRv_dRho = 0
+    // dRblw_dRho =
+
+    // dCl_dRho = 
+    // dCd_dRho = 
+}
+
+void CoupledAdjoint::gradientswrtVelocity(){
+    // dRphi_dV = 0
+    // dRM_dV = 0
+    // dRrho_dV = 0
+    // dRv_dV = 
+    // dRblw_dV = 
+
+    // dCl_dV = 
+    // dCd_dV = 
+}
+
+void CoupledAdjoint::gradientswrtBlowingVelocity(){
+    // dRphi_dBlw = 
+    // dRM_dBlw = 0
+    // dRrho_dBlw = 0
+    // dRv_dBlw = 0
+    // dRblw_dBlw = 
+
+    // dCl_dBlw = 
+    // dCd_dBlw = 
+}
\ No newline at end of file
diff --git a/blast/src/DCoupledAdjoint.h b/blast/src/DCoupledAdjoint.h
new file mode 100644
index 0000000..73aa0b5
--- /dev/null
+++ b/blast/src/DCoupledAdjoint.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#ifndef DCOUPLEDADJOINT_H
+#define DCOUPLEDADJOINT_H
+
+#include "blast.h"
+#include "wObject.h"
+#include "../../modules/dartflo/dart/src/wAdjoint.h"
+#include <Eigen/Sparse>
+
+#include <iostream>
+
+namespace blast
+{
+
+/**
+ * @brief Adjoint of the coupled system.
+ * @author Paul Dechamps
+ */
+class BLAST_API CoupledAdjoint : public fwk::wSharedObject
+{
+
+public:
+    std::shared_ptr<dart::Adjoint> iadjoint;     ///< Adjoint solver
+    std::shared_ptr<dart::Newton> isol;          ///< Inviscid Newton solver
+    std::shared_ptr<blast::Driver> vsol;         ///< Viscous solver
+    std::shared_ptr<tbox::LinearSolver> alinsol; ///< linear solver (adjoint aero)
+    double dCldAoA;
+    double dCddAoA;
+
+private:
+    size_t sizeBlowing;
+
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRphi_phi; ///< Inviscid flow Jacobian
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRphi_M;   ///< Derivative of the inviscid flow residual with respect to the Mach number
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRphi_v;   ///< Derivative of the inviscid flow residual with respect to the velocity
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRphi_rho; ///< Derivative of the inviscid flow residual with respect to the density
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRphi_blw; ///< Derivative of the inviscid flow residual with respect to the blowing velocity
+
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRM_phi; ///< Derivative of the Mach number residual with respect to the inviscid flow
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRM_M;   ///< Derivative of the Mach number residual with respect to the Mach number
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRM_v;   ///< Derivative of the Mach number residual with respect to the velocity
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRM_rho; ///< Derivative of the Mach number residual with respect to the density
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRM_blw; ///< Derivative of the Mach number residual with respect to the blowing velocity
+
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRrho_phi; ///< Derivative of the density residual with respect to the inviscid flow
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRrho_M;   ///< Derivative of the density residual with respect to the Mach number
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRrho_v;   ///< Derivative of the density residual with respect to the velocity
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRrho_rho; ///< Derivative of the density residual with respect to the density
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRrho_blw; ///< Derivative of the density residual with respect to the blowing velocity
+
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRv_phi; ///< Derivative of the velocity residual with respect to the inviscid flow
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRv_M;   ///< Derivative of the velocity residual with respect to the Mach number
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRv_v;   ///< Derivative of the velocity residual with respect to the velocity
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRv_rho; ///< Derivative of the velocity residual with respect to the density
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRv_blw; ///< Derivative of the velocity residual with respect to the blowing velocity
+
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRblw_phi; ///< Derivative of the blowing velocity residual with respect to the inviscid flow
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRblw_M;   ///< Derivative of the blowing velocity residual with respect to the Mach number
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRblw_v;   ///< Derivative of the blowing velocity residual with respect to the velocity
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRblw_rho; ///< Derivative of the blowing velocity residual with respect to the density
+    Eigen::SparseMatrix<double, Eigen::RowMajor> dRblw_blw; ///< Derivative of the blowing velocity residual with respect to the blowing velocity
+
+public:
+    CoupledAdjoint(std::shared_ptr<dart::Adjoint> iAdjoint, std::shared_ptr<blast::Driver> vSolver);
+    virtual ~CoupledAdjoint () {std::cout << "~CoupledAdjoint()\n";};
+
+    void run();
+
+private:
+    void buildAdjointMatrix(std::vector<std::vector<Eigen::SparseMatrix<double, Eigen::RowMajor>*>> &matrices, Eigen::SparseMatrix<double, Eigen::RowMajor> &matrixAdjoint);
+
+    void gradientswrtInviscidFlow();
+    void gradientswrtMachNumber();
+    void gradientswrtDensity();
+    void gradientswrtVelocity();
+    void gradientswrtBlowingVelocity();
+};
+} // namespace blast
+#endif // DDARTADJOINT
diff --git a/blast/src/blast.h b/blast/src/blast.h
index 71cdab8..6b166f5 100644
--- a/blast/src/blast.h
+++ b/blast/src/blast.h
@@ -48,6 +48,9 @@ class Edge;
 
 // Other
 class Closures;
+
+// Adjoint
+class CoupledAdjoint;
 } // namespace blast
 
 #endif // BLAST_H
diff --git a/blast/tests/dart/adjointVII.py b/blast/tests/dart/adjointVII.py
new file mode 100644
index 0000000..6b2f8bb
--- /dev/null
+++ b/blast/tests/dart/adjointVII.py
@@ -0,0 +1,218 @@
+#!/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 blast
+import numpy as np
+
+from fwk.wutils import parseargs
+import fwk
+from fwk.testing import *
+from fwk.coloring import ccolors
+
+import math
+
+def cfgInviscid(nthrds, verb):
+    import os.path
+    # 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.abspath(__file__)) + '/../../models/dart/n0012.geo', # Input file containing the model
+    'Pars' : {'xLgt' : 5, 'yLgt' : 5, 'msF' : 2, 'msTe' : 0.05, 'msLe' : 0.05}, # parameters for input file model
+    'Dim' : 2, # problem dimension
+    'Format' : 'gmsh', # 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)
+    'Wing' : 'airfoil', # LIST of names of physical groups containing the lifting surface boundary
+    'Wake' : 'wake', # LIST of names of physical group containing the wake
+    'WakeTip' : 'wakeTip', # LIST of names of physical group containing the edge of the wake
+    'Te' : 'te', # LIST of names of physical group containing the trailing edge
+    'dbc' : True,
+    'Upstream' : 'upstream',
+    # Freestream
+    'M_inf' : 0.2, # freestream Mach number
+    'AoA' : 2., # freestream angle of attack
+    # Geometry
+    'S_ref' : 1., # reference surface length
+    'c_ref' : 1., # reference chord length
+    'x_ref' : .25, # 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' : 20, # solver maximum number of iterations
+
+    'task': 'optimization'
+    }
+
+def cfgBlast(verb):
+    return {
+        'Re' : 1e7,       # Freestream Reynolds number
+        'Minf' : 0.2,     # Freestream Mach number (used for the computation of the time step only)
+        'CFL0' : 1,         # Inital CFL number of the calculation
+        'Verb': verb,       # Verbosity level of the solver
+        'couplIter': 25,    # Maximum number of iterations
+        'couplTol' : 1e-4,  # Tolerance of the VII methodology
+        'iterPrint': 5,     # int, number of iterations between outputs
+        'resetInv' : True,  # bool, flag to reset the inviscid calculation at every iteration.
+        'sections' : [0],
+        'xtrF' : [None, 0.4],# Forced transition location
+        'interpolator' : 'Matching',
+        'nDim' : 2
+    }
+
+def main():
+    # Timer.
+    tms = fwk.Timers()
+    tms['total'].start()
+
+    args = parseargs()
+    icfg = cfgInviscid(args.k, args.verb)
+    vcfg = cfgBlast(args.verb)
+
+    tms['pre'].start()
+    coupler, iSolverAPI, vSolver = viscUtils.initBlast(icfg, vcfg)
+    tms['pre'].stop()
+
+    adjointSolver = blast.CoupledAdjoint(iSolverAPI.adjointSolver, vSolver)
+
+    print(ccolors.ANSI_BLUE + 'PySolving...' + ccolors.ANSI_RESET)
+    tms['solver'].start()
+    aeroCoeffs = coupler.run()
+    #iSolverAPI.run()
+
+    adjointSolver.run()
+    print("sucess", adjointSolver)
+
+    quit()
+
+    args = parseargs()
+    icfg = cfgInviscid(args.k, args.verb)
+    vcfg = cfgBlast(args.verb)
+
+    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()
+    iSolverAPI.run()
+    quit()
+    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)
+    vSolution['Cdt_int'] = vSolution['Cdf'] + iSolverAPI.getCd()
+    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.230, 5e-2)) # XFOIL 0.2325
+    tests.add(CTest('Cd wake', vSolution['Cdt_w'], 0.0056, 1e-3, forceabs=True)) # XFOIL 0.00531
+    tests.add(CTest('Cd integral', vSolution['Cdt_int'], 0.0059, 1e-3, forceabs=True)) # XFOIL 0.00531
+    tests.add(CTest('Cdf', vSolution['Cdf'], 0.00465, 1e-3, forceabs=True)) # XFOIL 0.00084, Cdf = 0.00447
+    tests.add(CTest('xtr_top', vSolution['xtrT'], 0.20, 0.03, forceabs=True)) # XFOIL 0.1877
+    tests.add(CTest('xtr_bot', vSolution['xtrB'], 0.40, 0.01, forceabs=True)) # XFOIL 0.4998
+    tests.add(CTest('Iterations', len(aeroCoeffs), 17, 0, forceabs=True))
+    tests.run()
+
+    # Show results
+    if not args.nogui:
+        iCp = viscUtils.read('Cp_inviscid.dat')
+        vCp = viscUtils.read('Cp_viscous.dat')
+        xfoilCp = viscUtils.read('../../blast/models/references/cpXfoil_n0012.dat', delim=None, skip = 1)
+        xfoilCpInv = viscUtils.read('../../blast/models/references/cpXfoilInv_n0012.dat', delim=None, skip = 1)
+        plotcp = {'curves': [np.column_stack((vCp[:,0], vCp[:,3])),
+                            np.column_stack((xfoilCp[:,0], xfoilCp[:,1])),
+                            np.column_stack((iCp[:,0], iCp[:,3])),
+                            np.column_stack((xfoilCpInv[:,0], xfoilCpInv[:,1]))],
+                    'labels': ['Blast (VII)', 'XFOIL VII', 'DART (inviscid)', 'XFOIL (inviscid)'],
+                    'lw': [3, 3, 2, 2],
+                    'color': ['firebrick', 'darkblue', 'firebrick', 'darkblue'],
+                    'ls': ['-', '-', '--', '--'],
+                    'reverse': True,
+                    'xlim':[0, 1],
+                    'yreverse': True,
+                    'legend': True,
+                    'xlabel': '$x/c$',
+                    'ylabel': '$c_p$'
+                    }
+        viscUtils.plot(plotcp)
+
+        xfoilBl = viscUtils.read('../../blast/models/references/blXfoil_n0012.dat', delim=None, skip = 1)
+        plotcf = {'curves': [np.column_stack((vSolution['x'], vSolution['Cf'])),
+                            np.column_stack((xfoilBl[:,1], xfoilBl[:,6]))],
+                'labels': ['Blast', 'XFOIL'],
+                'lw': [3, 3],
+                'color': ['firebrick', 'darkblue'],
+                'ls': ['-', '-'],
+                'reverse': True,
+                'xlim':[0, 1],
+                'legend': True,
+                'xlabel': '$x/c$',
+                'ylabel': '$c_f$'
+                    }
+        viscUtils.plot(plotcf)
+
+    # eof
+    print('')
+
+if __name__ == "__main__":
+    main()
-- 
GitLab