From bb3f0e0c2f18f6b0df2d6a59af40d106fda9c7dd Mon Sep 17 00:00:00 2001
From: Dechamps Axel <axel.dechamps@student.uliege.be>
Date: Tue, 1 Feb 2022 08:13:44 +0000
Subject: [PATCH] Master Thesis - Dechamps - Implementation of the linear part

---
 fpm/models/agard445.geo | 144 ++++++++++++
 fpm/models/n0012.geo    | 445 +++++++++++++++++++------------------
 fpm/models/n0012.py     |   4 +-
 fpm/models/oneraM6.geo  | 327 ++++++++++++++++++++++++++++
 fpm/src/fBody.cpp       |  48 ++--
 fpm/src/fBuilder.cpp    | 470 +++++++++++++++++++++++++++++++++++++---
 fpm/src/fBuilder.h      |  16 +-
 fpm/src/fProblem.cpp    | 143 +++++++++++-
 fpm/src/fProblem.h      |  14 +-
 fpm/src/fSolver.cpp     |  54 +++--
 fpm/src/fSolver.h       |   6 +-
 fpm/src/fWake.cpp       |   2 +-
 fpm/tests/M6.py         |  83 +++++++
 fpm/tests/a445.py       |  84 +++++++
 fpm/tests/n12.py        |  20 +-
 fpm/tests/n12f.py       |   8 +-
 fpm/tests/n12t.py       |  16 +-
 17 files changed, 1567 insertions(+), 317 deletions(-)
 create mode 100644 fpm/models/agard445.geo
 create mode 100644 fpm/models/oneraM6.geo
 create mode 100644 fpm/tests/M6.py
 create mode 100644 fpm/tests/a445.py

diff --git a/fpm/models/agard445.geo b/fpm/models/agard445.geo
new file mode 100644
index 0000000..c419f7a
--- /dev/null
+++ b/fpm/models/agard445.geo
@@ -0,0 +1,144 @@
+/* AGARD 445 wing */
+
+// Mesh parameters
+// domain and mesh
+DefineConstant[ wNc = { 50, Name "Number of panel along chord" }
+                wNs= { 10, Name "Number of panel along span" }
+                bC = { 0.05, Name "Progression along chord" }
+                pS = { 1.0, Name "Progression along span" } ];
+
+//// 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};
+
+/// Lines
+// Airfoil 1:
+Spline(1) = {1:26};
+Spline(2) = {26:50,1};
+// Airfoil 2:
+Spline(3) = {52:77};
+Spline(4) = {77:101,52};
+// Airfoil 1 to airfoil 2:
+Line(61) = {1,52};
+Line(62) = {26,77};
+
+
+/// Line loops & Surfaces
+// Wing 1:
+Line Loop(11) = {1,62,-3,-61};
+Line Loop(12) = {2,61,-4,-62};
+Surface(11) = {-11};
+Surface(12) = {-12};
+
+//// MESH
+Transfinite Line{1,2,3,4} = wNc+ 1 Using Bump bC;
+Transfinite Line{61,62} = wNs + 1 Using Progression pS;
+Transfinite Surface{11,12};
+Recombine Surface{11,12};
+
+//// PHYSICAL GROUPS
+// Trailing edge
+Physical Line("wTe") = {61};
+// Wing:
+Physical Surface("wing") = {11,12};
diff --git a/fpm/models/n0012.geo b/fpm/models/n0012.geo
index c1c0f80..1f70a5d 100644
--- a/fpm/models/n0012.geo
+++ b/fpm/models/n0012.geo
@@ -2,122 +2,139 @@
 
 // Parameters
 // domain and mesh
-DefineConstant[ wSpn = { 2.0, Name "Wing span" }  ];
-DefineConstant[ wNc = { 80, Name "Number of cells along wing chord" }  ];
-DefineConstant[ wNs = { 10, Name "Number of cells along wing span" }  ];
-DefineConstant[ tSpn = { 0.0, Name "Tail span" }  ];
-DefineConstant[ tNc = { 40, Name "Number of cells along tail chord" }  ];
-DefineConstant[ tNs = { 6, Name "Number of cells along tail span" }  ];
-DefineConstant[ fWdt = { 0.0, Name "Field width" }  ];
-DefineConstant[ nX = { 10, Name "Number of field cells along X" }  ];
-DefineConstant[ nY = { 10, Name "Number of field cells along Y" }  ];
-DefineConstant[ nZ = { 10, Name "Number of field cells along Z" }  ];
+DefineConstant[
+    symY = { 1, Choices{0, 1}, Name "Y-plane symmetry" }
+    wSpn = { 2.0, Name "Wing span" }
+    wNc = { 80, Name "Number of cells along wing chord" }
+    wNs = { 10, Name "Number of cells along wing span" }
+    tSpn = { 0.0, Name "Tail span" }
+    tNc = { 40, Name "Number of cells along tail chord" }
+    tNs = { 6, Name "Number of cells along tail span" }
+    fWdt = { 0.0, Name "Field width" }
+    nX = { 10, Name "Number of field cells along X" }
+    nY = { 10, Name "Number of field cells along Y" }
+    nZ = { 10, Name "Number of field cells along Z" }
+];
 
 //// GEOMETRY
 
+If(!symY)
+    _wSpn = -wSpn/2;
+    _tSpn = -tSpn/2;
+    _fWdt = -fWdt/2;
+EndIf
+If(symY)
+    _wSpn = 0;
+    _tSpn = 0;
+    _fWdt = 0;
+    wSpn = 2*wSpn;
+    tSpn = 2*tSpn;
+    fWdt = 2*fWdt;
+EndIf
+
 /// Points
 
 // Wing
-Point(1) = {1.000000,-wSpn/2,0.000000};
-Point(2) = {0.999013,-wSpn/2,0.000143};
-Point(3) = {0.996057,-wSpn/2,0.000572};
-Point(4) = {0.991144,-wSpn/2,0.001280};
-Point(5) = {0.984292,-wSpn/2,0.002260};
-Point(6) = {0.975528,-wSpn/2,0.003501};
-Point(7) = {0.964888,-wSpn/2,0.004990};
-Point(8) = {0.952414,-wSpn/2,0.006710};
-Point(9) = {0.938153,-wSpn/2,0.008643};
-Point(10) = {0.922164,-wSpn/2,0.010770};
-Point(11) = {0.904508,-wSpn/2,0.013071};
-Point(12) = {0.885257,-wSpn/2,0.015523};
-Point(13) = {0.864484,-wSpn/2,0.018106};
-Point(14) = {0.842274,-wSpn/2,0.020795};
-Point(15) = {0.818712,-wSpn/2,0.023569};
-Point(16) = {0.793893,-wSpn/2,0.026405};
-Point(17) = {0.767913,-wSpn/2,0.029279};
-Point(18) = {0.740877,-wSpn/2,0.032168};
-Point(19) = {0.712890,-wSpn/2,0.035048};
-Point(20) = {0.684062,-wSpn/2,0.037896};
-Point(21) = {0.654508,-wSpn/2,0.040686};
-Point(22) = {0.624345,-wSpn/2,0.043394};
-Point(23) = {0.593691,-wSpn/2,0.045992};
-Point(24) = {0.562667,-wSpn/2,0.048455};
-Point(25) = {0.531395,-wSpn/2,0.050754};
-Point(26) = {0.500000,-wSpn/2,0.052862};
-Point(27) = {0.468605,-wSpn/2,0.054749};
-Point(28) = {0.437333,-wSpn/2,0.056390};
-Point(29) = {0.406309,-wSpn/2,0.057755};
-Point(30) = {0.375655,-wSpn/2,0.058819};
-Point(31) = {0.345492,-wSpn/2,0.059557};
-Point(32) = {0.315938,-wSpn/2,0.059947};
-Point(33) = {0.287110,-wSpn/2,0.059971};
-Point(34) = {0.259123,-wSpn/2,0.059614};
-Point(35) = {0.232087,-wSpn/2,0.058863};
-Point(36) = {0.206107,-wSpn/2,0.057712};
-Point(37) = {0.181288,-wSpn/2,0.056159};
-Point(38) = {0.157726,-wSpn/2,0.054206};
-Point(39) = {0.135516,-wSpn/2,0.051862};
-Point(40) = {0.114743,-wSpn/2,0.049138};
-Point(41) = {0.095492,-wSpn/2,0.046049};
-Point(42) = {0.077836,-wSpn/2,0.042615};
-Point(43) = {0.061847,-wSpn/2,0.038859};
-Point(44) = {0.047586,-wSpn/2,0.034803};
-Point(45) = {0.035112,-wSpn/2,0.030473};
-Point(46) = {0.024472,-wSpn/2,0.025893};
-Point(47) = {0.015708,-wSpn/2,0.021088};
-Point(48) = {0.008856,-wSpn/2,0.016078};
-Point(49) = {0.003943,-wSpn/2,0.010884};
-Point(50) = {0.000987,-wSpn/2,0.005521};
-Point(51) = {0.000000,-wSpn/2,0.000000};
-Point(52) = {0.000987,-wSpn/2,-0.005521};
-Point(53) = {0.003943,-wSpn/2,-0.010884};
-Point(54) = {0.008856,-wSpn/2,-0.016078};
-Point(55) = {0.015708,-wSpn/2,-0.021088};
-Point(56) = {0.024472,-wSpn/2,-0.025893};
-Point(57) = {0.035112,-wSpn/2,-0.030473};
-Point(58) = {0.047586,-wSpn/2,-0.034803};
-Point(59) = {0.061847,-wSpn/2,-0.038859};
-Point(60) = {0.077836,-wSpn/2,-0.042615};
-Point(61) = {0.095492,-wSpn/2,-0.046049};
-Point(62) = {0.114743,-wSpn/2,-0.049138};
-Point(63) = {0.135516,-wSpn/2,-0.051862};
-Point(64) = {0.157726,-wSpn/2,-0.054206};
-Point(65) = {0.181288,-wSpn/2,-0.056159};
-Point(66) = {0.206107,-wSpn/2,-0.057712};
-Point(67) = {0.232087,-wSpn/2,-0.058863};
-Point(68) = {0.259123,-wSpn/2,-0.059614};
-Point(69) = {0.287110,-wSpn/2,-0.059971};
-Point(70) = {0.315938,-wSpn/2,-0.059947};
-Point(71) = {0.345492,-wSpn/2,-0.059557};
-Point(72) = {0.375655,-wSpn/2,-0.058819};
-Point(73) = {0.406309,-wSpn/2,-0.057755};
-Point(74) = {0.437333,-wSpn/2,-0.056390};
-Point(75) = {0.468605,-wSpn/2,-0.054749};
-Point(76) = {0.500000,-wSpn/2,-0.052862};
-Point(77) = {0.531395,-wSpn/2,-0.050754};
-Point(78) = {0.562667,-wSpn/2,-0.048455};
-Point(79) = {0.593691,-wSpn/2,-0.045992};
-Point(80) = {0.624345,-wSpn/2,-0.043394};
-Point(81) = {0.654508,-wSpn/2,-0.040686};
-Point(82) = {0.684062,-wSpn/2,-0.037896};
-Point(83) = {0.712890,-wSpn/2,-0.035048};
-Point(84) = {0.740877,-wSpn/2,-0.032168};
-Point(85) = {0.767913,-wSpn/2,-0.029279};
-Point(86) = {0.793893,-wSpn/2,-0.026405};
-Point(87) = {0.818712,-wSpn/2,-0.023569};
-Point(88) = {0.842274,-wSpn/2,-0.020795};
-Point(89) = {0.864484,-wSpn/2,-0.018106};
-Point(90) = {0.885257,-wSpn/2,-0.015523};
-Point(91) = {0.904508,-wSpn/2,-0.013071};
-Point(92) = {0.922164,-wSpn/2,-0.010770};
-Point(93) = {0.938153,-wSpn/2,-0.008643};
-Point(94) = {0.952414,-wSpn/2,-0.006710};
-Point(95) = {0.964888,-wSpn/2,-0.004990};
-Point(96) = {0.975528,-wSpn/2,-0.003501};
-Point(97) = {0.984292,-wSpn/2,-0.002260};
-Point(98) = {0.991144,-wSpn/2,-0.001280};
-Point(99) = {0.996057,-wSpn/2,-0.000572};
-Point(100) = {0.999013,-wSpn/2,-0.000143};
+Point(1) = {1.000000,_wSpn,0.000000};
+Point(2) = {0.999013,_wSpn,0.000143};
+Point(3) = {0.996057,_wSpn,0.000572};
+Point(4) = {0.991144,_wSpn,0.001280};
+Point(5) = {0.984292,_wSpn,0.002260};
+Point(6) = {0.975528,_wSpn,0.003501};
+Point(7) = {0.964888,_wSpn,0.004990};
+Point(8) = {0.952414,_wSpn,0.006710};
+Point(9) = {0.938153,_wSpn,0.008643};
+Point(10) = {0.922164,_wSpn,0.010770};
+Point(11) = {0.904508,_wSpn,0.013071};
+Point(12) = {0.885257,_wSpn,0.015523};
+Point(13) = {0.864484,_wSpn,0.018106};
+Point(14) = {0.842274,_wSpn,0.020795};
+Point(15) = {0.818712,_wSpn,0.023569};
+Point(16) = {0.793893,_wSpn,0.026405};
+Point(17) = {0.767913,_wSpn,0.029279};
+Point(18) = {0.740877,_wSpn,0.032168};
+Point(19) = {0.712890,_wSpn,0.035048};
+Point(20) = {0.684062,_wSpn,0.037896};
+Point(21) = {0.654508,_wSpn,0.040686};
+Point(22) = {0.624345,_wSpn,0.043394};
+Point(23) = {0.593691,_wSpn,0.045992};
+Point(24) = {0.562667,_wSpn,0.048455};
+Point(25) = {0.531395,_wSpn,0.050754};
+Point(26) = {0.500000,_wSpn,0.052862};
+Point(27) = {0.468605,_wSpn,0.054749};
+Point(28) = {0.437333,_wSpn,0.056390};
+Point(29) = {0.406309,_wSpn,0.057755};
+Point(30) = {0.375655,_wSpn,0.058819};
+Point(31) = {0.345492,_wSpn,0.059557};
+Point(32) = {0.315938,_wSpn,0.059947};
+Point(33) = {0.287110,_wSpn,0.059971};
+Point(34) = {0.259123,_wSpn,0.059614};
+Point(35) = {0.232087,_wSpn,0.058863};
+Point(36) = {0.206107,_wSpn,0.057712};
+Point(37) = {0.181288,_wSpn,0.056159};
+Point(38) = {0.157726,_wSpn,0.054206};
+Point(39) = {0.135516,_wSpn,0.051862};
+Point(40) = {0.114743,_wSpn,0.049138};
+Point(41) = {0.095492,_wSpn,0.046049};
+Point(42) = {0.077836,_wSpn,0.042615};
+Point(43) = {0.061847,_wSpn,0.038859};
+Point(44) = {0.047586,_wSpn,0.034803};
+Point(45) = {0.035112,_wSpn,0.030473};
+Point(46) = {0.024472,_wSpn,0.025893};
+Point(47) = {0.015708,_wSpn,0.021088};
+Point(48) = {0.008856,_wSpn,0.016078};
+Point(49) = {0.003943,_wSpn,0.010884};
+Point(50) = {0.000987,_wSpn,0.005521};
+Point(51) = {0.000000,_wSpn,0.000000};
+Point(52) = {0.000987,_wSpn,-0.005521};
+Point(53) = {0.003943,_wSpn,-0.010884};
+Point(54) = {0.008856,_wSpn,-0.016078};
+Point(55) = {0.015708,_wSpn,-0.021088};
+Point(56) = {0.024472,_wSpn,-0.025893};
+Point(57) = {0.035112,_wSpn,-0.030473};
+Point(58) = {0.047586,_wSpn,-0.034803};
+Point(59) = {0.061847,_wSpn,-0.038859};
+Point(60) = {0.077836,_wSpn,-0.042615};
+Point(61) = {0.095492,_wSpn,-0.046049};
+Point(62) = {0.114743,_wSpn,-0.049138};
+Point(63) = {0.135516,_wSpn,-0.051862};
+Point(64) = {0.157726,_wSpn,-0.054206};
+Point(65) = {0.181288,_wSpn,-0.056159};
+Point(66) = {0.206107,_wSpn,-0.057712};
+Point(67) = {0.232087,_wSpn,-0.058863};
+Point(68) = {0.259123,_wSpn,-0.059614};
+Point(69) = {0.287110,_wSpn,-0.059971};
+Point(70) = {0.315938,_wSpn,-0.059947};
+Point(71) = {0.345492,_wSpn,-0.059557};
+Point(72) = {0.375655,_wSpn,-0.058819};
+Point(73) = {0.406309,_wSpn,-0.057755};
+Point(74) = {0.437333,_wSpn,-0.056390};
+Point(75) = {0.468605,_wSpn,-0.054749};
+Point(76) = {0.500000,_wSpn,-0.052862};
+Point(77) = {0.531395,_wSpn,-0.050754};
+Point(78) = {0.562667,_wSpn,-0.048455};
+Point(79) = {0.593691,_wSpn,-0.045992};
+Point(80) = {0.624345,_wSpn,-0.043394};
+Point(81) = {0.654508,_wSpn,-0.040686};
+Point(82) = {0.684062,_wSpn,-0.037896};
+Point(83) = {0.712890,_wSpn,-0.035048};
+Point(84) = {0.740877,_wSpn,-0.032168};
+Point(85) = {0.767913,_wSpn,-0.029279};
+Point(86) = {0.793893,_wSpn,-0.026405};
+Point(87) = {0.818712,_wSpn,-0.023569};
+Point(88) = {0.842274,_wSpn,-0.020795};
+Point(89) = {0.864484,_wSpn,-0.018106};
+Point(90) = {0.885257,_wSpn,-0.015523};
+Point(91) = {0.904508,_wSpn,-0.013071};
+Point(92) = {0.922164,_wSpn,-0.010770};
+Point(93) = {0.938153,_wSpn,-0.008643};
+Point(94) = {0.952414,_wSpn,-0.006710};
+Point(95) = {0.964888,_wSpn,-0.004990};
+Point(96) = {0.975528,_wSpn,-0.003501};
+Point(97) = {0.984292,_wSpn,-0.002260};
+Point(98) = {0.991144,_wSpn,-0.001280};
+Point(99) = {0.996057,_wSpn,-0.000572};
+Point(100) = {0.999013,_wSpn,-0.000143};
 
 Point(102) = {1.000000,wSpn/2,0.000000};
 Point(103) = {0.999013,wSpn/2,0.000143};
