From d5e7156ace68c081bf57bbf7f01432a648a01b6e Mon Sep 17 00:00:00 2001
From: acrovato <a.crovato@uliege.be>
Date: Tue, 7 Jul 2020 16:16:13 +0200
Subject: [PATCH] Implement Wake

---
 fpm/_src/fpmw.i      |  1 +
 fpm/models/n0012.geo |  5 ++-
 fpm/src/fBody.cpp    |  7 ----
 fpm/src/fBody.h      |  1 -
 fpm/src/fEdge.h      | 93 ++++++++++++++++++++++++++++++++++++++++++++
 fpm/src/fLifting.cpp | 68 +++++++++++++++++++++++++++-----
 fpm/src/fLifting.h   |  3 +-
 fpm/src/fProblem.cpp |  4 +-
 fpm/src/fWake.cpp    | 56 +++++++++++++++++++++++++-
 fpm/src/fWake.h      | 10 +++--
 fpm/src/fpm.h        |  4 ++
 fpm/tests/basic.py   |  2 +-
 12 files changed, 225 insertions(+), 29 deletions(-)
 create mode 100644 fpm/src/fEdge.h

diff --git a/fpm/_src/fpmw.i b/fpm/_src/fpmw.i
index d3a9efb..fdb1271 100644
--- a/fpm/_src/fpmw.i
+++ b/fpm/_src/fpmw.i
@@ -72,6 +72,7 @@ def __getitem__(self, name):
 %shared_ptr(fpm::Lifting);
 %immutable fpm::Lifting::wake; // avoids the creation of the setter method
 %include "fLifting.h"
+%shared_ptr(fpm::Wake);
 %include "fWake.h"
 
 %shared_ptr(fpm::Problem);
diff --git a/fpm/models/n0012.geo b/fpm/models/n0012.geo
index 4d69a59..9a0f91d 100644
--- a/fpm/models/n0012.geo
+++ b/fpm/models/n0012.geo
@@ -241,8 +241,8 @@ Surface(2) = {-2};
 /// Lines
 
 // Wing:
-Transfinite Line{1, 2, 5, 6} = nC Using Bump 0.05;
-Transfinite Line{61, 62} = nS;
+Transfinite Line{1, 2, 5, 6} = nC + 1 Using Bump 0.05;
+Transfinite Line{61, 62} = nS + 1;
 
 /// Surfaces
 Transfinite Surface{1, 2};
@@ -251,4 +251,5 @@ Recombine Surface{1, 2};
 //// PHYSICAL GROUPS
 
 // Wing:
+Physical Line("te") = {61};
 Physical Surface("wing") = {1,2};
diff --git a/fpm/src/fBody.cpp b/fpm/src/fBody.cpp
index b890528..b09b556 100644
--- a/fpm/src/fBody.cpp
+++ b/fpm/src/fBody.cpp
@@ -21,13 +21,6 @@
 using namespace tbox;
 using namespace fpm;
 
