From a9e42d7abc89c6d6bae318b8afc6757767cb19d0 Mon Sep 17 00:00:00 2001
From: acrovato <a.crovato@uliege.be>
Date: Mon, 6 Jul 2020 15:30:20 +0200
Subject: [PATCH] Add Problem, lifting and non-lifting Body, Wake. Add basic
 model + test.

---
 fpm/_src/fpmw.h      |   4 +
 fpm/_src/fpmw.i      |  13 ++-
 fpm/models/n0012.geo | 254 +++++++++++++++++++++++++++++++++++++++++++
 fpm/src/fBody.cpp    |  77 +++++++++++++
 fpm/src/fBody.h      |  55 ++++++++++
 fpm/src/fLifting.cpp |  44 ++++++++
 fpm/src/fLifting.h   |  47 ++++++++
 fpm/src/fProblem.cpp | 109 +++++++++++++++++++
 fpm/src/fProblem.h   |  62 +++++++++++
 fpm/src/fWake.cpp    |  27 +++++
 fpm/src/fWake.h      |  43 ++++++++
 fpm/src/fpm.h        |  10 +-
 fpm/tests/basic.py   |  57 ++++++++++
 13 files changed, 799 insertions(+), 3 deletions(-)
 create mode 100644 fpm/models/n0012.geo
 create mode 100644 fpm/src/fBody.cpp
 create mode 100644 fpm/src/fBody.h
 create mode 100644 fpm/src/fLifting.cpp
 create mode 100644 fpm/src/fLifting.h
 create mode 100644 fpm/src/fProblem.cpp
 create mode 100644 fpm/src/fProblem.h
 create mode 100644 fpm/src/fWake.cpp
 create mode 100644 fpm/src/fWake.h
 create mode 100644 fpm/tests/basic.py

diff --git a/fpm/_src/fpmw.h b/fpm/_src/fpmw.h
index ce160ec..53f2b18 100644
--- a/fpm/_src/fpmw.h
+++ b/fpm/_src/fpmw.h
@@ -14,4 +14,8 @@
  * limitations under the License.
  */
 
+#include "fBody.h"
+#include "fLifting.h"
+#include "fProblem.h"
 #include "fTimers.h"
+#include "fWake.h"
diff --git a/fpm/_src/fpmw.i b/fpm/_src/fpmw.i
index a9bb68b..d3a9efb 100644
--- a/fpm/_src/fpmw.i
+++ b/fpm/_src/fpmw.i
@@ -65,4 +65,15 @@ def __getitem__(self, name):
 }
 };
 