@@ -222,106 +239,106 @@ Point(201) = {0.999013,wSpn/2,-0.000143};
 
 // Tail
 If(tSpn != 0)
-    Point(301) = {5+1.000000/2,-tSpn/2,0.5+0.000000/2};
-    Point(302) = {5+0.999013/2,-tSpn/2,0.5+0.000143/2};
-    Point(303) = {5+0.996057/2,-tSpn/2,0.5+0.000572/2};
-    Point(304) = {5+0.991144/2,-tSpn/2,0.5+0.001280/2};
-    Point(305) = {5+0.984292/2,-tSpn/2,0.5+0.002260/2};
-    Point(306) = {5+0.975528/2,-tSpn/2,0.5+0.003501/2};
-    Point(307) = {5+0.964888/2,-tSpn/2,0.5+0.004990/2};
-    Point(308) = {5+0.952414/2,-tSpn/2,0.5+0.006710/2};
-    Point(309) = {5+0.938153/2,-tSpn/2,0.5+0.008643/2};
-    Point(310) = {5+0.922164/2,-tSpn/2,0.5+0.010770/2};
-    Point(311) = {5+0.904508/2,-tSpn/2,0.5+0.013071/2};
-    Point(312) = {5+0.885257/2,-tSpn/2,0.5+0.015523/2};
-    Point(313) = {5+0.864484/2,-tSpn/2,0.5+0.018106/2};
-    Point(314) = {5+0.842274/2,-tSpn/2,0.5+0.020795/2};
-    Point(315) = {5+0.818712/2,-tSpn/2,0.5+0.023569/2};
-    Point(316) = {5+0.793893/2,-tSpn/2,0.5+0.026405/2};
-    Point(317) = {5+0.767913/2,-tSpn/2,0.5+0.029279/2};
-    Point(318) = {5+0.740877/2,-tSpn/2,0.5+0.032168/2};
-    Point(319) = {5+0.712890/2,-tSpn/2,0.5+0.035048/2};
-    Point(320) = {5+0.684062/2,-tSpn/2,0.5+0.037896/2};
-    Point(321) = {5+0.654508/2,-tSpn/2,0.5+0.040686/2};
-    Point(322) = {5+0.624345/2,-tSpn/2,0.5+0.043394/2};
-    Point(323) = {5+0.593691/2,-tSpn/2,0.5+0.045992/2};
-    Point(324) = {5+0.562667/2,-tSpn/2,0.5+0.048455/2};
-    Point(325) = {5+0.531395/2,-tSpn/2,0.5+0.050754/2};
-    Point(326) = {5+0.500000/2,-tSpn/2,0.5+0.052862/2};
-    Point(327) = {5+0.468605/2,-tSpn/2,0.5+0.054749/2};
-    Point(328) = {5+0.437333/2,-tSpn/2,0.5+0.056390/2};
-    Point(329) = {5+0.406309/2,-tSpn/2,0.5+0.057755/2};
-    Point(330) = {5+0.375655/2,-tSpn/2,0.5+0.058819/2};
-    Point(331) = {5+0.345492/2,-tSpn/2,0.5+0.059557/2};
-    Point(332) = {5+0.315938/2,-tSpn/2,0.5+0.059947/2};
-    Point(333) = {5+0.287110/2,-tSpn/2,0.5+0.059971/2};
-    Point(334) = {5+0.259123/2,-tSpn/2,0.5+0.059614/2};
-    Point(335) = {5+0.232087/2,-tSpn/2,0.5+0.058863/2};
-    Point(336) = {5+0.206107/2,-tSpn/2,0.5+0.057712/2};
-    Point(337) = {5+0.181288/2,-tSpn/2,0.5+0.056159/2};
-    Point(338) = {5+0.157726/2,-tSpn/2,0.5+0.054206/2};
-    Point(339) = {5+0.135516/2,-tSpn/2,0.5+0.051862/2};
-    Point(340) = {5+0.114743/2,-tSpn/2,0.5+0.049138/2};
-    Point(341) = {5+0.095492/2,-tSpn/2,0.5+0.046049/2};
-    Point(342) = {5+0.077836/2,-tSpn/2,0.5+0.042615/2};
-    Point(343) = {5+0.061847/2,-tSpn/2,0.5+0.038859/2};
-    Point(344) = {5+0.047586/2,-tSpn/2,0.5+0.034803/2};
-    Point(345) = {5+0.035112/2,-tSpn/2,0.5+0.030473/2};
-    Point(346) = {5+0.024472/2,-tSpn/2,0.5+0.025893/2};
-    Point(347) = {5+0.015708/2,-tSpn/2,0.5+0.021088/2};
-    Point(348) = {5+0.008856/2,-tSpn/2,0.5+0.016078/2};
-    Point(349) = {5+0.003943/2,-tSpn/2,0.5+0.010884/2};
-    Point(350) = {5+0.000987/2,-tSpn/2,0.5+0.005521/2};
-    Point(351) = {5+0.000000/2,-tSpn/2,0.5+0.000000/2};
-    Point(352) = {5+0.000987/2,-tSpn/2,0.5-0.005521/2};
-    Point(353) = {5+0.003943/2,-tSpn/2,0.5-0.010884/2};
-    Point(354) = {5+0.008856/2,-tSpn/2,0.5-0.016078/2};
-    Point(355) = {5+0.015708/2,-tSpn/2,0.5-0.021088/2};
-    Point(356) = {5+0.024472/2,-tSpn/2,0.5-0.025893/2};
-    Point(357) = {5+0.035112/2,-tSpn/2,0.5-0.030473/2};
-    Point(358) = {5+0.047586/2,-tSpn/2,0.5-0.034803/2};
-    Point(359) = {5+0.061847/2,-tSpn/2,0.5-0.038859/2};
-    Point(360) = {5+0.077836/2,-tSpn/2,0.5-0.042615/2};
-    Point(361) = {5+0.095492/2,-tSpn/2,0.5-0.046049/2};
-    Point(362) = {5+0.114743/2,-tSpn/2,0.5-0.049138/2};
-    Point(363) = {5+0.135516/2,-tSpn/2,0.5-0.051862/2};
-    Point(364) = {5+0.157726/2,-tSpn/2,0.5-0.054206/2};
-    Point(365) = {5+0.181288/2,-tSpn/2,0.5-0.056159/2};
-    Point(366) = {5+0.206107/2,-tSpn/2,0.5-0.057712/2};
-    Point(367) = {5+0.232087/2,-tSpn/2,0.5-0.058863/2};
-    Point(368) = {5+0.259123/2,-tSpn/2,0.5-0.059614/2};
-    Point(369) = {5+0.287110/2,-tSpn/2,0.5-0.059971/2};
-    Point(370) = {5+0.315938/2,-tSpn/2,0.5-0.059947/2};
-    Point(371) = {5+0.345492/2,-tSpn/2,0.5-0.059557/2};
-    Point(372) = {5+0.375655/2,-tSpn/2,0.5-0.058819/2};
-    Point(373) = {5+0.406309/2,-tSpn/2,0.5-0.057755/2};
-    Point(374) = {5+0.437333/2,-tSpn/2,0.5-0.056390/2};
-    Point(375) = {5+0.468605/2,-tSpn/2,0.5-0.054749/2};
-    Point(376) = {5+0.500000/2,-tSpn/2,0.5-0.052862/2};
-    Point(377) = {5+0.531395/2,-tSpn/2,0.5-0.050754/2};
-    Point(378) = {5+0.562667/2,-tSpn/2,0.5-0.048455/2};
-    Point(379) = {5+0.593691/2,-tSpn/2,0.5-0.045992/2};
-    Point(380) = {5+0.624345/2,-tSpn/2,0.5-0.043394/2};
-    Point(381) = {5+0.654508/2,-tSpn/2,0.5-0.040686/2};
-    Point(382) = {5+0.684062/2,-tSpn/2,0.5-0.037896/2};
-    Point(383) = {5+0.712890/2,-tSpn/2,0.5-0.035048/2};
-    Point(384) = {5+0.740877/2,-tSpn/2,0.5-0.032168/2};
-    Point(385) = {5+0.767913/2,-tSpn/2,0.5-0.029279/2};
-    Point(386) = {5+0.793893/2,-tSpn/2,0.5-0.026405/2};
-    Point(387) = {5+0.818712/2,-tSpn/2,0.5-0.023569/2};
-    Point(388) = {5+0.842274/2,-tSpn/2,0.5-0.020795/2};
-    Point(389) = {5+0.864484/2,-tSpn/2,0.5-0.018106/2};
-    Point(390) = {5+0.885257/2,-tSpn/2,0.5-0.015523/2};
-    Point(391) = {5+0.904508/2,-tSpn/2,0.5-0.013071/2};
-    Point(392) = {5+0.922164/2,-tSpn/2,0.5-0.010770/2};
-    Point(393) = {5+0.938153/2,-tSpn/2,0.5-0.008643/2};
-    Point(394) = {5+0.952414/2,-tSpn/2,0.5-0.006710/2};
-    Point(395) = {5+0.964888/2,-tSpn/2,0.5-0.004990/2};
-    Point(396) = {5+0.975528/2,-tSpn/2,0.5-0.003501/2};
-    Point(397) = {5+0.984292/2,-tSpn/2,0.5-0.002260/2};
-    Point(398) = {5+0.991144/2,-tSpn/2,0.5-0.001280/2};
-    Point(399) = {5+0.996057/2,-tSpn/2,0.5-0.000572/2};
-    Point(400) = {5+0.999013/2,-tSpn/2,0.5-0.000143/2};
+    Point(301) = {5+1.000000/2,_tSpn,0.5+0.000000/2};
+    Point(302) = {5+0.999013/2,_tSpn,0.5+0.000143/2};
+    Point(303) = {5+0.996057/2,_tSpn,0.5+0.000572/2};
+    Point(304) = {5+0.991144/2,_tSpn,0.5+0.001280/2};
+    Point(305) = {5+0.984292/2,_tSpn,0.5+0.002260/2};
+    Point(306) = {5+0.975528/2,_tSpn,0.5+0.003501/2};
+    Point(307) = {5+0.964888/2,_tSpn,0.5+0.004990/2};
+    Point(308) = {5+0.952414/2,_tSpn,0.5+0.006710/2};
+    Point(309) = {5+0.938153/2,_tSpn,0.5+0.008643/2};
+    Point(310) = {5+0.922164/2,_tSpn,0.5+0.010770/2};
+    Point(311) = {5+0.904508/2,_tSpn,0.5+0.013071/2};
+    Point(312) = {5+0.885257/2,_tSpn,0.5+0.015523/2};
+    Point(313) = {5+0.864484/2,_tSpn,0.5+0.018106/2};
+    Point(314) = {5+0.842274/2,_tSpn,0.5+0.020795/2};
+    Point(315) = {5+0.818712/2,_tSpn,0.5+0.023569/2};
+    Point(316) = {5+0.793893/2,_tSpn,0.5+0.026405/2};
+    Point(317) = {5+0.767913/2,_tSpn,0.5+0.029279/2};
+    Point(318) = {5+0.740877/2,_tSpn,0.5+0.032168/2};
+    Point(319) = {5+0.712890/2,_tSpn,0.5+0.035048/2};
+    Point(320) = {5+0.684062/2,_tSpn,0.5+0.037896/2};
+    Point(321) = {5+0.654508/2,_tSpn,0.5+0.040686/2};
+    Point(322) = {5+0.624345/2,_tSpn,0.5+0.043394/2};
+    Point(323) = {5+0.593691/2,_tSpn,0.5+0.045992/2};
+    Point(324) = {5+0.562667/2,_tSpn,0.5+0.048455/2};
+    Point(325) = {5+0.531395/2,_tSpn,0.5+0.050754/2};
+    Point(326) = {5+0.500000/2,_tSpn,0.5+0.052862/2};
+    Point(327) = {5+0.468605/2,_tSpn,0.5+0.054749/2};
+    Point(328) = {5+0.437333/2,_tSpn,0.5+0.056390/2};
+    Point(329) = {5+0.406309/2,_tSpn,0.5+0.057755/2};
+    Point(330) = {5+0.375655/2,_tSpn,0.5+0.058819/2};
+    Point(331) = {5+0.345492/2,_tSpn,0.5+0.059557/2};
+    Point(332) = {5+0.315938/2,_tSpn,0.5+0.059947/2};
+    Point(333) = {5+0.287110/2,_tSpn,0.5+0.059971/2};
+    Point(334) = {5+0.259123/2,_tSpn,0.5+0.059614/2};
+    Point(335) = {5+0.232087/2,_tSpn,0.5+0.058863/2};
+    Point(336) = {5+0.206107/2,_tSpn,0.5+0.057712/2};
+    Point(337) = {5+0.181288/2,_tSpn,0.5+0.056159/2};
+    Point(338) = {5+0.157726/2,_tSpn,0.5+0.054206/2};
+    Point(339) = {5+0.135516/2,_tSpn,0.5+0.051862/2};
+    Point(340) = {5+0.114743/2,_tSpn,0.5+0.049138/2};
+    Point(341) = {5+0.095492/2,_tSpn,0.5+0.046049/2};
+    Point(342) = {5+0.077836/2,_tSpn,0.5+0.042615/2};
+    Point(343) = {5+0.061847/2,_tSpn,0.5+0.038859/2};
+    Point(344) = {5+0.047586/2,_tSpn,0.5+0.034803/2};
+    Point(345) = {5+0.035112/2,_tSpn,0.5+0.030473/2};
+    Point(346) = {5+0.024472/2,_tSpn,0.5+0.025893/2};
+    Point(347) = {5+0.015708/2,_tSpn,0.5+0.021088/2};
+    Point(348) = {5+0.008856/2,_tSpn,0.5+0.016078/2};
+    Point(349) = {5+0.003943/2,_tSpn,0.5+0.010884/2};
+    Point(350) = {5+0.000987/2,_tSpn,0.5+0.005521/2};
+    Point(351) = {5+0.000000/2,_tSpn,0.5+0.000000/2};
+    Point(352) = {5+0.000987/2,_tSpn,0.5-0.005521/2};
+    Point(353) = {5+0.003943/2,_tSpn,0.5-0.010884/2};
+    Point(354) = {5+0.008856/2,_tSpn,0.5-0.016078/2};
+    Point(355) = {5+0.015708/2,_tSpn,0.5-0.021088/2};
+    Point(356) = {5+0.024472/2,_tSpn,0.5-0.025893/2};
+    Point(357) = {5+0.035112/2,_tSpn,0.5-0.030473/2};
+    Point(358) = {5+0.047586/2,_tSpn,0.5-0.034803/2};
+    Point(359) = {5+0.061847/2,_tSpn,0.5-0.038859/2};
+    Point(360) = {5+0.077836/2,_tSpn,0.5-0.042615/2};
+    Point(361) = {5+0.095492/2,_tSpn,0.5-0.046049/2};
+    Point(362) = {5+0.114743/2,_tSpn,0.5-0.049138/2};
+    Point(363) = {5+0.135516/2,_tSpn,0.5-0.051862/2};
+    Point(364) = {5+0.157726/2,_tSpn,0.5-0.054206/2};
+    Point(365) = {5+0.181288/2,_tSpn,0.5-0.056159/2};
+    Point(366) = {5+0.206107/2,_tSpn,0.5-0.057712/2};
+    Point(367) = {5+0.232087/2,_tSpn,0.5-0.058863/2};
+    Point(368) = {5+0.259123/2,_tSpn,0.5-0.059614/2};
+    Point(369) = {5+0.287110/2,_tSpn,0.5-0.059971/2};
+    Point(370) = {5+0.315938/2,_tSpn,0.5-0.059947/2};
+    Point(371) = {5+0.345492/2,_tSpn,0.5-0.059557/2};
+    Point(372) = {5+0.375655/2,_tSpn,0.5-0.058819/2};
+    Point(373) = {5+0.406309/2,_tSpn,0.5-0.057755/2};
+    Point(374) = {5+0.437333/2,_tSpn,0.5-0.056390/2};
+    Point(375) = {5+0.468605/2,_tSpn,0.5-0.054749/2};
+    Point(376) = {5+0.500000/2,_tSpn,0.5-0.052862/2};
+    Point(377) = {5+0.531395/2,_tSpn,0.5-0.050754/2};
+    Point(378) = {5+0.562667/2,_tSpn,0.5-0.048455/2};
+    Point(379) = {5+0.593691/2,_tSpn,0.5-0.045992/2};
+    Point(380) = {5+0.624345/2,_tSpn,0.5-0.043394/2};
+    Point(381) = {5+0.654508/2,_tSpn,0.5-0.040686/2};
+    Point(382) = {5+0.684062/2,_tSpn,0.5-0.037896/2};
+    Point(383) = {5+0.712890/2,_tSpn,0.5-0.035048/2};
+    Point(384) = {5+0.740877/2,_tSpn,0.5-0.032168/2};
+    Point(385) = {5+0.767913/2,_tSpn,0.5-0.029279/2};
+    Point(386) = {5+0.793893/2,_tSpn,0.5-0.026405/2};
+    Point(387) = {5+0.818712/2,_tSpn,0.5-0.023569/2};
+    Point(388) = {5+0.842274/2,_tSpn,0.5-0.020795/2};
+    Point(389) = {5+0.864484/2,_tSpn,0.5-0.018106/2};
+    Point(390) = {5+0.885257/2,_tSpn,0.5-0.015523/2};
+    Point(391) = {5+0.904508/2,_tSpn,0.5-0.013071/2};
+    Point(392) = {5+0.922164/2,_tSpn,0.5-0.010770/2};
+    Point(393) = {5+0.938153/2,_tSpn,0.5-0.008643/2};
+    Point(394) = {5+0.952414/2,_tSpn,0.5-0.006710/2};
+    Point(395) = {5+0.964888/2,_tSpn,0.5-0.004990/2};
+    Point(396) = {5+0.975528/2,_tSpn,0.5-0.003501/2};
+    Point(397) = {5+0.984292/2,_tSpn,0.5-0.002260/2};
+    Point(398) = {5+0.991144/2,_tSpn,0.5-0.001280/2};
+    Point(399) = {5+0.996057/2,_tSpn,0.5-0.000572/2};
+    Point(400) = {5+0.999013/2,_tSpn,0.5-0.000143/2};
 
     Point(402) = {5+1.000000/2,tSpn/2,0.5+0.000000/2};
     Point(403) = {5+0.999013/2,tSpn/2,0.5+0.000143/2};