-Body::Body(std::shared_ptr<MshData> _msh, int no) : Group(_msh, no), Cl(0), Cd(0), Cs(0), Cm(0)
-{
-    cLoadX.resize(tag->elems.size());
-    cLoadY.resize(tag->elems.size());
-    cLoadZ.resize(tag->elems.size());
-}
-
 Body::Body(std::shared_ptr<MshData> _msh, std::string const &name) : Group(_msh, name), Cl(0), Cd(0), Cs(0), Cm(0)
 {
     cLoadX.resize(tag->elems.size());
diff --git a/fpm/src/fBody.h b/fpm/src/fBody.h
index 05096ba..8d49a02 100644
--- a/fpm/src/fBody.h
+++ b/fpm/src/fBody.h
@@ -39,7 +39,6 @@ public:
     std::vector<double> cLoadY; ///< y-component of aerodynamic load coefficient on boundary
     std::vector<double> cLoadZ; ///< z-component of aerodynamic load coefficient on boundary
 
-    Body(std::shared_ptr<tbox::MshData> _msh, int no);
     Body(std::shared_ptr<tbox::MshData> _msh, std::string const &name);
     ~Body() { std::cout << "~Body()\n"; }
 
diff --git a/fpm/src/fEdge.h b/fpm/src/fEdge.h
new file mode 100644
index 0000000..959675c
--- /dev/null
+++ b/fpm/src/fEdge.h
@@ -0,0 +1,93 @@
+/*
+ * 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 FEDGE_H
+#define FEDGE_H
+
+#include "fpm.h"
+#include "wNode.h"
+
+#ifndef SWIG
+namespace fpm
+{
+
+/**
+ * @brief Common edge of three elements
+ * @authors Adrien Crovato
+ * @todo copy-pasted from waves/flow/wFace.h
+ */
+class FPM_API Edge
+{
+public:
+    std::vector<tbox::Node *> nods;
+    tbox::Element *el0;
+    tbox::Element *el1;
+    tbox::Element *el2;
+
+    Edge(std::vector<tbox::Node *> const &_nods) : nods(_nods), el0(NULL), el1(NULL), el2(NULL) {}
+};
+
+/**
+ * @brief Edge comparator
+ * @authors Adrien Crovato, Romain Boman
+ * @todo copy-pasted from waves/flow/wFace.h
+ */
+class FPM_API EquEdge
+{
+public:
+    // Default parameters
+    static const size_t bucket_size = 4;
+    static const size_t min_buckets = 8;
+    // Function returning a unique size_t per Edge
+    size_t operator()(Edge *const f) const
+    {
+        size_t sum = 0;
+        for (auto n : f->nods)
+            sum += static_cast<size_t>(n->no);
+
+        return sum; // sum of nodes id
+    }
+    // Function allowing to univoquely identify a Edge
+    bool operator()(Edge *const f0, Edge *const f1) const
+    {
+        bool flag = false;
+        size_t cnt = 0;
+        // compare nodes of f0 to nodes of f1
+        for (auto n0 : f0->nods)
+        {
+            for (auto n1 : f1->nods)
+            {
+                if (n0 == n1)
+                {
+                    cnt++;
+                    break;
+                }
+            }
+            if (cnt == 0)
+                break;
+        }
+        // check the number of shared nodes
+        if (cnt == f0->nods.size())
+            flag = true;
+
+        return flag; // true if f0 = f1 (ie, f0 and f1 share the same nodes)
+    }
+};
+
+} // namespace fpm
+#endif
+
+#endif //FEDGE_H
diff --git a/fpm/src/fLifting.cpp b/fpm/src/fLifting.cpp
index 4c018ae..63d51ed 100644
--- a/fpm/src/fLifting.cpp
+++ b/fpm/src/fLifting.cpp
@@ -16,20 +16,69 @@
 
 #include "fLifting.h"
 #include "fWake.h"
+#include "wMshData.h"
+#include "wGmshExport.h"
 #include "wTag.h"
-#include <iomanip>
-#include <fstream>
+#include "wElement.h"
+#include "wQuad4.h"
+#include "wNode.h"
 using namespace tbox;
 using namespace fpm;
 
-Lifting::Lifting(std::shared_ptr<MshData> _msh, int no) : Body(_msh, no)
+Lifting::Lifting(std::shared_ptr<MshData> _msh, std::vector<std::string> const &names, double xF) : Body(_msh, names[0])
 {
-    wake = new Wake();
-}
+    // Sanity check
+    if (names.size() != 2)
+    {
+        std::stringstream err;
+        err << "fpm::Lifting should be built with 2 physical groups but " << names.size() << " were given!\n";
+        throw std::runtime_error(err.str());
+    }
 
