diff --git a/fpm/_src/fpmw.h b/fpm/_src/fpmw.h
index 53f2b18b0db15b196870dc076465365e9fbc49d2..59e1f6abbbd1fc34219e567d8808a5105443ffc5 100644
--- a/fpm/_src/fpmw.h
+++ b/fpm/_src/fpmw.h
@@ -15,7 +15,6 @@
  */
 
 #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 fdb12718a218aa4703b9eb3f457b7b73871671e9..b68248d2b3c6045c12dbdb11b2cb1cf1a4b0b304 100644
--- a/fpm/_src/fpmw.i
+++ b/fpm/_src/fpmw.i
@@ -68,10 +68,8 @@ def __getitem__(self, name):
 %include <std_shared_ptr.i>
 
 %shared_ptr(fpm::Body);
+%immutable fpm::Body::wake; // avoids the creation of the setter method
 %include "fBody.h"
-%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"
 
diff --git a/fpm/src/fBody.cpp b/fpm/src/fBody.cpp
index b09b5569ba7b5af7dd0bcf893f05411a591a7151..10e9200919399c52368a26c8e4866072772ca6c7 100644
--- a/fpm/src/fBody.cpp
+++ b/fpm/src/fBody.cpp
@@ -15,14 +15,85 @@
  */
 
 #include "fBody.h"
+#include "fWake.h"
+#include "wMshData.h"
+#include "wNode.h"
+#include "wElement.h"
 #include "wTag.h"
+#include "wQuad4.h"
+#include "wGmshExport.h"
 #include <iomanip>
 #include <fstream>
 using namespace tbox;
 using namespace fpm;
 
-Body::Body(std::shared_ptr<MshData> _msh, std::string const &name) : Group(_msh, name), Cl(0), Cd(0), Cs(0), Cm(0)
+Body::Body(std::shared_ptr<MshData> _msh, std::string const &id,
+           std::vector<std::string> const &teIds, double xF) : Group(_msh, id), Cl(0), Cd(0), Cs(0), Cm(0)
 {
+    // If wakes are requested, check if they are already available, otherwise create them
+    bool hasChanged = false;
+    size_t j = 0;
+    for (auto teId : teIds)
+    {
+        std::cout << "Creating wake... " << std::flush;
+        try
+        {
+            wakes.push_back(new Wake(_msh, tag->name + "Wake" + std::to_string(j), tag));
+            std::cout << *wakes.back() << " already existing, nothing done." << std::endl;
+        }
+        catch (const std::out_of_range &)
+        {
+            // Notify that mesh needs to be saved
+            hasChanged = true;
+            // Create tags
+            Tag *tagp = new Tag(static_cast<int>(msh->ptags.rbegin()->first + 1), tag->name + "Wake" + std::to_string(j), 2);
+            Tag *tage = new Tag(static_cast<int>(msh->etags.rbegin()->first + 1), tag->name + "Wake" + std::to_string(j), 2);
+            msh->ntags[tag->name + "Wake" + std::to_string(j)] = 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, teId);
+            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)
+            {
+                // Sanity check
+                if (xF <= n->pos(0))
+                {
+                    std::stringstream err;
+                    err << "fpm::Body: zero or negative length wake requested for " << *tag << "!\n";
+                    throw std::runtime_error(err.str());
+                }
+                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
+            wakes.push_back(new Wake(_msh, tag->name + "Wake" + std::to_string(j), tag));
+            std::cout << *wakes.back() << " created. " << std::flush;
+        }
+        ++j;
+    }
+    // Save the mesh if modified
+    if (hasChanged)
+    {
+        GmshExport writer(_msh);
+        writer.save(_msh->name);
+    }
+
+    // Size load vectors
     cLoadX.resize(tag->elems.size());
     cLoadY.resize(tag->elems.size());
     cLoadZ.resize(tag->elems.size());
@@ -67,4 +138,7 @@ void Body::save(std::string const &name)
 void Body::write(std::ostream &out) const
 {
     out << "fpm::Body " << *tag << std::endl;
+    out << "with";
+    for (auto w : wakes)
+        out << "\t" << *w;
 }
diff --git a/fpm/src/fBody.h b/fpm/src/fBody.h
index 8d49a02593c9cab53404f7a60863caa597081fe2..7cdea5ad3a2dd2b152f8ff2f60662d2cffeb44b0 100644
--- a/fpm/src/fBody.h
+++ b/fpm/src/fBody.h
@@ -38,8 +38,9 @@ public:
     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
+    std::vector<Wake *> wakes;  ///< wake(s) attached to the lifting surface
 
-    Body(std::shared_ptr<tbox::MshData> _msh, std::string const &name);
+    Body(std::shared_ptr<tbox::MshData> _msh, std::string const &name, std::vector<std::string> const &teNames, double xF);
     ~Body() { std::cout << "~Body()\n"; }
 
     virtual void save(std::string const &name);