@@ -427,10 +444,10 @@ EndIf
 
 // Field
 If(fWdt != 0)
-    Point(1000001) = {-2, -fWdt/2, -2};
-    Point(1000002) = {8, -fWdt/2, -2};
-    Point(1000003) = {8, -fWdt/2, 2};
-    Point(1000004) = {-2, -fWdt/2, 2};
+    Point(1000001) = {-2, _fWdt, -2};
+    Point(1000002) = {8, _fWdt, -2};
+    Point(1000003) = {8, _fWdt, 2};
+    Point(1000004) = {-2, _fWdt, 2};
     Point(1000005) = {-2, fWdt/2, -2};
     Point(1000006) = {8, fWdt/2, -2};
     Point(1000007) = {8, fWdt/2, 2};
diff --git a/fpm/models/n0012.py b/fpm/models/n0012.py
index aecc964..bb51b25 100644
--- a/fpm/models/n0012.py
+++ b/fpm/models/n0012.py
@@ -31,7 +31,7 @@ def main(p, tail=False, field=False):
     print(ccolors.ANSI_BLUE + 'PyMeshing...' + ccolors.ANSI_RESET)
     msh = gmsh.MeshLoader('n0012.geo', __file__).execute(**p['pars'])
     # problem
-    pbl = fpm.Problem(msh, p['aoa'], p['aos'], p['mach'], p['spn'], 1, 0, 0, 0)
+    pbl = fpm.Problem(msh, p['aoa'], p['aos'], p['mach'], p['spn'], 1, 0, 0, 0, bool(p['sym']))
     wing = fpm.Body(msh, 'wing', ['wTe'], 10)
     pbl.add(wing)
     if tail:
@@ -52,6 +52,8 @@ def main(p, tail=False, field=False):
     tms['total'].stop()
     print(ccolors.ANSI_BLUE + 'PyTiming...' + ccolors.ANSI_RESET)
     print(tms)
+    # return
+    return slv
 
 if __name__ == '__main__':
     main()