-//%include <std_shared_ptr.i>
+%include <std_shared_ptr.i>
+
+%shared_ptr(fpm::Body);
+%include "fBody.h"
+%shared_ptr(fpm::Lifting);
+%immutable fpm::Lifting::wake; // avoids the creation of the setter method
+%include "fLifting.h"
+%include "fWake.h"
+
+%shared_ptr(fpm::Problem);
+%immutable fpm::Problem::msh; // avoids the creation of the setter method
+%include "fProblem.h"
diff --git a/fpm/models/n0012.geo b/fpm/models/n0012.geo
new file mode 100644
index 0000000..4d69a59
--- /dev/null
+++ b/fpm/models/n0012.geo
@@ -0,0 +1,254 @@
+/* Rectangular NACA0012 wing */
+
+// Parameters
+// domain and mesh
+DefineConstant[ spn = { 2.0, Name "Wing span" }  ];
+DefineConstant[ nC = { 80, Name "Number of cells along chord" }  ];
+DefineConstant[ nS = { 10, Name "Number of cells along span" }  ];
+
+//// GEOMETRY
+
+/// Points
+
+// Airfoil 1: naca0012, 101 points 
+Point(1) = {1.000000,0.000000,0.000000};
+Point(2) = {0.999013,0.000000,0.000143};
+Point(3) = {0.996057,0.000000,0.000572};
+Point(4) = {0.991144,0.000000,0.001280};
+Point(5) = {0.984292,0.000000,0.002260};
+Point(6) = {0.975528,0.000000,0.003501};
+Point(7) = {0.964888,0.000000,0.004990};
+Point(8) = {0.952414,0.000000,0.006710};
+Point(9) = {0.938153,0.000000,0.008643};
+Point(10) = {0.922164,0.000000,0.010770};
+Point(11) = {0.904508,0.000000,0.013071};
+Point(12) = {0.885257,0.000000,0.015523};
+Point(13) = {0.864484,0.000000,0.018106};
+Point(14) = {0.842274,0.000000,0.020795};
+Point(15) = {0.818712,0.000000,0.023569};
+Point(16) = {0.793893,0.000000,0.026405};
+Point(17) = {0.767913,0.000000,0.029279};
+Point(18) = {0.740877,0.000000,0.032168};
+Point(19) = {0.712890,0.000000,0.035048};
+Point(20) = {0.684062,0.000000,0.037896};
+Point(21) = {0.654508,0.000000,0.040686};
+Point(22) = {0.624345,0.000000,0.043394};
+Point(23) = {0.593691,0.000000,0.045992};
+Point(24) = {0.562667,0.000000,0.048455};
+Point(25) = {0.531395,0.000000,0.050754};
+Point(26) = {0.500000,0.000000,0.052862};
+Point(27) = {0.468605,0.000000,0.054749};
+Point(28) = {0.437333,0.000000,0.056390};
+Point(29) = {0.406309,0.000000,0.057755};
+Point(30) = {0.375655,0.000000,0.058819};
+Point(31) = {0.345492,0.000000,0.059557};
+Point(32) = {0.315938,0.000000,0.059947};
+Point(33) = {0.287110,0.000000,0.059971};
+Point(34) = {0.259123,0.000000,0.059614};
+Point(35) = {0.232087,0.000000,0.058863};
+Point(36) = {0.206107,0.000000,0.057712};
+Point(37) = {0.181288,0.000000,0.056159};
+Point(38) = {0.157726,0.000000,0.054206};
+Point(39) = {0.135516,0.000000,0.051862};
+Point(40) = {0.114743,0.000000,0.049138};
+Point(41) = {0.095492,0.000000,0.046049};
+Point(42) = {0.077836,0.000000,0.042615};
+Point(43) = {0.061847,0.000000,0.038859};
+Point(44) = {0.047586,0.000000,0.034803};
+Point(45) = {0.035112,0.000000,0.030473};
+Point(46) = {0.024472,0.000000,0.025893};
+Point(47) = {0.015708,0.000000,0.021088};
+Point(48) = {0.008856,0.000000,0.016078};
+Point(49) = {0.003943,0.000000,0.010884};
+Point(50) = {0.000987,0.000000,0.005521};
+Point(51) = {0.000000,0.000000,0.000000};
+Point(52) = {0.000987,0.000000,-0.005521};
+Point(53) = {0.003943,0.000000,-0.010884};
+Point(54) = {0.008856,0.000000,-0.016078};
+Point(55) = {0.015708,0.000000,-0.021088};
+Point(56) = {0.024472,0.000000,-0.025893};
+Point(57) = {0.035112,0.000000,-0.030473};
+Point(58) = {0.047586,0.000000,-0.034803};
+Point(59) = {0.061847,0.000000,-0.038859};
+Point(60) = {0.077836,0.000000,-0.042615};
+Point(61) = {0.095492,0.000000,-0.046049};
+Point(62) = {0.114743,0.000000,-0.049138};
+Point(63) = {0.135516,0.000000,-0.051862};
+Point(64) = {0.157726,0.000000,-0.054206};
+Point(65) = {0.181288,0.000000,-0.056159};
+Point(66) = {0.206107,0.000000,-0.057712};
+Point(67) = {0.232087,0.000000,-0.058863};
+Point(68) = {0.259123,0.000000,-0.059614};
+Point(69) = {0.287110,0.000000,-0.059971};
+Point(70) = {0.315938,0.000000,-0.059947};
+Point(71) = {0.345492,0.000000,-0.059557};
+Point(72) = {0.375655,0.000000,-0.058819};
+Point(73) = {0.406309,0.000000,-0.057755};
+Point(74) = {0.437333,0.000000,-0.056390};
+Point(75) = {0.468605,0.000000,-0.054749};
+Point(76) = {0.500000,0.000000,-0.052862};
+Point(77) = {0.531395,0.000000,-0.050754};
+Point(78) = {0.562667,0.000000,-0.048455};
+Point(79) = {0.593691,0.000000,-0.045992};
+Point(80) = {0.624345,0.000000,-0.043394};
+Point(81) = {0.654508,0.000000,-0.040686};
+Point(82) = {0.684062,0.000000,-0.037896};
+Point(83) = {0.712890,0.000000,-0.035048};
+Point(84) = {0.740877,0.000000,-0.032168};
+Point(85) = {0.767913,0.000000,-0.029279};
+Point(86) = {0.793893,0.000000,-0.026405};
+Point(87) = {0.818712,0.000000,-0.023569};
+Point(88) = {0.842274,0.000000,-0.020795};
+Point(89) = {0.864484,0.000000,-0.018106};
+Point(90) = {0.885257,0.000000,-0.015523};
+Point(91) = {0.904508,0.000000,-0.013071};
+Point(92) = {0.922164,0.000000,-0.010770};
+Point(93) = {0.938153,0.000000,-0.008643};
+Point(94) = {0.952414,0.000000,-0.006710};
+Point(95) = {0.964888,0.000000,-0.004990};
+Point(96) = {0.975528,0.000000,-0.003501};
+Point(97) = {0.984292,0.000000,-0.002260};
+Point(98) = {0.991144,0.000000,-0.001280};
+Point(99) = {0.996057,0.000000,-0.000572};
+Point(100) = {0.999013,0.000000,-0.000143};
+
+// Airfoil 2: naca0012, 101 points 
+Point(102) = {1.000000,spn,0.000000};
+Point(103) = {0.999013,spn,0.000143};
+Point(104) = {0.996057,spn,0.000572};
+Point(105) = {0.991144,spn,0.001280};
+Point(106) = {0.984292,spn,0.002260};
+Point(107) = {0.975528,spn,0.003501};
+Point(108) = {0.964888,spn,0.004990};
+Point(109) = {0.952414,spn,0.006710};
+Point(110) = {0.938153,spn,0.008643};
+Point(111) = {0.922164,spn,0.010770};
+Point(112) = {0.904508,spn,0.013071};
+Point(113) = {0.885257,spn,0.015523};
+Point(114) = {0.864484,spn,0.018106};
+Point(115) = {0.842274,spn,0.020795};
+Point(116) = {0.818712,spn,0.023569};
+Point(117) = {0.793893,spn,0.026405};
+Point(118) = {0.767913,spn,0.029279};
+Point(119) = {0.740877,spn,0.032168};
+Point(120) = {0.712890,spn,0.035048};
+Point(121) = {0.684062,spn,0.037896};
+Point(122) = {0.654508,spn,0.040686};
+Point(123) = {0.624345,spn,0.043394};
+Point(124) = {0.593691,spn,0.045992};
+Point(125) = {0.562667,spn,0.048455};
+Point(126) = {0.531395,spn,0.050754};
+Point(127) = {0.500000,spn,0.052862};
+Point(128) = {0.468605,spn,0.054749};
+Point(129) = {0.437333,spn,0.056390};
+Point(130) = {0.406309,spn,0.057755};
+Point(131) = {0.375655,spn,0.058819};
+Point(132) = {0.345492,spn,0.059557};
+Point(133) = {0.315938,spn,0.059947};
+Point(134) = {0.287110,spn,0.059971};
+Point(135) = {0.259123,spn,0.059614};
+Point(136) = {0.232087,spn,0.058863};
+Point(137) = {0.206107,spn,0.057712};
+Point(138) = {0.181288,spn,0.056159};
+Point(139) = {0.157726,spn,0.054206};
+Point(140) = {0.135516,spn,0.051862};
+Point(141) = {0.114743,spn,0.049138};
+Point(142) = {0.095492,spn,0.046049};
+Point(143) = {0.077836,spn,0.042615};
+Point(144) = {0.061847,spn,0.038859};
+Point(145) = {0.047586,spn,0.034803};
+Point(146) = {0.035112,spn,0.030473};
+Point(147) = {0.024472,spn,0.025893};
+Point(148) = {0.015708,spn,0.021088};
+Point(149) = {0.008856,spn,0.016078};
+Point(150) = {0.003943,spn,0.010884};
+Point(151) = {0.000987,spn,0.005521};
+Point(152) = {0.000000,spn,0.000000};
+Point(153) = {0.000987,spn,-0.005521};
+Point(154) = {0.003943,spn,-0.010884};
+Point(155) = {0.008856,spn,-0.016078};
+Point(156) = {0.015708,spn,-0.021088};
+Point(157) = {0.024472,spn,-0.025893};
+Point(158) = {0.035112,spn,-0.030473};
+Point(159) = {0.047586,spn,-0.034803};
+Point(160) = {0.061847,spn,-0.038859};
+Point(161) = {0.077836,spn,-0.042615};
+Point(162) = {0.095492,spn,-0.046049};
+Point(163) = {0.114743,spn,-0.049138};
+Point(164) = {0.135516,spn,-0.051862};
+Point(165) = {0.157726,spn,-0.054206};
+Point(166) = {0.181288,spn,-0.056159};
+Point(167) = {0.206107,spn,-0.057712};
+Point(168) = {0.232087,spn,-0.058863};
+Point(169) = {0.259123,spn,-0.059614};
+Point(170) = {0.287110,spn,-0.059971};
+Point(171) = {0.315938,spn,-0.059947};
+Point(172) = {0.345492,spn,-0.059557};
+Point(173) = {0.375655,spn,-0.058819};
+Point(174) = {0.406309,spn,-0.057755};
+Point(175) = {0.437333,spn,-0.056390};
+Point(176) = {0.468605,spn,-0.054749};
+Point(177) = {0.500000,spn,-0.052862};
+Point(178) = {0.531395,spn,-0.050754};
+Point(179) = {0.562667,spn,-0.048455};
+Point(180) = {0.593691,spn,-0.045992};
+Point(181) = {0.624345,spn,-0.043394};
+Point(182) = {0.654508,spn,-0.040686};
+Point(183) = {0.684062,spn,-0.037896};
+Point(184) = {0.712890,spn,-0.035048};
+Point(185) = {0.740877,spn,-0.032168};
+Point(186) = {0.767913,spn,-0.029279};
+Point(187) = {0.793893,spn,-0.026405};
+Point(188) = {0.818712,spn,-0.023569};
+Point(189) = {0.842274,spn,-0.020795};
+Point(190) = {0.864484,spn,-0.018106};
+Point(191) = {0.885257,spn,-0.015523};
+Point(192) = {0.904508,spn,-0.013071};
+Point(193) = {0.922164,spn,-0.010770};
+Point(194) = {0.938153,spn,-0.008643};
+Point(195) = {0.952414,spn,-0.006710};
+Point(196) = {0.964888,spn,-0.004990};
+Point(197) = {0.975528,spn,-0.003501};
+Point(198) = {0.984292,spn,-0.002260};
+Point(199) = {0.991144,spn,-0.001280};
+Point(200) = {0.996057,spn,-0.000572};
+Point(201) = {0.999013,spn,-0.000143};
+
+/// Lines
+
+// Airfoil 1:
+Spline(1) = {1:51};
+Spline(2) = {51:100,1};
+
+// Airfoil 2:
+Spline(5) = {102:152};
+Spline(6) = {152:201,102};
+
+// Airfoil 1 to airfoil 2:
+Line(61) = {1,102};
+Line(62) = {51,152};
+
+/// Line loops & Surfaces
+
+// Wing:
+Line Loop(1) = {1,62,-5,-61};
+Line Loop(2) = {2,61,-6,-62};
+Surface(1) = {-1};
+Surface(2) = {-2};
+
+//// MESHING
+
+/// Lines
+
+// Wing:
+Transfinite Line{1, 2, 5, 6} = nC Using Bump 0.05;
+Transfinite Line{61, 62} = nS;
+
+/// Surfaces
+Transfinite Surface{1, 2};
+Recombine Surface{1, 2};
+
+//// PHYSICAL GROUPS
+
+// Wing:
+Physical Surface("wing") = {1,2};
diff --git a/fpm/src/fBody.cpp b/fpm/src/fBody.cpp
new file mode 100644
index 0000000..b890528
--- /dev/null
+++ b/fpm/src/fBody.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 "fBody.h"
+#include "wTag.h"
+#include <iomanip>
+#include <fstream>
+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());
+    cLoadY.resize(tag->elems.size());
+    cLoadZ.resize(tag->elems.size());
+}
+
+/**
+ * @brief Save global quantities to file
+ */
+void Body::save(std::string const &name)
+{
+    // Write to file
+    std::cout << "writing file: " << name + ".dat"
+              << "... " << std::flush;
+    std::ofstream outfile;
+    outfile.open(name + ".dat");
+    // Header
+    outfile << "$Body - " << tag->name << std::endl;
+    // Element count
+    outfile << "$Elements" << std::endl;
+    outfile << tag->elems.size();
+    // Aerodynamic coefficients
+    outfile << "$Aerodynamic coefficients" << std::endl;
+    outfile << "#" << std::fixed
+            << std::setw(14) << "Cl"
+            << std::setw(15) << "Cd"
+            << std::setw(15) << "Cs"
+            << std::setw(15) << "Cm"
+            << std::endl;
+    outfile << std::fixed
+            << std::setw(15) << Cl
+            << std::setw(15) << Cd
+            << std::setw(15) << Cs
+            << std::setw(15) << Cm
+            << std::endl;
+    // Footer
+    outfile << std::endl;
+    // Close file
+    outfile.close();
+    std::cout << "done!" << std::endl;
+}
+
+void Body::write(std::ostream &out) const
+{
+    out << "fpm::Body " << *tag << std::endl;
+}
diff --git a/fpm/src/fBody.h b/fpm/src/fBody.h
new file mode 100644
index 0000000..05096ba
--- /dev/null
+++ b/fpm/src/fBody.h
@@ -0,0 +1,55 @@
+/*
+ * 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 FBODY_H
+#define FBODY_H
+
+#include "fpm.h"
+#include "wGroup.h"
+#include <vector>
+
+namespace fpm
+{
+
+/**
+ * @brief Manage a body immersed in the fluid
+ * @authors Adrien Crovato
+ */
+class FPM_API Body : public tbox::Group
+{
+public:
+    double Cl;                  ///< lift coefficient
+    double Cd;                  ///< drag coefficient
+    double Cs;                  ///< sideforce coefficient
+    double Cm;                  ///< pitch moment coefficient (positive nose-up)
+    std::vector<double> cLoadX; ///< x-component of aerodynamic load coefficient on boundary
+    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"; }
+
+    virtual void save(std::string const &name);
+
+#ifndef SWIG
+    virtual void write(std::ostream &out) const override;
+#endif
+};
+
+} // namespace fpm
+
+#endif //FBODY_H
diff --git a/fpm/src/fLifting.cpp b/fpm/src/fLifting.cpp
new file mode 100644
index 0000000..4c018ae
--- /dev/null
+++ b/fpm/src/fLifting.cpp
@@ -0,0 +1,44 @@
+/*
+ * 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 "fLifting.h"
+#include "fWake.h"
+#include "wTag.h"
+#include <iomanip>
+#include <fstream>
+using namespace tbox;
+using namespace fpm;
+
+Lifting::Lifting(std::shared_ptr<MshData> _msh, int no) : Body(_msh, no)
+{
+    wake = new Wake();
+}
+
+Lifting::Lifting(std::shared_ptr<MshData> _msh, std::string const &name) : Body(_msh, name)
+{
+    wake = new Wake();
+}
+
+Lifting::~Lifting()
+{
+    delete wake;
+    std::cout << "~Lifting()\n";
+}
+
+void Lifting::write(std::ostream &out) const
+{
+    out << "fpm::Lifting " << *tag << std::endl;
+}
diff --git a/fpm/src/fLifting.h b/fpm/src/fLifting.h
new file mode 100644
index 0000000..e8439f0
--- /dev/null
+++ b/fpm/src/fLifting.h
@@ -0,0 +1,47 @@
+/*
+ * 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 FLIFTING_H
+#define FLIFTING_H
+
+#include "fpm.h"
+#include "fBody.h"
+#include "fWake.h"
+
+namespace fpm
+{
+
+/**
+ * @brief Manage a lifting body immersed in the fluid
+ * @authors Adrien Crovato
+ */
+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();
+
+#ifndef SWIG
+    virtual void write(std::ostream &out) const override;
+#endif
+};
+
+} // namespace fpm
+
+#endif //FLIFTING_H
diff --git a/fpm/src/fProblem.cpp b/fpm/src/fProblem.cpp
new file mode 100644
index 0000000..fb67f53
--- /dev/null
+++ b/fpm/src/fProblem.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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 "fProblem.h"
+#include "fBody.h"
+#include "fLifting.h"
+#include "wTag.h"
+#include "wElement.h"
+#include "wMem.h"
+using namespace tbox;
+using namespace fpm;
+
+Problem::Problem(std::shared_ptr<MshData> _msh, double aoa, double aos, double sref, double cref,
+                 double xref, double yref, double zref) : msh(_msh), alpha(aoa), beta(aos),
+                                                          sR(sref), cR(cref)
+{
+    xR(0) = xref;
+    xR(1) = yref;
+    xR(2) = zref;
+}
+
+/**
+ * @brief Add a non-lifting body
+ */
+void Problem::add(std::shared_ptr<Body> b)
+{
+    bodies.push_back(b);
+}
+/**
+ * @brief Add a lifting body
+ */
+void Problem::add(std::shared_ptr<Lifting> l)
+{
+    liftings.push_back(l);
+}
+
+/**
+ * @brief Update the elements memory (Jacobian)
+ */
+void Problem::updateMem()
+{
+    // Update surface Jacobian
+    for (auto s : bodies)
+        for (auto e : s->tag->elems)
+            e->getVMem().update(false);
+    for (auto s : liftings)
+        for (auto e : s->tag->elems)
+            e->getVMem().update(false);
+}
+
+/**
+ * @brief Check that Problem is not empty and that elements are supported
+ */
+void Problem::check() const
+{
+    // Sanity checks
+    if (liftings.empty())
+        throw std::runtime_error("No lifting bodies provided!\n");
+
+    // Three-dimension problem
+    // Surface element type
+    for (auto b : bodies)
+        for (auto e : b->tag->elems)
+            if (e->type() != ELTYPE::QUAD4)
+            {
+                std::stringstream err;
+                err << "FPM solver is only implemented for surface elements of type "
+                    << ELTYPE::QUAD4 << " (" << e->type() << " was given)!\n";
+                throw std::runtime_error(err.str());
+            }
+    for (auto l : liftings)
+        for (auto e : l->tag->elems)
+            if (e->type() != ELTYPE::QUAD4)
+            {
+                std::stringstream err;
+                err << "FPM solver is only implemented for surface elements of type "
+                    << ELTYPE::QUAD4 << " (" << e->type() << " was given)!\n";
+                throw std::runtime_error(err.str());
+            }
+}
+
+void Problem::write(std::ostream &out) const
+{
+    out << "fpm::Problem definition"
+        << "\n\tAngle of attack: " << alpha
+        << "\n\tAngle of sideslip: " << beta
+        << "\n\tReference area: " << sR
+        << "\n\tReference length: " << cR
+        << "\n\tReference point: [" << xR.transpose() << "]"
+        << std::endl;
+    out << "with";
+    for (auto b : bodies)
+        out << "\n\t" << *b;
+    for (auto l : liftings)
+        out << "\n\t" << *l;
+}
diff --git a/fpm/src/fProblem.h b/fpm/src/fProblem.h
new file mode 100644
index 0000000..d34d255
--- /dev/null
+++ b/fpm/src/fProblem.h
@@ -0,0 +1,62 @@
+/*
+ * 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 FPROBLEM_H
+#define FPROBLEM_H
+
+#include "fpm.h"
+#include "wObject.h"
+#include <memory>
+#include <vector>
+#include <Eigen/Dense>
+
+namespace fpm
+{
+
+/**
+ * @brief Manage the problem
+ * @authors Adrien Crovato
+ */
+class FPM_API Problem : public fwk::wSharedObject
+{
+public:
+    std::shared_ptr<tbox::MshData> msh; ///< Mesh data structure
+#ifndef SWIG
+    std::vector<std::shared_ptr<Body>> bodies;      ///< Non-lifting bodies
+    std::vector<std::shared_ptr<Lifting>> liftings; ///< Lifting bodies
+#endif
+    // Reference values
+    double alpha;       ///< Angle of attack
+    double beta;        ///< Angle of sideslip
+    double sR;          ///< Reference surface
+    double cR;          ///< Reference chord
+    Eigen::Vector3d xR; ///< Reference center point (for moment computation)
+
+    Problem(std::shared_ptr<tbox::MshData> _msh, double aoa, double aos, double sref, double cref, double xref, double yref, double zref);
+    ~Problem() { std::cout << "~Problem()\n"; }
+
+    void add(std::shared_ptr<Body> b);
+    void add(std::shared_ptr<Lifting> l);
+
+#ifndef SWIG
+    void check() const;
+    void updateMem();
+    virtual void write(std::ostream &out) const override;
+#endif
+};
+
+} // namespace fpm
+
+#endif //FPROBLEM_H
diff --git a/fpm/src/fWake.cpp b/fpm/src/fWake.cpp
new file mode 100644
index 0000000..02aed26
--- /dev/null
+++ b/fpm/src/fWake.cpp
@@ -0,0 +1,27 @@
+/*
+ * 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 "fWake.h"
+using namespace fpm;
+
+Wake::Wake() : fwk::wObject()
+{
+}
+
+void Wake::write(std::ostream &out) const
+{
+    out << "fpm::Wake " << std::endl;
+}
diff --git a/fpm/src/fWake.h b/fpm/src/fWake.h
new file mode 100644
index 0000000..f32d68e
--- /dev/null
+++ b/fpm/src/fWake.h
@@ -0,0 +1,43 @@
+/*
+ * 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 FWAKE_H
+#define FWAKE_H
+
+#include "fpm.h"
+#include "wObject.h"
+
+namespace fpm
+{
+
+/**
+ * @brief Manage the wake attached to a lifting body
+ * @authors Adrien Crovato
+ */
+class FPM_API Wake : public fwk::wObject
+{
+public:
+    Wake();
+    ~Wake() {}
+
+#ifndef SWIG
+    virtual void write(std::ostream &out) const override;
+#endif
+};
+
+} // namespace fpm
+
+#endif //FWAKE_H
diff --git a/fpm/src/fpm.h b/fpm/src/fpm.h
index e408ab5..2eb1d20 100644
--- a/fpm/src/fpm.h
+++ b/fpm/src/fpm.h
@@ -35,8 +35,14 @@ namespace fpm
 {
 
 // utilities
-class fTimer;
-class fTimers;
+class Timer;
+class Timers;
+
+// problem definition
+class Problem;
+class Body;
+class Lifting;
+class Wake;
 
 } // namespace fpm
 