-Lifting::Lifting(std::shared_ptr<MshData> _msh, std::string const &name) : Body(_msh, name)
-{
-    wake = new Wake();
+    // Check if wake is already available and create it otherwise
+    std::cout << "Creating wake... " << std::flush;
+    try
+    {
+        wake = new Wake(_msh, names[0] + "Wake", tag);
+        std::cout << *wake << " already existing, nothing done." << std::endl;
+    }
+    catch (const std::out_of_range &)
+    {
+        // Create tags
+        Tag *tagp = new Tag(static_cast<int>(msh->ptags.rbegin()->first + 1), tag->name + "Wake", 2);
+        Tag *tage = new Tag(static_cast<int>(msh->etags.rbegin()->first + 1), tag->name + "Wake", 2);
+        msh->ntags[tag->name + "Wake"] = tagp;
+        msh->ptags[static_cast<int>(msh->ptags.rbegin()->first + 1)] = tagp;
+        msh->etags[static_cast<int>(msh->etags.rbegin()->first + 1)] = tage;
+        // Sort TE nodes (ascending along y-coordinate)
+        Group te(_msh, names[1]);
+        std::vector<Node *> teNodes;
+        for (auto e : te.tag->elems)
+            for (auto n : e->nodes)
+                teNodes.push_back(n);
+        std::sort(teNodes.begin(), teNodes.end(), [](Node *a, Node *b) -> bool { return a->pos(1) < b->pos(1); });
+        teNodes.erase(std::unique(teNodes.begin(), teNodes.end()), teNodes.end());
+        // Translate TE nodes (along x-coordinate)
+        std::vector<Node *> wkNodes;
+        for (auto n : teNodes)
+        {
+            Node *nN = new Node(msh->nodes.back()->no + 1, Eigen::Vector3d(xF, n->pos(1), n->pos(2)));
+            wkNodes.push_back(nN);
+            msh->nodes.push_back(nN);
+        }
+        // Create wake elements
+        for (size_t i = 0; i < wkNodes.size() - 1; ++i)
+        {
+            std::vector<Node *> qnodes = {teNodes[i], wkNodes[i], wkNodes[i + 1], teNodes[i + 1]};
+            msh->elems.push_back(new Quad4(msh->elems.back()->no + 1, tagp, tage, tag->elems[0]->parts, qnodes));
+        }
+
+        // Create wake and save modified mesh
+        wake = new Wake(_msh, names[0] + "Wake", tag);
+        std::cout << *wake << " created. " << std::flush;
+        GmshExport writer(_msh);
+        writer.save(_msh->name);
+    }
 }
 
 Lifting::~Lifting()
@@ -40,5 +89,6 @@ Lifting::~Lifting()
 
 void Lifting::write(std::ostream &out) const
 {
-    out << "fpm::Lifting " << *tag << std::endl;
+    out << "fpm::Lifting " << *tag
+        << " with " << *wake << std::endl;
 }
diff --git a/fpm/src/fLifting.h b/fpm/src/fLifting.h
index e8439f0..2cfedc7 100644
--- a/fpm/src/fLifting.h
+++ b/fpm/src/fLifting.h
@@ -33,8 +33,7 @@ class FPM_API Lifting : public Body
 public:
     Wake *wake; ///< wake attached to the lifting surface
 
-    Lifting(std::shared_ptr<tbox::MshData> _msh, int no);
-    Lifting(std::shared_ptr<tbox::MshData> _msh, std::string const &name);
+    Lifting(std::shared_ptr<tbox::MshData> _msh, std::vector<std::string> const &names, double xF);
     ~Lifting();
 
 #ifndef SWIG
diff --git a/fpm/src/fProblem.cpp b/fpm/src/fProblem.cpp
index fb67f53..6c2e349 100644
--- a/fpm/src/fProblem.cpp
+++ b/fpm/src/fProblem.cpp
@@ -103,7 +103,7 @@ void Problem::write(std::ostream &out) const
         << std::endl;
     out << "with";
     for (auto b : bodies)
-        out << "\n\t" << *b;
+        out << "\t" << *b;
     for (auto l : liftings)
-        out << "\n\t" << *l;
+        out << "\t" << *l;
 }
diff --git a/fpm/src/fWake.cpp b/fpm/src/fWake.cpp
index 02aed26..2b95ce7 100644
--- a/fpm/src/fWake.cpp
+++ b/fpm/src/fWake.cpp
@@ -15,13 +15,65 @@
  */
 
 #include "fWake.h"
+#include "fEdge.h"
+#include "wNode.h"
+#include "wElement.h"
+#include <vector>
+#include <unordered_set>
+using namespace tbox;
 using namespace fpm;
 