diff --git a/fpm/models/oneraM6.geo b/fpm/models/oneraM6.geo
new file mode 100644
index 0000000..418d940
--- /dev/null
+++ b/fpm/models/oneraM6.geo
@@ -0,0 +1,327 @@
+/* Onera M6 wing */
+
+// Mesh parameters
+// domain and mesh
+DefineConstant[ wNc = { 50, Name "Number of panel along chord" }
+                wNs = { 10, Name "Number of panel along span" }
+                bC = { 0.05, Name "Progression along chord" }
+                pS = { 1.0, Name "Progression along span" } ];
+
+//// GEOMETRY
+/// Points
+// Airfoil 1: oneraM6, 143 points 
+Point(1) = {0.805900,0.000000,0.000000};
+Point(2) = {0.804129,0.000000,0.000232};
+Point(3) = {0.802038,0.000000,0.000505};
+Point(4) = {0.799569,0.000000,0.000828};
+Point(5) = {0.796652,0.000000,0.001210};
+Point(6) = {0.793208,0.000000,0.001661};
+Point(7) = {0.789139,0.000000,0.002193};
+Point(8) = {0.784331,0.000000,0.002822};
+Point(9) = {0.778649,0.000000,0.003565};
+Point(10) = {0.771932,0.000000,0.004444};
+Point(11) = {0.763989,0.000000,0.005483};
+Point(12) = {0.754592,0.000000,0.006710};
+Point(13) = {0.743470,0.000000,0.008151};
+Point(14) = {0.730299,0.000000,0.009828};
+Point(15) = {0.714691,0.000000,0.011690};
+Point(16) = {0.696182,0.000000,0.013774};
+Point(17) = {0.677546,0.000000,0.015783};
+Point(18) = {0.658809,0.000000,0.017717};
+Point(19) = {0.639970,0.000000,0.019586};
+Point(20) = {0.621026,0.000000,0.021397};
+Point(21) = {0.601980,0.000000,0.023159};
+Point(22) = {0.582826,0.000000,0.024875};
+Point(23) = {0.563568,0.000000,0.026547};
+Point(24) = {0.544202,0.000000,0.028170};
+Point(25) = {0.524729,0.000000,0.029737};
+Point(26) = {0.505147,0.000000,0.031238};
+Point(27) = {0.485455,0.000000,0.032658};
+Point(28) = {0.465652,0.000000,0.033984};
+Point(29) = {0.445738,0.000000,0.035197};
+Point(30) = {0.425711,0.000000,0.036282};
+Point(31) = {0.405570,0.000000,0.037225};
+Point(32) = {0.385317,0.000000,0.038011};
+Point(33) = {0.364947,0.000000,0.038631};
+Point(34) = {0.344460,0.000000,0.039073};
+Point(35) = {0.323856,0.000000,0.039344};
+Point(36) = {0.303135,0.000000,0.039432};
+Point(37) = {0.282293,0.000000,0.039343};
+Point(38) = {0.261331,0.000000,0.039078};
+Point(39) = {0.240248,0.000000,0.038642};
+Point(40) = {0.219042,0.000000,0.038037};
+Point(41) = {0.197712,0.000000,0.037261};
+Point(42) = {0.176258,0.000000,0.036306};
+Point(43) = {0.154679,0.000000,0.035154};
+Point(44) = {0.132972,0.000000,0.033774};
+Point(45) = {0.111131,0.000000,0.032117};
+Point(46) = {0.092662,0.000000,0.030457};
+Point(47) = {0.077073,0.000000,0.028830};
+Point(48) = {0.063937,0.000000,0.027269};
+Point(49) = {0.052891,0.000000,0.025800};
+Point(50) = {0.043619,0.000000,0.024441};
+Point(51) = {0.035851,0.000000,0.023203};
+Point(52) = {0.029356,0.000000,0.022027};
+Point(53) = {0.023936,0.000000,0.020812};
+Point(54) = {0.019428,0.000000,0.019503};
+Point(55) = {0.015684,0.000000,0.018096};
+Point(56) = {0.012586,0.000000,0.016619};
+Point(57) = {0.010032,0.000000,0.015114};
+Point(58) = {0.007931,0.000000,0.013618};
+Point(59) = {0.006214,0.000000,0.012165};
+Point(60) = {0.004815,0.000000,0.010776};
+Point(61) = {0.003683,0.000000,0.009463};
+Point(62) = {0.002775,0.000000,0.008233};
+Point(63) = {0.002050,0.000000,0.007089};
+Point(64) = {0.001480,0.000000,0.006027};
+Point(65) = {0.001037,0.000000,0.005045};
+Point(66) = {0.000698,0.000000,0.004138};
+Point(67) = {0.000444,0.000000,0.003301};
+Point(68) = {0.000260,0.000000,0.002529};
+Point(69) = {0.000135,0.000000,0.001818};
+Point(70) = {0.000056,0.000000,0.001162};
+Point(71) = {0.000013,0.000000,0.000557};
+Point(72) = {0.000000,0.000000,0.000000};
+Point(73) = {0.000013,0.000000,-0.000557};
+Point(74) = {0.000056,0.000000,-0.001162};
+Point(75) = {0.000135,0.000000,-0.001818};
+Point(76) = {0.000260,0.000000,-0.002529};
+Point(77) = {0.000444,0.000000,-0.003301};
+Point(78) = {0.000698,0.000000,-0.004138};
+Point(79) = {0.001037,0.000000,-0.005045};
+Point(80) = {0.001480,0.000000,-0.006027};
+Point(81) = {0.002050,0.000000,-0.007089};
+Point(82) = {0.002775,0.000000,-0.008233};
+Point(83) = {0.003683,0.000000,-0.009463};
+Point(84) = {0.004815,0.000000,-0.010776};
+Point(85) = {0.006214,0.000000,-0.012165};
+Point(86) = {0.007931,0.000000,-0.013618};
+Point(87) = {0.010032,0.000000,-0.015114};
+Point(88) = {0.012586,0.000000,-0.016619};
+Point(89) = {0.015684,0.000000,-0.018096};
+Point(90) = {0.019428,0.000000,-0.019503};
+Point(91) = {0.023936,0.000000,-0.020812};
+Point(92) = {0.029356,0.000000,-0.022027};
+Point(93) = {0.035851,0.000000,-0.023203};
+Point(94) = {0.043619,0.000000,-0.024441};
+Point(95) = {0.052891,0.000000,-0.025800};
+Point(96) = {0.063937,0.000000,-0.027269};
+Point(97) = {0.077073,0.000000,-0.028830};
+Point(98) = {0.092662,0.000000,-0.030457};
+Point(99) = {0.111131,0.000000,-0.032117};
+Point(100) = {0.132972,0.000000,-0.033774};
+Point(101) = {0.154679,0.000000,-0.035154};
+Point(102) = {0.176258,0.000000,-0.036306};
+Point(103) = {0.197712,0.000000,-0.037261};
+Point(104) = {0.219042,0.000000,-0.038037};
+Point(105) = {0.240248,0.000000,-0.038642};
+Point(106) = {0.261331,0.000000,-0.039078};
+Point(107) = {0.282293,0.000000,-0.039343};
+Point(108) = {0.303135,0.000000,-0.039432};
+Point(109) = {0.323856,0.000000,-0.039344};
+Point(110) = {0.344460,0.000000,-0.039073};
+Point(111) = {0.364947,0.000000,-0.038631};
+Point(112) = {0.385317,0.000000,-0.038011};
+Point(113) = {0.405570,0.000000,-0.037225};
+Point(114) = {0.425711,0.000000,-0.036282};
+Point(115) = {0.445738,0.000000,-0.035197};
+Point(116) = {0.465652,0.000000,-0.033984};
+Point(117) = {0.485455,0.000000,-0.032658};
+Point(118) = {0.505147,0.000000,-0.031238};
+Point(119) = {0.524729,0.000000,-0.029737};
+Point(120) = {0.544202,0.000000,-0.028170};
+Point(121) = {0.563568,0.000000,-0.026547};
+Point(122) = {0.582826,0.000000,-0.024875};
+Point(123) = {0.601980,0.000000,-0.023159};
+Point(124) = {0.621026,0.000000,-0.021397};
+Point(125) = {0.639970,0.000000,-0.019586};
+Point(126) = {0.658809,0.000000,-0.017717};
+Point(127) = {0.677546,0.000000,-0.015783};
+Point(128) = {0.696182,0.000000,-0.013774};
+Point(129) = {0.714691,0.000000,-0.011690};
+Point(130) = {0.730299,0.000000,-0.009828};
+Point(131) = {0.743470,0.000000,-0.008151};
+Point(132) = {0.754592,0.000000,-0.006710};
+Point(133) = {0.763989,0.000000,-0.005483};
+Point(134) = {0.771932,0.000000,-0.004444};
+Point(135) = {0.778649,0.000000,-0.003565};
+Point(136) = {0.784331,0.000000,-0.002822};
+Point(137) = {0.789139,0.000000,-0.002193};
+Point(138) = {0.793208,0.000000,-0.001661};
+Point(139) = {0.796652,0.000000,-0.001210};
+Point(140) = {0.799569,0.000000,-0.000828};
+Point(141) = {0.802038,0.000000,-0.000505};
+Point(142) = {0.804129,0.000000,-0.000232};
+// Airfoil 2: oneraM6, 143 points 
+Point(144) = {1.143427,1.196000,0.000000};
+Point(145) = {1.142432,1.196000,0.000130};
+Point(146) = {1.141256,1.196000,0.000284};
+Point(147) = {1.139869,1.196000,0.000466};
+Point(148) = {1.138230,1.196000,0.000680};
+Point(149) = {1.136294,1.196000,0.000933};
+Point(150) = {1.134007,1.196000,0.001232};
+Point(151) = {1.131305,1.196000,0.001586};
+Point(152) = {1.128112,1.196000,0.002004};
+Point(153) = {1.124337,1.196000,0.002498};
+Point(154) = {1.119873,1.196000,0.003082};
+Point(155) = {1.114592,1.196000,0.003771};
+Point(156) = {1.108341,1.196000,0.004581};
+Point(157) = {1.100939,1.196000,0.005523};
+Point(158) = {1.092167,1.196000,0.006570};
+Point(159) = {1.081765,1.196000,0.007741};
+Point(160) = {1.071292,1.196000,0.008870};
+Point(161) = {1.060762,1.196000,0.009957};
+Point(162) = {1.050174,1.196000,0.011007};
+Point(163) = {1.039528,1.196000,0.012025};
+Point(164) = {1.028824,1.196000,0.013015};
+Point(165) = {1.018059,1.196000,0.013980};
+Point(166) = {1.007236,1.196000,0.014919};
+Point(167) = {0.996353,1.196000,0.015831};
+Point(168) = {0.985409,1.196000,0.016712};
+Point(169) = {0.974403,1.196000,0.017556};
+Point(170) = {0.963336,1.196000,0.018354};
+Point(171) = {0.952208,1.196000,0.019099};
+Point(172) = {0.941016,1.196000,0.019781};
+Point(173) = {0.929760,1.196000,0.020391};
+Point(174) = {0.918441,1.196000,0.020920};
+Point(175) = {0.907059,1.196000,0.021362};
+Point(176) = {0.895611,1.196000,0.021711};
+Point(177) = {0.884097,1.196000,0.021959};
+Point(178) = {0.872518,1.196000,0.022111};
+Point(179) = {0.860873,1.196000,0.022161};
+Point(180) = {0.849160,1.196000,0.022111};
+Point(181) = {0.837379,1.196000,0.021962};
+Point(182) = {0.825530,1.196000,0.021717};
+Point(183) = {0.813612,1.196000,0.021377};
+Point(184) = {0.801625,1.196000,0.020941};
+Point(185) = {0.789568,1.196000,0.020404};
+Point(186) = {0.777440,1.196000,0.019757};
+Point(187) = {0.765241,1.196000,0.018981};
+Point(188) = {0.752966,1.196000,0.018050};
+Point(189) = {0.742587,1.196000,0.017117};
+Point(190) = {0.733826,1.196000,0.016203};
+Point(191) = {0.726444,1.196000,0.015325};
+Point(192) = {0.720236,1.196000,0.014500};
+Point(193) = {0.715025,1.196000,0.013736};
+Point(194) = {0.710659,1.196000,0.013040};
+Point(195) = {0.707009,1.196000,0.012379};
+Point(196) = {0.703963,1.196000,0.011696};
+Point(197) = {0.701429,1.196000,0.010961};
+Point(198) = {0.699325,1.196000,0.010170};
+Point(199) = {0.697584,1.196000,0.009340};
+Point(200) = {0.696149,1.196000,0.008494};
+Point(201) = {0.694968,1.196000,0.007654};
+Point(202) = {0.694003,1.196000,0.006837};
+Point(203) = {0.693217,1.196000,0.006056};
+Point(204) = {0.692581,1.196000,0.005318};
+Point(205) = {0.692070,1.196000,0.004627};
+Point(206) = {0.691663,1.196000,0.003984};
+Point(207) = {0.691343,1.196000,0.003387};
+Point(208) = {0.691094,1.196000,0.002835};
+Point(209) = {0.690903,1.196000,0.002325};
+Point(210) = {0.690760,1.196000,0.001855};
+Point(211) = {0.690657,1.196000,0.001421};
+Point(212) = {0.690587,1.196000,0.001022};
+Point(213) = {0.690542,1.196000,0.000653};
+Point(214) = {0.690518,1.196000,0.000313};
+Point(215) = {0.690511,1.196000,0.000000};
+Point(216) = {0.690518,1.196000,-0.000313};
+Point(217) = {0.690542,1.196000,-0.000653};
+Point(218) = {0.690587,1.196000,-0.001022};
+Point(219) = {0.690657,1.196000,-0.001421};
+Point(220) = {0.690760,1.196000,-0.001855};
+Point(221) = {0.690903,1.196000,-0.002325};
+Point(222) = {0.691094,1.196000,-0.002835};
+Point(223) = {0.691343,1.196000,-0.003387};
+Point(224) = {0.691663,1.196000,-0.003984};
+Point(225) = {0.692070,1.196000,-0.004627};
+Point(226) = {0.692581,1.196000,-0.005318};
+Point(227) = {0.693217,1.196000,-0.006056};
+Point(228) = {0.694003,1.196000,-0.006837};
+Point(229) = {0.694968,1.196000,-0.007654};
+Point(230) = {0.696149,1.196000,-0.008494};
+Point(231) = {0.697584,1.196000,-0.009340};
+Point(232) = {0.699325,1.196000,-0.010170};
+Point(233) = {0.701429,1.196000,-0.010961};
+Point(234) = {0.703963,1.196000,-0.011696};
+Point(235) = {0.707009,1.196000,-0.012379};
+Point(236) = {0.710659,1.196000,-0.013040};
+Point(237) = {0.715025,1.196000,-0.013736};
+Point(238) = {0.720236,1.196000,-0.014500};
+Point(239) = {0.726444,1.196000,-0.015325};
+Point(240) = {0.733826,1.196000,-0.016203};
+Point(241) = {0.742587,1.196000,-0.017117};
+Point(242) = {0.752966,1.196000,-0.018050};
+Point(243) = {0.765241,1.196000,-0.018981};
+Point(244) = {0.777440,1.196000,-0.019757};
+Point(245) = {0.789568,1.196000,-0.020404};
+Point(246) = {0.801625,1.196000,-0.020941};
+Point(247) = {0.813612,1.196000,-0.021377};
+Point(248) = {0.825530,1.196000,-0.021717};
+Point(249) = {0.837379,1.196000,-0.021962};
+Point(250) = {0.849160,1.196000,-0.022111};
+Point(251) = {0.860873,1.196000,-0.022161};
+Point(252) = {0.872518,1.196000,-0.022111};
+Point(253) = {0.884097,1.196000,-0.021959};
+Point(254) = {0.895611,1.196000,-0.021711};
+Point(255) = {0.907059,1.196000,-0.021362};
+Point(256) = {0.918441,1.196000,-0.020920};
+Point(257) = {0.929760,1.196000,-0.020391};
+Point(258) = {0.941016,1.196000,-0.019781};
+Point(259) = {0.952208,1.196000,-0.019099};
+Point(260) = {0.963336,1.196000,-0.018354};
+Point(261) = {0.974403,1.196000,-0.017556};
+Point(262) = {0.985409,1.196000,-0.016712};
+Point(263) = {0.996353,1.196000,-0.015831};
+Point(264) = {1.007236,1.196000,-0.014919};
+Point(265) = {1.018059,1.196000,-0.013980};
+Point(266) = {1.028824,1.196000,-0.013015};
+Point(267) = {1.039528,1.196000,-0.012025};
+Point(268) = {1.050174,1.196000,-0.011007};
+Point(269) = {1.060762,1.196000,-0.009957};
+Point(270) = {1.071292,1.196000,-0.008870};
+Point(271) = {1.081765,1.196000,-0.007741};
+Point(272) = {1.092167,1.196000,-0.006570};
+Point(273) = {1.100939,1.196000,-0.005523};
+Point(274) = {1.108341,1.196000,-0.004581};
+Point(275) = {1.114592,1.196000,-0.003771};
+Point(276) = {1.119873,1.196000,-0.003082};
+Point(277) = {1.124337,1.196000,-0.002498};
+Point(278) = {1.128112,1.196000,-0.002004};
+Point(279) = {1.131305,1.196000,-0.001586};
+Point(280) = {1.134007,1.196000,-0.001232};
+Point(281) = {1.136294,1.196000,-0.000933};
+Point(282) = {1.138230,1.196000,-0.000680};
+Point(283) = {1.139869,1.196000,-0.000466};
+Point(284) = {1.141256,1.196000,-0.000284};
+Point(285) = {1.142432,1.196000,-0.000130};
+
+/// Lines
+// Airfoil 1:
+Spline(1) = {1:72};
+Spline(2) = {72:142,1};
+// Airfoil 2:
+Spline(3) = {144:215};
+Spline(4) = {215:285,144};
+// Airfoil 1 to airfoil 2:
+Line(61) = {1,144};
+Line(62) = {72,215};
+
+/// Line loops & Surfaces
+// Wing 1:
+Line Loop(11) = {1,62,-3,-61};
+Line Loop(12) = {2,61,-4,-62};
+Surface(11) = {-11};
+Surface(12) = {-12};
+
+//// MESH
+Transfinite Line{1,2,3,4} = wNc + 1 Using Bump bC;
+Transfinite Line{61,62} = wNs + 1 Using Progression pS;
+Transfinite Surface{11,12};
+Recombine Surface{11,12};
+
+//// PHYSICAL GROUPS
+// Trailing edge
+Physical Line("wTe") = {61};
+// Wing:
+Physical Surface("wing") = {11,12};
diff --git a/fpm/src/fBody.cpp b/fpm/src/fBody.cpp
index 571fb58..2b9bf3e 100644
--- a/fpm/src/fBody.cpp
+++ b/fpm/src/fBody.cpp
@@ -30,17 +30,6 @@ using namespace fpm;
 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)
 {
-    // Get nodes
-    for (auto e : tag->elems)
-        for (auto n : e->nodes)
-            nodes.push_back(n);
-    std::sort(nodes.begin(), nodes.end());
-    nodes.erase(std::unique(nodes.begin(), nodes.end()), nodes.end());
-    // Associate each node to its adjacent elements
-    for (auto e : tag->elems)
-        for (auto n : e->nodes)
-            neMap[n].push_back(e);
-
     // If wakes are requested, check if they are already available, otherwise create them
     bool hasChanged = false;
     size_t j = 0;
@@ -85,14 +74,36 @@ Body::Body(std::shared_ptr<MshData> _msh, std::string const &id,
                 wkNodes.push_back(nN);
                 msh->nodes.push_back(nN);
             }
-            // Create wake elements
+            // Create wake elements and wake
             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));