diff --git a/fpm/src/fLifting.cpp b/fpm/src/fLifting.cpp
deleted file mode 100644
index 63d51ed17c4cb19b4bbf8300994de803122bf3f5..0000000000000000000000000000000000000000
--- a/fpm/src/fLifting.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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 "wMshData.h"
-#include "wGmshExport.h"
-#include "wTag.h"
-#include "wElement.h"
-#include "wQuad4.h"
-#include "wNode.h"
-using namespace tbox;
-using namespace fpm;
-
-Lifting::Lifting(std::shared_ptr<MshData> _msh, std::vector<std::string> const &names, double xF) : Body(_msh, names[0])
-{
-    // 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());
-    }
-
-    // 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()
-{
-    delete wake;
-    std::cout << "~Lifting()\n";
-}
-
-void Lifting::write(std::ostream &out) const
-{
-    out << "fpm::Lifting " << *tag
-        << " with " << *wake << std::endl;
-}
diff --git a/fpm/src/fLifting.h b/fpm/src/fLifting.h
deleted file mode 100644
index 2cfedc79d4dd26843a6c4f809da14767b4db3980..0000000000000000000000000000000000000000
--- a/fpm/src/fLifting.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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, std::vector<std::string> const &names, double xF);
-    ~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
index 6c2e3497056fc9dee0edd2179323397803228bb0..5ac54291b181be9abdb8f9c0436e77302dfed82e 100644
--- a/fpm/src/fProblem.cpp
+++ b/fpm/src/fProblem.cpp
@@ -16,15 +16,14 @@
 
 #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),
+Problem::Problem(std::shared_ptr<MshData> _smsh, double aoa, double aos, double sref, double cref,
+                 double xref, double yref, double zref) : smsh(_smsh), alpha(aoa), beta(aos),
                                                           sR(sref), cR(cref)
 {
     xR(0) = xref;
@@ -39,13 +38,6 @@ 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)
@@ -56,9 +48,6 @@ void Problem::updateMem()
     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);
 }
 
 /**
@@ -67,7 +56,7 @@ void Problem::updateMem()
 void Problem::check() const
 {
     // Sanity checks
-    if (liftings.empty())
+    if (bodies.empty())
         throw std::runtime_error("No lifting bodies provided!\n");
 
     // Three-dimension problem
@@ -81,15 +70,6 @@ void Problem::check() const
                     << 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
@@ -104,6 +84,4 @@ void Problem::write(std::ostream &out) const
     out << "with";
     for (auto b : bodies)
         out << "\t" << *b;
-    for (auto l : liftings)
-        out << "\t" << *l;
 }
diff --git a/fpm/src/fProblem.h b/fpm/src/fProblem.h
index d34d255155813a8ae5b828accae17dfe8836011e..54cf0159924a8e100ad81a1ca01fc77b90669efb 100644
--- a/fpm/src/fProblem.h
+++ b/fpm/src/fProblem.h
@@ -32,10 +32,9 @@ namespace fpm
 class FPM_API Problem : public fwk::wSharedObject
 {
 public:
-    std::shared_ptr<tbox::MshData> msh; ///< Mesh data structure
+    std::shared_ptr<tbox::MshData> smsh; ///< surface mesh data structure
 #ifndef SWIG
-    std::vector<std::shared_ptr<Body>> bodies;      ///< Non-lifting bodies
-    std::vector<std::shared_ptr<Lifting>> liftings; ///< Lifting bodies
+    std::vector<std::shared_ptr<Body>> bodies; ///< bodies in fluid
 #endif
     // Reference values
     double alpha;       ///< Angle of attack
@@ -44,11 +43,10 @@ public:
     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::shared_ptr<tbox::MshData> _smsh, 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;
diff --git a/fpm/src/fpm.h b/fpm/src/fpm.h
index 0d681a8504d5405b1564c9775b485d8080aa1f6e..a56c56666b8cef734fd365b5fdff0158dab358e1 100644
--- a/fpm/src/fpm.h
+++ b/fpm/src/fpm.h
@@ -41,7 +41,6 @@ class Timers;
 // problem definition
 class Problem;
 class Body;
-class Lifting;
 class Wake;
 
 // misc
diff --git a/fpm/tests/basic.py b/fpm/tests/basic.py
index 232d38fa7344ebbd066f3c532f0c3ffebfe76ab2..af8a994832dc1d3edcabf841750934bbd74664c3 100644
--- a/fpm/tests/basic.py
+++ b/fpm/tests/basic.py
@@ -34,9 +34,7 @@ def main():
     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', 'te'], 5)
+    wing = fpm.Body(msh, 'wing', ['te'], 5)
     pbl.add(wing)
     print(pbl)
     # end timer