-Wake::Wake() : fwk::wObject()
+Wake::Wake(std::shared_ptr<MshData> _msh, std::string const &name, Tag *const &wTag) : Group(_msh, name)
 {
+    // Create set of TE edges
+    std::unordered_set<Edge *, EquEdge, EquEdge> teEdges;
+    for (auto e : tag->elems)
+    {
+        Edge *ed = new Edge({e->nodes[0], e->nodes[3]});
+        ed->el0 = e;
+        teEdges.insert(ed);
+    }
+
+    // Find wing elements having an edge on the TE
+    for (auto e : wTag->elems)
+    {
+        size_t idx = 0;
+        for (size_t i = 0; i < e->nodes.size(); ++i)
+        {
+            // build wing edge
+            std::vector<Node *> eN(2);
+            for (size_t j = 0; j < 2; ++j)
+                eN[j] = e->nodes[(idx + j) % e->nodes.size()];
+            Edge ed(eN);
+            // check if wing edge is a TE edge
+            auto it = teEdges.find(&ed);
+            if (it != teEdges.end())
+            {
+                // check normal direction
+                if (e->normal().dot((*it)->el0->normal()) > 0)
+                    (*it)->el1 = e;
+                else
+                    (*it)->el2 = e;
+            }
+            idx++;
+        }
+    }
+
+    // Create a map between the wing and wake elements
+    for (auto ed : teEdges)
+    {
+        if (ed->el1 == NULL || ed->el2 == NULL)
+        {
+            std::stringstream err;
+            err << "fpm::Lifting: no wing element from tag " << *wTag << "could be associated to wake element " << *(ed->el0) << "!\n";
+            throw std::runtime_error(err.str());
+        }
+        wkMap[ed->el0] = std::pair<Element *, Element *>(ed->el1, ed->el2);
+        delete ed;
+    }
 }
 
 void Wake::write(std::ostream &out) const
 {
-    out << "fpm::Wake " << std::endl;
+    out << "fpm::Wake " << *tag << std::endl;
 }
diff --git a/fpm/src/fWake.h b/fpm/src/fWake.h
index f32d68e..6d725ac 100644
--- a/fpm/src/fWake.h
+++ b/fpm/src/fWake.h
@@ -18,7 +18,9 @@
 #define FWAKE_H
 
 #include "fpm.h"
-#include "wObject.h"
+#include "wGroup.h"
+#include "wTag.h"
+#include <map>
 
 namespace fpm
 {
@@ -27,10 +29,12 @@ namespace fpm
  * @brief Manage the wake attached to a lifting body
  * @authors Adrien Crovato
  */
-class FPM_API Wake : public fwk::wObject
+class FPM_API Wake : public tbox::Group
 {
 public:
-    Wake();
+    std::map<tbox::Element *, std::pair<tbox::Element *, tbox::Element *>> wkMap; ///< wing-wake map
+
+    Wake(std::shared_ptr<tbox::MshData> _msh, std::string const &name, tbox::Tag *const &wTag);
     ~Wake() {}
 
 #ifndef SWIG
diff --git a/fpm/src/fpm.h b/fpm/src/fpm.h
index 2eb1d20..0d681a8 100644
--- a/fpm/src/fpm.h
+++ b/fpm/src/fpm.h
@@ -44,6 +44,10 @@ class Body;
 class Lifting;
 class Wake;
 
+// misc
+class Edge;
+class EquEdge;
+
 } // namespace fpm
 
 #endif //FPM_H
diff --git a/fpm/tests/basic.py b/fpm/tests/basic.py
index 1a96b37..232d38f 100644
--- a/fpm/tests/basic.py
+++ b/fpm/tests/basic.py
@@ -36,7 +36,7 @@ def main():
     pbl = fpm.Problem(msh, 0, 0, 5., 1., 0., 0., 0.)
     fuselage = fpm.Body(msh, 'wing')
     pbl.add(fuselage)
-    wing = fpm.Lifting(msh, 'wing')
+    wing = fpm.Lifting(msh, ['wing', 'te'], 5)
     pbl.add(wing)
     print(pbl)
     # end timer
-- 
GitLab