+            // Duplicate TE nodes and swap thos of lower wing elements
+            std::map<Node *, Node *> teMap;
+            for (auto n : teNodes)
+            {
+                Node *nN = new Node(msh->nodes.back()->no + 1, n->pos);
+                teMap[n] = nN;
+                msh->nodes.push_back(nN);
+            }
+            for (auto p : wakes.back()->wkMap)
+            {
+                Element *lwe = p.second.second;
+                for (size_t i = 0; i < lwe->nodes.size(); ++i)
+                {
+                    try
+                    {
+                        lwe->nodes[i] = teMap.at(lwe->nodes[i]);
+                    }
+                    catch (const std::out_of_range &)
+                    {
+                        //std::cout << lwe->nodes[i]->no << "not found in map!\n";
+                    }
+                }
+            }
             std::cout << *wakes.back() << " created. " << std::flush;
         }
         ++j;
@@ -104,6 +115,17 @@ Body::Body(std::shared_ptr<MshData> _msh, std::string const &id,
         writer.save(_msh->name);
     }
 
+    // Get nodes (now that they have been duplicated at lower TE)
+    for (auto e : tag->elems)
+        for (auto n : e->nodes)
+            nodes.push_back(n);
+    std::sort(nodes.begin(), nodes.end());
+    nodes.erase(std::unique(nodes.begin(), nodes.end()), nodes.end());
+    // Associate each node to its adjacent elements
+    for (auto e : tag->elems)
+        for (auto n : e->nodes)
+            neMap[n].push_back(e);
+
     // Size load vectors
     cLoadX.resize(nodes.size());
     cLoadY.resize(nodes.size());
diff --git a/fpm/src/fBuilder.cpp b/fpm/src/fBuilder.cpp
index 720b3b8..fe5a573 100644
--- a/fpm/src/fBuilder.cpp
+++ b/fpm/src/fBuilder.cpp
@@ -22,6 +22,7 @@
 
 #include "wTag.h"
 #include "wElement.h"
+#include "wNode.h"
 #include "wMem.h"
 
 #define PI 3.14159
@@ -31,13 +32,12 @@ using namespace fpm;
 
 /**
  * @brief Initialize the solver and perform sanity checks
- * @authors Adrien Crovato
+ * @authors Adrien Crovato & Axel Dechamps
  */
 Builder::Builder(std::shared_ptr<Problem> _pbl) : pbl(_pbl), valid(false)
 {
-    // Check the problem and update element memory
+    // Check the problem
     pbl->check();
-    pbl->updateMem();
 
     // Setup AIC matrices
     pbl->field ? nF = pbl->field->tag->elems.size() : nF = 0;
@@ -50,15 +50,8 @@ Builder::Builder(std::shared_ptr<Problem> _pbl) : pbl(_pbl), valid(false)
     Cx = Eigen::MatrixXd::Zero(nP, nF);
     Cy = Eigen::MatrixXd::Zero(nP, nF);
     Cz = Eigen::MatrixXd::Zero(nP, nF);
-}
 
-/**
- * @brief Fill AIC matrices
- */
-void Builder::run()
-{
     // global->local id
-    std::map<Element *, int> rows;
     int i = 0;
     for (auto body : pbl->bodies)
         for (auto e : body->tag->elems)
@@ -66,28 +59,175 @@ void Builder::run()
             rows[e] = i;
             i++;
         }
+}
 
-    // body to body
-    for (auto ibody : pbl->bodies)
-        for (auto ei : ibody->tag->elems)
+/**
+ * @brief Fill AIC matrices
+ */
+void Builder::run()
+{
+    // Update element memory
+    pbl->updateMem();
+
+    // body to body - body panels
+    for (auto jbody : pbl->bodies)
+    {
+        for (auto ej : jbody->tag->elems)
         {
+            // Initialisation of storage variables for the iteration
+            Eigen::Matrix3d glob2loc;
+            size_t nVertices = ej->nodes.size();
+            Eigen::MatrixXd trsfCoordSrc(3, nVertices);
+
+            // Computations related to source panels
+            glob2loc = Glob2loc(false, ej); // Transformation matrix
+            trsfCoordSrc = TrsfCoordSrc(false, ej, glob2loc); // Local coordinates of the source panel
+
             // body panels
-            for (auto jbody : pbl->bodies)
-                for (auto ej : jbody->tag->elems)
+            for (auto ibody : pbl->bodies)
+            {
+                for (auto ei : ibody->tag->elems)
+                {
+                    // Initialisation of storage variables for the iteration
+                    Eigen::Vector3d trsfCoordTgt;
+                    Eigen::Vector3d distance;
+                    Eigen::Vector2d mutau;
+
+                    // Computations related to target panels
+                    trsfCoordTgt = TrsfCoordTgt(false, ei, ej, glob2loc); // Local coordinates of the target panel
+                    distance = Distance(false, ei, ej, glob2loc); // Distances between source and target panels
+                    mutau = MuTau(false, ei, ej, trsfCoordSrc, trsfCoordTgt, distance); // Surface AIC
+
+                    // Assembly of the matrices
+                    A(rows.at(ei), rows.at(ej)) = mutau(0);
+                    B(rows.at(ei), rows.at(ej)) = mutau(1);
+                }
+            }
+        }
+    }
+
+    // body to body - body panels - symmetry
+    if (pbl->symY)
+    {
+        for (auto jbody : pbl->bodies)
+        {
+            for (auto ej : jbody->tag->elems)
+            {
+                // Initialisation of storage variables for the iteration
+                Eigen::Matrix3d glob2loc;
+                size_t nVertices = ej->nodes.size();
+                Eigen::MatrixXd trsfCoordSrc(3, nVertices);
+
+                // Computations related to source panels
+                glob2loc = Glob2loc(true, ej); // Transformation matrix
+                trsfCoordSrc = TrsfCoordSrc(true, ej, glob2loc); // Local coordinates of the source panel
+
+                // body panels
+                for (auto ibody : pbl->bodies)
+                {
+                    for (auto ei : ibody->tag->elems)
+                    {
+                        // Initialisation of storage variables for the iteration
+                        Eigen::Vector3d trsfCoordTgt;
+                        Eigen::Vector3d distance;
+                        Eigen::Vector2d mutau;
+
+                        // Computations related to target panels
+                        trsfCoordTgt = TrsfCoordTgt(true, ei, ej, glob2loc); // Local coordinates of the target panel
+                        distance = Distance(true, ei, ej, glob2loc); // Distances between source and target panels
+                        mutau = MuTau(true, ei, ej, trsfCoordSrc, trsfCoordTgt, distance); // Surface AIC
+
+                        // Assembly of the matrices
+                        A(rows.at(ei), rows.at(ej)) -= mutau(0);
+                        B(rows.at(ei), rows.at(ej)) -= mutau(1);
+                    }
+                }
+            }
+        }
+    }
+
+    // body to body - wake panels
+    for (auto jbody : pbl->bodies)
+    {
+        for (auto wake : jbody->wakes)
+        {
+            for (auto ew : wake->tag->elems)
+            {
+                // Initialisation of storage variables for the iteration
+                Eigen::Matrix3d glob2loc;
+                size_t nVertices = ew->nodes.size();
+                Eigen::MatrixXd trsfCoordSrc(3, nVertices);
+
+                // Computations related to source panels
+                glob2loc = Glob2loc(false, ew); // Transformation matrix
+                trsfCoordSrc = TrsfCoordSrc(false, ew, glob2loc); // Local coordinates of the source panel
+
+                // wake panels
+                for (auto ibody : pbl->bodies)
                 {
-                    A(rows.at(ei), rows.at(ej)) = mu(ei, ej);
-                    B(rows.at(ei), rows.at(ej)) = tau(ei, ej);
+                    for (auto ei : ibody->tag->elems)
+                    {
+                        // Initialisation of storage variables for the iteration
+                        Eigen::Vector3d trsfCoordTgt;
+                        Eigen::Vector3d distance;
+                        Eigen::Vector2d wmutau;
+
+                        // Computations related to target panels
+                        trsfCoordTgt = TrsfCoordTgt(false, ei, ew, glob2loc); // Local coordinates of the target panel
+                        distance = Distance(false, ei, ew, glob2loc); // Distances between source and target panels
+                        wmutau = MuTau(false, ei, ew, trsfCoordSrc, trsfCoordTgt, distance); // Surface AIC
+
+                        // Assembly of the matrices
+                        A(rows.at(ei), rows.at(wake->wkMap.at(ew).first)) += wmutau(0);
+                        A(rows.at(ei), rows.at(wake->wkMap.at(ew).second)) -= wmutau(0);
+                    }
                 }
-            // wake panels
-            for (auto jbody : pbl->bodies)
-                for (auto wake : jbody->wakes)
-                    for (auto ew : wake->tag->elems)
+            }
+        }
+    }
+
+    // body to body - wake panels - symmetry
+    if (pbl->symY)
+    {
+        for (auto jbody : pbl->bodies)
+        {
+            for (auto wake : jbody->wakes)
+            {
+                for (auto ew : wake->tag->elems)
+                {
+                    // Initialisation of storage variables for the iteration
+                    Eigen::Matrix3d glob2loc;
+                    size_t nVertices = ew->nodes.size();
+                    Eigen::MatrixXd trsfCoordSrc(3, nVertices);
+
+                    // Computations related to source panels
+                    glob2loc = Glob2loc(true, ew); // Transformation matrix
+                    trsfCoordSrc = TrsfCoordSrc(true, ew, glob2loc); // Local coordinates of the source panel
+
+                    // wake panels
+                    for (auto ibody : pbl->bodies)
                     {
-                        double wmu = mu(ei, ew);
-                        A(rows.at(ei), rows.at(wake->wkMap.at(ew).first)) += wmu;
-                        A(rows.at(ei), rows.at(wake->wkMap.at(ew).second)) -= wmu;
+                        for (auto ei : ibody->tag->elems)
+                        {
+                            // Initialisation of storage variables for the iteration
+                            Eigen::Vector3d trsfCoordTgt;
+                            Eigen::Vector3d distance;
+                            Eigen::Vector2d wmutau;
+
+                            // Computations related to target panels
+                            trsfCoordTgt = TrsfCoordTgt(true, ei, ew, glob2loc); // Local coordinates of the target panel
+                            distance = Distance(true, ei, ew, glob2loc); // Distances between source and target panels
+                            wmutau = MuTau(true, ei, ew, trsfCoordSrc, trsfCoordTgt, distance); // Surface AIC
+
+                            // Assembly of the matrices
+                            A(rows.at(ei), rows.at(wake->wkMap.at(ew).first)) -= wmutau(0);
+                            A(rows.at(ei), rows.at(wake->wkMap.at(ew).second)) += wmutau(0);
+                        }
                     }
+                }
+            }
         }
+    }
 
     // field to body
     if (pbl->field)
@@ -98,58 +238,316 @@ void Builder::run()
                     C(rows.at(e), j) = sigma(e, pbl->field->tag->elems[j]);
                     Cx(rows.at(e), j) = sigmaX(e, pbl->field->tag->elems[j]);
                     Cy(rows.at(e), j) = sigmaY(e, pbl->field->tag->elems[j]);
-                    Cz(rows.at(e), j) = sigmaY(e, pbl->field->tag->elems[j]);
+                    Cz(rows.at(e), j) = sigmaZ(e, pbl->field->tag->elems[j]);
                 }
 
     valid = true;
 }
 
 /**
- * @brief Compute doublet AIC from body panel ej to body panel ei
+ * @brief Compute unit axes of the source panel
  */
-double Builder::mu(Element *ei, Element *ej)
+Eigen::Matrix3d Builder::Glob2loc(bool symy, Element *ej)
 {
-    if (ei == ej)
-        return 0.5;
-    else
-        return 0;
+    Eigen::Matrix3d glob2loc; // Transformation matrix
+    Eigen::Vector3d n = ej->normal(); // Unit normal vector of the source panel
+
+    // Computation of the unit longitudinal vector - Source panel
+    Eigen::Vector3d cg = ej->getVMem().getCg();
+    Eigen::Vector3d x0 = ej->nodes[0]->pos; // Coordinates of node 1
+    Eigen::Vector3d l = (x0 - cg).normalized();
+
+    // Computation of the unit perpendicular vector - Source panel
+    Eigen::Vector3d p = (n.cross(l)).normalized();
+
+    // Handling of the outputs
+    glob2loc.row(0) = l;
+    glob2loc.row(1) = p;
+    glob2loc.row(2) = n;
+
+    if(symy)
+    {
+        glob2loc.row(1) = -glob2loc.row(1);
+        glob2loc.col(1) = -glob2loc.col(1);
+    }
+
+    return glob2loc;
+}
+
+/**
+ * @brief Compute coordinates of the source panel from global to local axes
+ */
+Eigen::MatrixXd Builder::TrsfCoordSrc(bool symy, Element *ej, Eigen::Matrix3d const &glob2loc)
+{
+    size_t nVertices = ej->nodes.size();
+    Eigen::MatrixXd trsfCoordSrc(3, nVertices);
+    Eigen::MatrixXd coords(3, nVertices);
+
+    for (int i = 0; i < 3; ++i)
+        for (int j = 0; j < nVertices; ++j)
+            coords(i, j) = ej->nodes[(nVertices - 1) - j]->pos[i]; // Global coordinates of the nodes of the source panel
+
+    if(symy)
+        coords.row(1) = -coords.row(1);
+
+    for (int i = 0; i < nVertices; ++i)
+        trsfCoordSrc.col(i) = glob2loc * coords.col(i); // Local coordinates of the nodes of the source panel
+
+    return trsfCoordSrc;
 }