diff --git a/fpm/tests/basic.py b/fpm/tests/basic.py
new file mode 100644
index 0000000..1a96b37
--- /dev/null
+++ b/fpm/tests/basic.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# 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.
+
+# Dummy test
+# Adrien Crovato
+
+import fpm
+import tbox
+import tbox.gmsh as gmsh
+from fwk.testing import *
+from fwk.coloring import ccolors
+
+def main():
+    # timer
+    tms = fpm.Timers()
+    tms['total'].start()
+    # mesh
+    print(ccolors.ANSI_BLUE + 'PyMeshing...' + ccolors.ANSI_RESET)
+    pars = {'spn' : 5, 'nC' : 80, 'nS' : 10}
+    msh = gmsh.MeshLoader('../models/n0012.geo', __file__).execute(**pars)
+    # problem
+    pbl = fpm.Problem(msh, 0, 0, 5., 1., 0., 0., 0.)
+    fuselage = fpm.Body(msh, 'wing')
+    pbl.add(fuselage)
+    wing = fpm.Lifting(msh, 'wing')
+    pbl.add(wing)
+    print(pbl)
+    # end timer
+    tms['total'].stop()
+    print(ccolors.ANSI_BLUE + 'PyTiming...' + ccolors.ANSI_RESET)
+    print(tms)
+
+    # test
+    print(ccolors.ANSI_BLUE + 'PyTesting...' + ccolors.ANSI_RESET)
+    tests = CTests()
+    tests.run()
+    
+    # eof
+    print('')
+
+if __name__ == '__main__':
+    main()
+
-- 
GitLab