+
 /**
- * @brief Compute source AIC from body panel ej to body panel ei
+ * @brief Compute coordinates of the target panel from global to local axes
  */
-double Builder::tau(Element *ei, Element *ej)
+Eigen::Vector3d Builder::TrsfCoordTgt(bool symy, Element *ei, Element *ej, Eigen::Matrix3d const &glob2loc)
 {
-    if (ei == ej)
-        return 0;
+    Eigen::Vector3d trsfCoordTgt;
+    Eigen::Vector3d cg = ei->getVMem().getCg(); // Global coordinates of the CG of the target panel
+    trsfCoordTgt = glob2loc * cg; // Local coordinates of the target panel
+
+    return trsfCoordTgt;
+}
+
+/**
+ * @brief Compute distances between two panels
+ */
+Eigen::Vector3d Builder::Distance(bool symy, Element *ei, Element *ej, Eigen::Matrix3d const &glob2loc)
+{
+    Eigen::Vector3d distance; // Distance between the target and source panels
+    Eigen::Vector3d cgSrc = ej->getVMem().getCg();
+    Eigen::Vector3d cgTgt = ei->getVMem().getCg();
+
+    for (int i = 0; i < 3; ++i)
+        distance(i) = cgTgt(i) - cgSrc(i);
+
+    if(symy)
+        distance(1) = cgTgt(1) + 2 * cgSrc(1);
+
+    distance = glob2loc * distance;
+
+    return distance;
+}
+
+/**
+ * @brief Compute doublet AIC and source AIC from body panel ej to body panel ei
+ */
+Eigen::Vector2d Builder::MuTau(bool symy, Element *ei, Element *ej, Eigen::MatrixXd const &trsfCoordSrc, Eigen::Vector3d const &trsfCoordTgt, Eigen::Vector3d const &distance)
+{
+    double mu = 0;
+    double tau = 0;
+    Eigen::Vector2d mu_tau;
+    Eigen::Vector4d d, e, h, r, F, G;
+    size_t nVertices = ei->nodes.size();
+
+    // Computation of the doublet AIC - mu
+    if (ei->no == ej->no && !symy)
+        mu = 0.5; // Doublet term - Influence of a panel on itself
     else
-        return 0;
+    {
+        // Coefficients
+        for (int i = 1; i <= nVertices; ++i)
+        {
+            e(i - 1) = (trsfCoordTgt(0) - trsfCoordSrc(0,i - 1)) * (trsfCoordTgt(0) - trsfCoordSrc(0,i - 1)) + distance(2) * distance(2);
+            h(i - 1) = (trsfCoordTgt(0) - trsfCoordSrc(0,i - 1)) * (trsfCoordTgt(1) - trsfCoordSrc(1,i - 1));
+            r(i - 1) = sqrt((trsfCoordTgt(0) - trsfCoordSrc(0,i - 1)) * (trsfCoordTgt(0) - trsfCoordSrc(0,i - 1)) + (trsfCoordTgt(1) - trsfCoordSrc(1,i - 1)) * (trsfCoordTgt(1) - trsfCoordSrc(1,i - 1)) + distance(2) * distance(2));
+        }
+        for (int i = 1; i <= nVertices; ++i)
+        {
+            int j = i % nVertices + 1;
+
+            F(i - 1) = (trsfCoordSrc(1,j - 1) - trsfCoordSrc(1,i - 1)) * e(i - 1) - (trsfCoordSrc(0,j - 1) - trsfCoordSrc(0,i - 1)) * h(i - 1);
+            G(i - 1) = (trsfCoordSrc(1,j - 1) - trsfCoordSrc(1,i - 1)) * e(j - 1) - (trsfCoordSrc(0,j - 1) - trsfCoordSrc(0,i - 1)) * h(j - 1);
+        }
+        // Doublet term - Influence of a panel on another panel
+        for (int i = 1; i <= nVertices; ++i)
+        {
+            int j = i % nVertices + 1;
+
+            mu = mu + atan2(distance(2) * (trsfCoordSrc(0,j - 1) - trsfCoordSrc(0,i - 1)) * (F(i - 1) * r(j - 1) - G(i - 1) * r(i - 1)),
+                            distance(2) * distance(2) * (trsfCoordSrc(0,j - 1) - trsfCoordSrc(0,i - 1)) * (trsfCoordSrc(0,j - 1) - trsfCoordSrc(0,i - 1)) * r(i - 1) * r(j - 1) + F(i - 1) * G(i - 1));
+        }
+        mu = -1 / (4 * PI) * mu;
+    }
+
+    // Computation of the source AIC - tau
+    // Coefficients
+    for (int i = 1; i <= nVertices; ++i)
+    {
+        int j = i % nVertices + 1;
+
+        d(i - 1) = sqrt((trsfCoordSrc(0,j - 1) - trsfCoordSrc(0,i - 1)) * (trsfCoordSrc(0,j - 1) - trsfCoordSrc(0,i - 1)) + (trsfCoordSrc(1,j - 1) - trsfCoordSrc(1,i - 1)) * (trsfCoordSrc(1,j - 1) - trsfCoordSrc(1,i - 1)));
+        r(i - 1) = sqrt((trsfCoordTgt(0) - trsfCoordSrc(0,i - 1)) * (trsfCoordTgt(0) - trsfCoordSrc(0,i - 1)) + (trsfCoordTgt(1) - trsfCoordSrc(1,i - 1)) * (trsfCoordTgt(1) - trsfCoordSrc(1,i - 1)) + distance(2) * distance(2));
+    }
+
+    if (ei->no == ej->no && !symy)
+    {
+        // Source term - Influence of a panel on itself
+        for (int i = 1; i <= nVertices; ++i)
+        {
+            int j = i % nVertices + 1;
+
+            tau = tau + ((trsfCoordTgt(0) - trsfCoordSrc(0,i - 1)) * (trsfCoordSrc(1,j - 1) - trsfCoordSrc(1,i - 1)) - (trsfCoordTgt(1) - trsfCoordSrc(1,i - 1)) * (trsfCoordSrc(0,j - 1) - trsfCoordSrc(0,i - 1))) / d(i - 1) * log((r(i - 1) + r(j - 1) + d(i - 1)) / (r(i - 1) + r(j - 1) - d(i - 1)));
+        }
+        tau = -1 / (4 * PI) * tau;
+    }
+    else
+    {
+        // Source term - Influence of a panel on another panel
+        for (int i = 1; i <= nVertices; ++i)
+        {
+            int j = i % nVertices + 1;
+
+            tau = tau + ((trsfCoordTgt(0) - trsfCoordSrc(0,i - 1)) * (trsfCoordSrc(1,j - 1) - trsfCoordSrc(1,i - 1)) - (trsfCoordTgt(1) - trsfCoordSrc(1,i - 1)) * (trsfCoordSrc(0,j - 1) - trsfCoordSrc(0,i - 1))) / d(i - 1) * log((r(i - 1) + r(j - 1) + d(i - 1)) / (r(i - 1) + r(j - 1) - d(i - 1)));
+        }
+
+        tau = -1 / (4 * PI) * tau - distance(2) * mu;
+    }
+
+    mu_tau(0) = mu;
+    mu_tau(1) = tau;
+
+    return mu_tau;
 }
+
 /**
  * @brief Compute source AIC from field panel ej to body panel ei
  */
 double Builder::sigma(Element *ei, Element *ej)
 {
+    /* Initialisation of the parameters */
+    //    double sigma = 0;
+    //    double A, B, C, I, II, III, R;
+
+    /* Import of the coordinates of the influencing field cell vertices */
+    //    x = 0; y = 0; z = 0;
+    //    xC = 0; yC = 0; zC = 0;
+
+    /* Field source potential influence between two field panels */
+    //    for (int i = 1; i <= 2; ++i) {
+    //        for (int j = 1; j <= 2; ++j) {
+    //            for (int k = 1; k <= 2; ++k) {
+    //                A = x - xC[i-1];
+    //                B = y - yC[j-1];
+    //                C = z - zC[k-1];
+    //                R = sqrt(A * A + B * B + C * C);
+    //                I = B * C * log(A + R) - A * A / 2 * atan((B * C) / (A * R));
+    //                II = C * A * log(B + R) - B * B / 2 * atan((C * A) / (B * R));
+    //                III = A * B * log(C + R) - C * C / 2 * atan((A * B) / (C * R));
+
+    //                sigma -= pow((-1),(double)(i + j + k)) * (I + II + III);
+    //            }
+    //        }
+    //    }
+
+    //    sigma = - 1 / (4 * PI) * sigma;
+
+    //    return sigma;
+
     return 0;
 }
+
 /**
  * @brief Compute source x-velocity AIC from field panel ej to body panel ei
  */
 double Builder::sigmaX(Element *ei, Element *ej)
 {
+    /* Initialisation of the parameters */
+    //    double sigmaX = 0;
+    //    double A, B, C, R, t0;
+
+    /* Import of the coordinates of the influencing field cell vertices */
+    //    x = 0; y = 0; z = 0;
+    //    xC = 0; yC = 0; zC = 0;
+
+    /* Field source velocity influence between a field panel and a body panel */
+    //    for (int i = 1; i <= 2; ++i) {
+    //        for (int j = 1; j <= 2; ++j) {
+    //            for (int k = 1; k <= 2; ++k) {
+    //                A = (x - xC[i-1]);
+    //                B = (y - yC[j-1]);
+    //                C = (z - zC[k-1]);
+    //                R = sqrt(A * A + B * B + C * C);
+    //                t0 = B / 2 * log((R + C) / (R - C)) + C / 2 * log((R + B) / (R - B)) - A * atan((B * C) / (A * R));
+
+    //                sigmaX += pow((-1),(double)(i + j + k)) * t0;
+    //            }
+    //        }
+    //    }
+
+    //    sigmaX = 1 / (4 * PI) * sigmaX;
+
+    //    return sigmaX;
+
     return 0;
 }
+
 /**
  * @brief Compute source y-velocity AIC from field panel ej to body panel ei
  */
 double Builder::sigmaY(Element *ei, Element *ej)
 {
+    /* Initialisation of the parameters */
+    //    double sigmaY = 0;
+    //    double A, B, C, R, t1;
+
+    /* Import of the coordinates of the influencing field cell vertices */
+    //    x = 0; y = 0; z = 0;
+    //    xC = 0; yC = 0; zC = 0;
+
+    /* Field source velocity influence between a field panel and a body panel */
+    //    for (int i = 1; i <= 2; ++i) {
+    //        for (int j = 1; j <= 2; ++j) {
+    //            for (int k = 1; k <= 2; ++k) {
+    //                A = (x - xC[i-1]);
+    //                B = (y - yC[j-1]);
+    //                C = (z - zC[k-1]);
+    //                R = sqrt(A * A + B * B + C * C);
+    //                t1 = C / 2 * log((R + A) / (R - A)) + A / 2 * log((R + C) / (R - C)) - B * atan((C * A) / (B * R));
+
+    //                sigmaY += pow((-1),(double)(i + j + k)) * t1;
+    //            }
+    //        }
+    //    }
+
+    //    sigmaY = 1 / (4 * PI) * sigmaY;
+
+    //    return sigmaY;
+
     return 0;
 }
+
 /**
  * @brief Compute source z-velocity AIC from field panel ej to body panel ei
  */
 double Builder::sigmaZ(Element *ei, Element *ej)
 {
+    /* Initialisation of the parameters */
+    //    double sigmaZ = 0;
+    //    double A, B, C, R, t2;
+
+    /* Import of the coordinates of the influencing field cell vertices */
+    //    x = 0; y = 0; z = 0;
+    //    xC = 0; yC = 0; zC = 0;
+
+    /* Field source velocity influence between a field panel and a body panel */
+    //    for (int i = 1; i <= 2; ++i) {
+    //        for (int j = 1; j <= 2; ++j) {
+    //            for (int k = 1; k <= 2; ++k) {
+    //                A = (x - xC[i-1]);
+    //                B = (y - yC[j-1]);
+    //                C = (z - zC[k-1]);
+    //                R = sqrt(A * A + B * B + C * C);
+    //                t2 = A / 2 * log((R + B) / (R - B)) + B / 2 * log((R + A) / (R - A)) - C * atan((A * B) / (C * R));
+
+    //                sigmaZ += pow((-1),(double)(i + j + k)) * t2;
+    //            }
+    //        }
+    //    }
+
+    //    sigmaZ = 1 / (4 * PI) * sigmaZ;
+
+    //    return sigmaZ;
+
     return 0;
 }
 
diff --git a/fpm/src/fBuilder.h b/fpm/src/fBuilder.h
index bb290cc..0e721a8 100644
--- a/fpm/src/fBuilder.h
+++ b/fpm/src/fBuilder.h
@@ -24,18 +24,21 @@
 #include <vector>
 #include <memory>
 #include <Eigen/Dense>
+#include <map>
 
 namespace fpm
 {
 
 /**
  * @brief Aerodynamic Influence Coefficients builder
- * @authors Adrien Crovato
- * @todo add symmetry support
- * @todo implement AIC computation
+ * @authors Adrien Crovato and Axel Dechamps
+ * @todo implement field AIC computation
  */
 class FPM_API Builder : public fwk::wSharedObject
 {
+
+std::map<tbox::Element *, int> rows; ///< map linking an element to a matrix cell
+
 public:
     std::shared_ptr<Problem> pbl; ///< problem definition
 
@@ -59,8 +62,11 @@ public:
 #endif
 
 private:
-    double mu(tbox::Element *ei, tbox::Element *ej);
-    double tau(tbox::Element *ei, tbox::Element *ej);
+    Eigen::Matrix3d Glob2loc(bool symy, tbox::Element *ej);
+    Eigen::MatrixXd TrsfCoordSrc(bool symy, tbox::Element *ej, Eigen::Matrix3d const &glob2loc);
+    Eigen::Vector3d TrsfCoordTgt(bool symy, tbox::Element *ei, tbox::Element *ej, Eigen::Matrix3d const &glob2loc);
+    Eigen::Vector3d Distance(bool symy, tbox::Element *ei, tbox::Element *ej, Eigen::Matrix3d const &glob2loc);
+    Eigen::Vector2d MuTau(bool symy, tbox::Element *ei, tbox::Element *ej, Eigen::MatrixXd const &trsfCoordSrc, Eigen::Vector3d const &trsfCoordTgt, Eigen::Vector3d const &distance);
     double sigma(tbox::Element *ei, tbox::Element *ej);
     double sigmaX(tbox::Element *ei, tbox::Element *ej);
     double sigmaY(tbox::Element *ei, tbox::Element *ej);
diff --git a/fpm/src/fProblem.cpp b/fpm/src/fProblem.cpp
index ef7843f..5d1b840 100644
--- a/fpm/src/fProblem.cpp
+++ b/fpm/src/fProblem.cpp
@@ -15,17 +15,20 @@
  */
 
 #include "fProblem.h"
+#include "fBuilder.h"
 #include "fField.h"
 #include "fBody.h"
+#include "fWake.h"
 #include "wTag.h"
 #include "wElement.h"
+#include "wNode.h"
 #include "wMem.h"
 #include "wCache.h"
 using namespace tbox;
 using namespace fpm;
 
 Problem::Problem(std::shared_ptr<MshData> _msh, double aoa, double aos, double mch,
-                 double sref, double cref, double xref, double yref, double zref) : msh(_msh), alpha(aoa), beta(aos), mach(mch), sR(sref), cR(cref)
+                 double sref, double cref, double xref, double yref, double zref, bool symy) : msh(_msh), alpha(aoa), beta(aos), mach(mch), sR(sref), cR(cref), symY(symy)
 {
     xR(0) = xref;
     xR(1) = yref;
@@ -39,6 +42,7 @@ void Problem::set(std::shared_ptr<Field> f)
 {
     field = f;
 }
+
 /**
  * @brief Add a non-lifting body
  */
@@ -50,21 +54,129 @@ void Problem::add(std::shared_ptr<Body> b)
 /**
  * @brief Compute velocity at element
  */
-Eigen::Vector3d Problem::U(tbox::Element &e, std::vector<double> const &phi)
+Eigen::Vector3d Problem::U(tbox::Element &e, std::vector<double> const &mu, double const &tau)
 {
-    Eigen::Vector3d v(0, 0, 0);
-    // @todo to be implemented if usefull
-    return v;
-    std::cout << std::endl;
+    Eigen::Vector3d U(0, 0, 0);
+    size_t nVertices = e.nodes.size();                          // Number of nodes per panel
+    size_t nGP = e.getVCache().getVGauss().getN();              // Number of Gauss points on the element
+    Eigen::MatrixXd coords(3,nVertices);
+    for (int i = 0; i < 3; ++i)
+        for (int j = 0; j < nVertices; ++j)
+            coords(i,j) = e.nodes[(nVertices - 1) - j]->pos[i]; // Coordinates of the panel
+        
+    // Import of the shape functions at the Gauss points
+    Eigen::MatrixXd ff(nVertices,nGP);
+    for(int i = 0; i < nVertices; ++i)
+        for(int j = 0; j < nGP; ++j)
+            ff(i,j) = e.getVCache().getSf(j)[i];
+
+    // Import of the derivatives of the shape functions at the Gauss points
+    Eigen::MatrixXd dff(2 * nGP,nVertices);
+    for(int i = 0; i < nGP; ++i)
+        for(int j = 0; j < 2; ++j)
+            dff.row(j + (2 * i)) = e.getVCache().getDsf(i).row(j);
+    
+    // Computation of the Jacobian matrix - xi-derivative (first row of the Jacobian)
+    Eigen::MatrixXd dXi(3,nGP);
+    for(int i = 0; i < nGP; ++i)
+    {
+        dXi.col(i) << 0, 0, 0;
+
+        for(int j = 0; j < nVertices; ++j)
+        {
+            dXi(0,i) += dff((2 * i),j) * coords(0,j);
+            dXi(1,i) += dff((2 * i),j) * coords(1,j);
+            dXi(2,i) += dff((2 * i),j) * coords(2,j);
+        }
+    }
+
+    // Computation of the Jacobian matrix - eta-derivative (second row of the Jacobian)
+    Eigen::MatrixXd dEta(3,nGP);
+    for(int i = 0; i < nGP; ++i)
+    {
+        dEta.col(i) << 0, 0, 0;
+
+        for(int j = 0; j < nVertices; ++j)
+        {
+            dEta(0,i) += dff(1 + (2 * i),j) * coords(0,j);
+            dEta(1,i) += dff(1 + (2 * i),j) * coords(1,j);
+            dEta(2,i) += dff(1 + (2 * i),j) * coords(2,j);
+        }
+    }
+
+    // Computation of the Jacobian matrix - cross-product (third row of the Jacobian)
+    Eigen::MatrixXd dZeta(3,nGP);
+
+    for(int i = 0; i < nGP; ++i)
+    {
+        Eigen::Vector3d dxi = dXi.col(i);
+        Eigen::Vector3d deta = dEta.col(i);
+        Eigen::Vector3d dzeta;
+        dzeta = (dxi.cross(deta)).normalized();
+        dZeta.col(i) = dzeta; 
+    }
+
+    // Assembly of the Jacobian and partial computation of the velocity
+    // Done for each Gauss point -> For n Gauss points, there are n values of the velocity -> Average
+    Eigen::MatrixXd v(3,nGP);
+    for(int i = 0; i < nGP; ++i)
+    {
+        // Creation of the Jacobian
+        Eigen::Matrix3d J0;
+        J0.row(0) = dXi.col(i).transpose();
+        J0.row(1) = dEta.col(i).transpose();
+        J0.row(2) = dZeta.col(i).transpose();
+
+        // Inversion of the Jacobian
+        Eigen::Matrix3d Jinv = J0.inverse();
+
+        // Removal of the third column 
+        Eigen::MatrixXd J(3,2);
+        for(int j = 0; j < 2; ++j)
+        {
+            J.col(j) = Jinv.col(j);
+        }
+
+        // Import of the doublets at the nodes of the panel
+        Eigen::VectorXd mu_element(nVertices);
+        for (int j = 0; j < nVertices; ++j)
+        {
+            mu_element(j) = mu[e.nodes[j]->no - 1];
+        }
+
+        // Computation of the velocity
+        Eigen::MatrixXd dff0(2,4);
+        dff0.row(0) = dff.row(2 * i);
+        dff0.row(1) = dff.row(1 + (2 * i));
+        v.col(i) = J * dff0 * mu_element;
+    }
+
+    // Average of the velocity
+    for(int i = 0; i < nGP; ++i)
+    {
+        U += v.col(i);
+    }
+    U = U/nGP;
+
+    // Import of the unit normal vector - Target panel
+    Eigen::Vector3d n = e.normal(); 
+
+    // Contribution of the normal velocity
+    U += (-tau)*n;
+
+    // Contribution of the freestream velocity
+    U += Ui();
+    
+    return U;
 }
 
 /**
  * @brief Compute density at element
  */
-double Problem::Rho(tbox::Element &e, std::vector<double> const &phi)
+double Problem::Rho(Eigen::Vector3d U)
 {
     // particularized: pow(1 + 0.5 * (gamma - 1) * mInf * mInf * (1 - gradU2), 1 / (gamma - 1));
-    Eigen::Vector3d u = U(e, phi);
+    Eigen::Vector3d u = U;
     double a = 1 + 0.2 * mach * mach * (1 - u.dot(u));
     return sqrt(a * a * a * a * a);
 }
@@ -72,14 +184,14 @@ double Problem::Rho(tbox::Element &e, std::vector<double> const &phi)
 /**
  * @brief Compute Mach number at element
  */
-double Problem::Mach(tbox::Element &e, std::vector<double> const &phi)
+double Problem::Mach(Eigen::Vector3d U)
 {
     if (mach == 0)
         return 0;
     else
     {
         // particularized: pow(1 + 0.5 * (gamma - 1) * mInf * mInf * (1 - gradU2), 1 / (gamma - 1));
-        Eigen::Vector3d u = U(e, phi);
+        Eigen::Vector3d u = U;
         double a2 = 1 / (mach * mach) + 0.2 * (1 - u.dot(u)); // speed of sound squared
         return u.norm() / sqrt(a2);
     }
@@ -88,9 +200,9 @@ double Problem::Mach(tbox::Element &e, std::vector<double> const &phi)
 /**
  * @brief Compute pressure coefficient at element
  */
-double Problem::Cp(tbox::Element &e, std::vector<double> const &phi)
+double Problem::Cp(Eigen::Vector3d U)
 {
-    Eigen::Vector3d u = U(e, phi);
+    Eigen::Vector3d u = U;
     if (mach == 0)
         return 1 - u.dot(u);
     else
@@ -112,8 +224,15 @@ void Problem::updateMem()
             e->getVMem().update(true);
     // Update surface Jacobian
     for (auto s : bodies)
+    {
         for (auto e : s->tag->elems)
+        {
             e->getVMem().update(false);
+        }
+        for(auto w : s-> wakes)
+            for (auto e : w->tag->elems)
+                e->getVMem().update(false);
+    }
 }
 
 /**
diff --git a/fpm/src/fProblem.h b/fpm/src/fProblem.h
index 4bf8c33..3cdd15c 100644
--- a/fpm/src/fProblem.h
+++ b/fpm/src/fProblem.h
@@ -27,8 +27,7 @@ namespace fpm
 
 /**
  * @brief Manage the problem
- * @authors Adrien Crovato
- * @todo check and implement velocity computation
+ * @authors Adrien Crovato & Axel Dechamps
  */
 class FPM_API Problem : public fwk::wSharedObject
 {
@@ -45,8 +44,9 @@ public:
     double sR;          ///< Reference surface
     double cR;          ///< Reference chord
     Eigen::Vector3d xR; ///< Reference center point (for moment computation)
+    bool symY;          ///< True if mesh is symmetric with respect to y-plane
 
-    Problem(std::shared_ptr<tbox::MshData> _msh, double aoa, double aos, double mch, double sref, double cref, double xref, double yref, double zref);
+    Problem(std::shared_ptr<tbox::MshData> _msh, double aoa, double aos, double mch, double sref, double cref, double xref, double yref, double zref, bool symy);
     ~Problem() { std::cout << "~Problem()\n"; }
 
     void set(std::shared_ptr<Field> f);
@@ -55,10 +55,10 @@ public:
     // Functions
     inline double PhiI(Eigen::Vector3d const &pos);
     inline Eigen::Vector3d Ui();
-    Eigen::Vector3d U(tbox::Element &e, std::vector<double> const &phi);
-    double Rho(tbox::Element &e, std::vector<double> const &phi);
-    double Mach(tbox::Element &e, std::vector<double> const &phi);
-    double Cp(tbox::Element &e, std::vector<double> const &phi);
+    Eigen::Vector3d U(tbox::Element &e, std::vector<double> const &mu, double const &tau);
+    double Rho(Eigen::Vector3d U);
+    double Mach(Eigen::Vector3d U);
+    double Cp(Eigen::Vector3d U);
 
 #ifndef SWIG
     void check() const;
diff --git a/fpm/src/fSolver.cpp b/fpm/src/fSolver.cpp
index 51066d4..6e49acc 100644
--- a/fpm/src/fSolver.cpp
+++ b/fpm/src/fSolver.cpp
@@ -36,7 +36,7 @@ using namespace fpm;
 
 /**
  * @brief Initialize the solver and perform sanity checks
- * @authors Adrien Crovato
+ * @authors Adrien Crovato & Axel Dechamps
  */
 Solver::Solver(std::shared_ptr<Builder> _aic) : aic(_aic), Cl(0), Cd(0), Cs(0), Cm(0)
 {
@@ -56,8 +56,10 @@ Solver::Solver(std::shared_ptr<Builder> _aic) : aic(_aic), Cl(0), Cd(0), Cs(0),
     // Setup variables
     phi.resize(aic->pbl->msh->nodes.size(), 0.);
     vPhi.resize(aic->pbl->msh->nodes.size(), 0.);
+    muNodes.resize(aic->pbl->msh->nodes.size(), 0.);
     U.resize(aic->pbl->msh->elems.size(), Eigen::Vector3d::Zero());
     rho.resize(aic->pbl->msh->elems.size(), 0.);
+    taue.resize(aic->pbl->msh->elems.size(), 0.);
     M.resize(aic->pbl->msh->elems.size(), 0.);
     Cp.resize(aic->pbl->msh->elems.size(), 0.);
 }
@@ -84,12 +86,18 @@ void Solver::run()
     Eigen::VectorXd mu(aic->nP), tau(aic->nP);
     int ig = 0;
     for (auto body : aic->pbl->bodies)
+    {
         for (int i = 0; i < body->tag->elems.size(); ++i, ++ig)
+        {
             tau(ig) = (aic->pbl->Ui() + Eigen::Vector3d(uX(ig), uY(ig), uZ(ig))).dot(body->tag->elems[i]->normal());
+            taue[body->tag->elems[i]->no-1] = aic->pbl->Ui().dot(body->tag->elems[i]->normal());
+        }
+    }
+
     std::cout << "done!" << std::endl;
 
     std::cout << "Solving for body doublets... " << std::flush;
-    Eigen::PartialPivLU<Eigen::MatrixXd>(aic->A).solve(aic->B * tau);
+    mu = Eigen::PartialPivLU<Eigen::MatrixXd>(aic->A).solve(aic->B * tau);
     std::cout << "done!" << std::endl;
 
     std::cout << "Computing flow and loads on bodies... " << std::flush;
@@ -110,6 +118,8 @@ void Solver::save(std::shared_ptr<MshExport> mshWriter, int n)
     Results results;
     results.scalars_at_nodes["phi"] = &phi;
     results.scalars_at_nodes["varPhi"] = &vPhi;
+    results.scalars_at_elems["mu"] = &mue;
+    results.scalars_at_elems["tau"] = &taue;
     results.vectors_at_elems["U"] = &U;
     results.scalars_at_elems["rho"] = &rho;
     results.scalars_at_elems["Mach"] = &M;
@@ -134,43 +144,57 @@ void Solver::save(std::shared_ptr<MshExport> mshWriter, int n)
  */
 void Solver::computeFlow(const Eigen::VectorXd &mu, const Eigen::VectorXd &tau, const Eigen::VectorXd &sigma)
 {
-    // @todo should we
-    // - compute velocity from phi at nodes or mu at nodes? if mu, then store phi at elements only
+    // Compute phi and mu at elements and transfer at nodes
+    Eigen::VectorXd phie = -aic->A * mu - aic->B * tau - aic->C * sigma;
+
+    mue.resize(mu.size());
+    for(size_t i = 0 ; i < mue.size() ; ++i)
+        mue[i] = mu(i);
 
-    // Compute phi at elements and transfer at nodes
-    Eigen::VectorXd phie = aic->A * mu + aic->B * tau + aic->C * sigma;
     auto pbl = aic->pbl;
     // reset all values
     for (auto n : pbl->msh->nodes)
+    {
         vPhi[n->no - 1] = 0;
+        muNodes[n->no - 1] = 0;
+    }
     int ig = 0;
     for (auto body : pbl->bodies)
     {
-        // associate the potential value to its element
+        // associate the potential and doublet value to its element
         std::map<Element *, double> mapPhi;
+        std::map<Element *, double> mapMu;
         for (auto e : body->tag->elems)
         {
             mapPhi[e] = phie[ig];
+            mapMu[e] = mu[ig];
             ig++;
         }
         // average at nodes
         for (auto n : body->nodes)
-            for (auto e : body->neMap.at(n))
-                vPhi[n->no - 1] += mapPhi.at(e) / body->neMap.at(n).size(); // maybe use area average?
+        {
+            for (auto e : body->neMap.at(n)) 
+            {
+                vPhi[n->no - 1] += mapPhi.at(e) / body->neMap.at(n).size(); 
+                muNodes[n->no - 1] += mapMu.at(e) / body->neMap.at(n).size(); 
+            }
+        }
     }
 
     // Compute surface flow
     for (auto body : pbl->bodies)
     {
         for (auto n : body->nodes)
-            phi[n->no - 1] = pbl->PhiI(n->pos) + vPhi[n->row]; // total potential
+            phi[n->no - 1] = pbl->PhiI(n->pos) + vPhi[n->no - 1]; // total potential
 
+        int ig = 0;
         for (auto e : body->tag->elems)
         {
-            U[e->no - 1] = pbl->U(*e, phi);     // velocity
-            rho[e->no - 1] = pbl->Rho(*e, phi); // density
-            M[e->no - 1] = pbl->Mach(*e, phi);  // Mach number
-            Cp[e->no - 1] = pbl->Cp(*e, phi);   // pressure coefficient
+            U[e->no - 1] = pbl->U(*e, muNodes, tau(ig));    // velocity
+            rho[e->no - 1] = pbl->Rho(U[e->no - 1]);        // density
+            M[e->no - 1] = pbl->Mach(U[e->no - 1]);         // Mach number
+            Cp[e->no - 1] = pbl->Cp(U[e->no - 1]);          // pressure coefficient
+            ig += 1;
         }
     }
 }
@@ -205,7 +229,7 @@ void Solver::computeLoad()
         // Compute aerodynamic load coefficients (i.e. Load / pressure / surface)
         // compute coefficients along x and vertical (y in 2D, z in 3D) directions and pitching moment (along -z in 2D, y in 3D)
         body->Cm = 0;
-        Eigen::Vector3d Cf(0, 0, 0);
+        Eigen::Vector3d Cf(0, 0, 0);        
         for (auto e : body->tag->elems)
         {
             Eigen::Vector3d l = e->getVMem().getCg() - pbl->xR;                       // lever arm
diff --git a/fpm/src/fSolver.h b/fpm/src/fSolver.h
index a7c9084..c2cdb1a 100644
--- a/fpm/src/fSolver.h
+++ b/fpm/src/fSolver.h
@@ -30,8 +30,7 @@ namespace fpm
 
 /**
  * @brief Field panel solver
- * @authors Adrien Crovato
- * @todo check and implement velocity computation
+ * @authors Adrien Crovato & Axel Dechamps
  */
 class FPM_API Solver : public fwk::wSharedObject
 {
@@ -40,6 +39,9 @@ public:
 
     std::vector<double> phi;        ///< full potential
     std::vector<double> vPhi;       ///< perturbation potential
+    std::vector<double> taue;       ///< body source at the element
+    std::vector<double> muNodes;    ///< body doublet averaged at the nodes
+    std::vector<double> mue;        ///< body doublet averaged at the elements
     std::vector<Eigen::Vector3d> U; ///< velocity
     std::vector<double> rho;        ///< density
     std::vector<double> M;          ///< mach number
diff --git a/fpm/src/fWake.cpp b/fpm/src/fWake.cpp
index 2b95ce7..0b40761 100644
--- a/fpm/src/fWake.cpp
+++ b/fpm/src/fWake.cpp
@@ -65,7 +65,7 @@ Wake::Wake(std::shared_ptr<MshData> _msh, std::string const &name, Tag *const &w
         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";
+            err << "fpm::Wake: 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);
diff --git a/fpm/tests/M6.py b/fpm/tests/M6.py
new file mode 100644
index 0000000..702110d
--- /dev/null
+++ b/fpm/tests/M6.py
@@ -0,0 +1,83 @@
+#!/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.
+
+# Onera M6 wing
+# Axel Dechamps
+
+from fwk.testing import *
+from fwk.coloring import ccolors
+import math
+import fpm
+import tbox
+import tbox.gmsh as gmsh
+
+def main():
+    # geometry parameters
+    S_ref = 1.5057      # Reference surface (full if symY = 0, half if symY = 1)
+    c_ref = 0.805       # Reference chord
+    wNc = 100           # Number of panels along the chord
+    wNs = 10            # Number of panels along the span
+    bC = 1.0            # Progression along chord
+    pS = 1.0            # Progression along span
+
+    # flow parameters
+    AoA = 3.06 * math.pi / 180  # Angle of attack (numeric value given in degrees)
+    Mach = 0                    # Mach number
+
+    # timer
+    tms = fpm.Timers()
+    tms['total'].start()
+
+    # mesh
+    print(ccolors.ANSI_BLUE + 'PyMeshing...' + ccolors.ANSI_RESET)
+    pars = {'wNc' : wNc, 'wNs' : wNs, 'bC' : bC, 'pS' : pS}
+    msh = gmsh.MeshLoader('../models/oneraM6.geo', __file__).execute(**pars)
+
+    # problem
+    pbl = fpm.Problem(msh, AoA, 0., Mach, S_ref, c_ref, 0, 0, 0, True)
+    wing = fpm.Body(msh, 'wing', ['wTe'], 10)
+    pbl.add(wing)
+
+    # AIC builder
+    aic = fpm.Builder(pbl)
+    aic.run()
+
+    # solver
+    slv = fpm.Solver(aic)
+    slv.run()
+    slv.save(tbox.GmshExport(msh))
+
+    # 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()
+    if AoA == 3.06 * math.pi / 180 and Mach == 0:
+        tests.add(CTest('CL', slv.Cl, 0.0998, 5e-2))
+        tests.add(CTest('CD', slv.Cd, 0.0020, 5e-2))
+        tests.add(CTest('CS', slv.Cs, 0.0037, 5e-2))
+        tests.add(CTest('CM', slv.Cm, -0.0580, 5e-2))
+    tests.run()
+
+    # eof
+    print('')
+
+if __name__ == '__main__':
+    main()
diff --git a/fpm/tests/a445.py b/fpm/tests/a445.py
new file mode 100644
index 0000000..8cd41f6
--- /dev/null
+++ b/fpm/tests/a445.py
@@ -0,0 +1,84 @@
+#!/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.
+
+# Agard 445 wing
+# Axel Dechamps
+
+from fwk.testing import *
+from fwk.coloring import ccolors
+import math
+import fpm
+import tbox
+import tbox.gmsh as gmsh
+
+def main():    
+    # geometry parameters
+    S_ref = 0.35        # Reference surface (full if symY = 0, half if symY = 1)
+    c_ref = 0.559       # Reference chord
+    wNc = 100           # Number of panels along the chord
+    wNs = 10            # Number of panels along the span
+    bC = 1.0            # Progression along chord
+    pS = 1.0            # Progression along span
+    
+    # flow parameters
+    AoA = 1 * math.pi / 180  # Angle of attack (numeric value given in degrees)
+    Mach = 0                 # Mach number
+
+    # timer
+    tms = fpm.Timers()
+    tms['total'].start()
+
+    # mesh
+    print(ccolors.ANSI_BLUE + 'PyMeshing...' + ccolors.ANSI_RESET)
+    pars = {'wNc' : wNc, 'wNs' : wNs, 'bC' : bC, 'pS' : pS}
+    msh = gmsh.MeshLoader('../models/agard445.geo', __file__).execute(**pars)
+
+    # problem
+    pbl = fpm.Problem(msh, AoA, 0., Mach, S_ref, c_ref, 0, 0, 0, True)
+    wing = fpm.Body(msh, 'wing', ['wTe'], 10)
+    pbl.add(wing)
+
+    # AIC builder
+    aic = fpm.Builder(pbl)
+    aic.run()
+
+    # solver
+    slv = fpm.Solver(aic)
+    slv.run()
+    slv.save(tbox.GmshExport(msh))
+
+    # 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()
+    if AoA == 1 * math.pi / 180 and Mach == 0:
+        tests.add(CTest('CL', slv.Cl, 0.054790, 5e-2)) 
+        tests.add(CTest('CD', slv.Cd, 0.000221, 5e-2)) 
+        tests.add(CTest('CS', slv.Cs, 0.001184, 5e-2))
+        tests.add(CTest('CM', slv.Cm, -0.048214, 5e-2))
+    tests.run()
+    
+    # eof
+    print('')
+
+if __name__ == '__main__':
+    main()
+
diff --git a/fpm/tests/n12.py b/fpm/tests/n12.py
index 663229f..59010c7 100644
--- a/fpm/tests/n12.py
+++ b/fpm/tests/n12.py
@@ -19,22 +19,34 @@
 # Adrien Crovato
 
 from fwk.testing import *
+import math
 
 def main():
     p = {}
     # geometry parameters
-    p['pars'] = {'wSpn' : 5, 'wNc' : 80, 'wNs' : 10}
-    p['spn'] = 5
+    p['sym'] = 0
+    p['spn'] = 5 # true span if sym=0, half span if sym=1
+    p['pars'] = {'symY' : p['sym'],'wSpn' : p['spn'], 'wNc' : 100, 'wNs' : 10} 
     # flow parameters
-    p['aoa'] = 0
+    p['aoa'] = 3 * math.pi / 180 
     p['aos'] = 0
     p['mach'] = 0
     # run
     import fpm.models.n0012 as n12
-    n12.main(p)
+    solver = n12.main(p)
     # test
     print(ccolors.ANSI_BLUE + 'PyTesting...' + ccolors.ANSI_RESET)
     tests = CTests()
+    if(p['aoa'] == 0 * math.pi / 180 and p['mach'] == 0):
+        tests.add(CTest('CL', solver.Cl, 0.00000, 5e-2)) # aero (old code) = 0.00000
+        tests.add(CTest('CD', solver.Cd, 0.00021, 5e-2)) # aero (old code) = 0.00013
+        tests.add(CTest('CS', solver.Cs, -0.00000, 5e-2))
+        tests.add(CTest('CM', solver.Cm, -0.00000, 5e-2))
+    if(p['aoa'] == 3 * math.pi / 180 and p['mach'] == 0):
+        tests.add(CTest('CL', solver.Cl, 0.229, 5e-2)) # aero (old code) = 0.226
+        tests.add(CTest('CD', solver.Cd, 0.003, 5e-2)) # aero (old code) = 0.003
+        tests.add(CTest('CS', solver.Cs, -0.000, 5e-2))
+        tests.add(CTest('CM', solver.Cm, -0.056, 5e-2))
     tests.run()
     
     # eof
diff --git a/fpm/tests/n12f.py b/fpm/tests/n12f.py
index edd633a..086b301 100644
--- a/fpm/tests/n12f.py
+++ b/fpm/tests/n12f.py
@@ -19,14 +19,16 @@
 # Adrien Crovato
 
 from fwk.testing import *
+import math
 
 def main():
     p = {}
     # geometry parameters
-    p['pars'] = {'wSpn' : 5, 'wNc' : 80, 'wNs' : 10, 'fWdt' : 10, 'nX' : 10, 'nY' : 10, 'nZ' : 4}
-    p['spn'] = 5
+    p['sym'] = 0
+    p['spn'] = 5 # true span if sym=0, half span if sym=1
+    p['pars'] = {'symY' : p['sym'], 'wSpn' : p['spn'], 'wNc' : 100, 'wNs' : 10, 'fWdt' : 10, 'nX' : 10, 'nY' : 10, 'nZ' : 4}
     # flow parameters
-    p['aoa'] = 0
+    p['aoa'] = 3 * math.pi / 180 
     p['aos'] = 0
     p['mach'] = 0.8
     # run
diff --git a/fpm/tests/n12t.py b/fpm/tests/n12t.py
index 7834735..fa1d843 100644
--- a/fpm/tests/n12t.py
+++ b/fpm/tests/n12t.py
@@ -19,22 +19,30 @@
 # Adrien Crovato
 
 from fwk.testing import *
+import math
 
 def main():
     p = {}
     # geometry parameters
-    p['pars'] = {'wSpn' : 5, 'wNc' : 80, 'wNs' : 10, 'tSpn' : 3, 'tNc' : 40, 'tNs' : 5}
-    p['spn'] = 5
+    p['sym'] = 0
+    p['spn'] = 5 # true span if sym=0, half span if sym=1
+    p['tspn'] = 3 # true span if sym=0, half span if sym=1
+    p['pars'] = {'symY' : p['sym'], 'wSpn' : p['spn'], 'wNc' : 100, 'wNs' : 10, 'tSpn' : p['tspn'], 'tNc' : 100, 'tNs' : 10}
     # flow parameters
-    p['aoa'] = 0
+    p['aoa'] = 3 * math.pi / 180 
     p['aos'] = 0
     p['mach'] = 0
     # run
     import fpm.models.n0012 as n12
-    n12.main(p, tail=True)
+    solver = n12.main(p, tail=True)
     # test
     print(ccolors.ANSI_BLUE + 'PyTesting...' + ccolors.ANSI_RESET)
     tests = CTests()
+    if(p['aoa'] == 3 * math.pi / 180 and p['mach'] == 0):
+        tests.add(CTest('CL', solver.Cl, 0.273, 5e-2))
+        tests.add(CTest('CD', solver.Cd, 0.004, 5e-2))
+        tests.add(CTest('CS', solver.Cs, -0.000, 5e-2))
+        tests.add(CTest('CM', solver.Cm, -0.277, 5e-2))
     tests.run()
     
     # eof
-- 
GitLab