diff --git a/README.md b/README.md
index 60a40a70bf43f5014480409a7c43c4890b334653..bfd21d98097c1112f6a5362d9bf0ce65e813747f 100644
--- a/README.md
+++ b/README.md
@@ -35,6 +35,7 @@ A more exhaustive list of features can be found in the [complete
 documentation][rotare-doc] that will be made available with version 1.0.0.
 
 ### Types of rotors
+
 - [x] Helicopters main or tail rotors
 - [x] Aircraft propellers
 - [ ] Wind/tidal turbines
@@ -128,7 +129,8 @@ contact me directly at tlambert@uliege.be.
 
 We would like to thank Theo Delvaux for his initial implementation of the
 oblique flight with the `indfact` solver. We would also like to thank Johan Le
-for his work on the implementation of coaxial rotors during his master thesis.
+and Lyraie Rakotondratsimba for their work on the implementation of coaxial
+rotors during his master thesis.
 
 Their excellent contributions facilitated considerably the final implementation
 of these features in Rotare.
diff --git a/src/classes/@Blade/Blade.m b/src/classes/@Blade/Blade.m
index 19c54624b332570b371bbeaebe6240bcd1a89e14..423c88e1b46e8ff3a25886564bf98df03467f143 100644
--- a/src/classes/@Blade/Blade.m
+++ b/src/classes/@Blade/Blade.m
@@ -19,15 +19,16 @@ classdef Blade < handle
     % -----
     %
     % Blade properties:
-    %   nElem - Number of elements, [-]
-    %   dy    - Element span, [m]
-    %   y     - Element absolute radial position, [m]
-    %   r     - Element relative radial position (relative to the total radius), [-]
-    %   area  - Element area, [m^2]
-    %   chord - Element chord, [m]
-    %   twist - Element twist (also called stagger angle), [rad]
-    %   iAf   - Index of airfoil to use for each element, [-]
-    %   sol   - Element solidity, [-]
+    %   nElem   - Number of elements, [-]
+    %   dy      - Element span, [m]
+    %   y       - Element absolute radial position, [m]
+    %   r       - Element relative radial position (relative to the total radius), [-]
+    %   area    - Element area, [m^2]
+    %   chord   - Element chord, [m]
+    %   twist   - Element twist (also called stagger angle), [rad]
+    %   iAf     - Index of airfoil to use for each element, [-]
+    %   sol     - Element solidity, [-]
+    %   spinDir - Rotor spin direction (used for coaxial rotors; 1 = cw, -1 = ccw), [-]
     %
     % Blade methods:
     %
@@ -44,6 +45,7 @@ classdef Blade < handle
     %   twist   : Twist of the guide stations, [deg] (same size as rad)
     %   iAf     : Index of the airfoil for the guide stations, [-] (same size as rad)
     %   nElem   : Number of elements to mesh the whole blade, [-]
+    %   spinDir : Rotor spin direction (used for coaxial rotors; 1 = cw, -1 = ccw), [-]
     %
     % See also: Rotor, rotare, template, af_tools.Airfoil.
     %
@@ -69,6 +71,7 @@ classdef Blade < handle
         chord (1, :) double {mustBePositive} % Element chord, [m]
         twist (1, :) double {mustBeFinite}   % Element twist (also called stagger angle), [rad]
         iAf (1, :) double {mustBePositive}   % Index of airfoil to use for each element, [-]
+        spinDir (1, 1) = 1 % Rotor spin direction (used for coaxial rotors; 1 = cw, -1 = ccw), [-]
     end
 
     properties (SetAccess = private, Dependent)
diff --git a/src/classes/@ElemPerf/ElemPerf.m b/src/classes/@ElemPerf/ElemPerf.m
index e30aafb034b3a8d549a52b1ea4c5b0c9fa8b7178..4c4d7dfef27d3c5c92162167ac82b45421989124 100644
--- a/src/classes/@ElemPerf/ElemPerf.m
+++ b/src/classes/@ElemPerf/ElemPerf.m
@@ -36,6 +36,8 @@ classdef ElemPerf < handle
     %
     % <a href="https:/gitlab.uliege.be/rotare/documentation">Complete documentation (online)</a>
 
+    % ----------------------------------------------------------------------------------------------
+    % FIXME: Documentation
     % ----------------------------------------------------------------------------------------------
     % Implementation:
     %   - alpha is implemented as a dependent property to prevent Matlab complaining when we set cl
@@ -85,6 +87,7 @@ classdef ElemPerf < handle
     properties (Dependent)
         alpha    (1, :) double % Angle of attack, [rad]
         reynolds (1, :) double % Reynolds number, [-]
+        massflow (1, :) double % Mass flow rate through the element, [kg/s]
         indVelAx (1, :) double % Induced axial velocity at rotor disk, [m/s]
         indVelTg (1, :) double % Induced tangential velocity at rotor disk, [m/s]
         inflowRat (1, :) double % Inflow ratio, [-]
@@ -96,7 +99,7 @@ classdef ElemPerf < handle
     % Cache for dependent properties to avoid excessive recalculation
     properties (Access = private, Hidden)
         alpha_    (1, :) double % Angle of attack, [rad]
-        reynolds_ (1, :) double % Reynolds number, [-]
+        massflow_ (1, :) double % Mass flow rate through the element, [kg/s]
         indVelAx_ (1, :) double % Induced axial velocity at rotor disk, [m/s]
         indVelTg_ (1, :) double % Induced tangential velocity at rotor disk, [m/s]
         inflowRat_ (1, :) double % Inflow ratio, [-]
@@ -176,6 +179,10 @@ classdef ElemPerf < handle
 
         end
 
+        function massflow = get.massflow(self)
+            massflow = self.massflow_;
+        end
+
         function indVelAx = get.indVelAx(self)
             indVelAx = self.indVelAx_;
         end
@@ -211,13 +218,21 @@ classdef ElemPerf < handle
             [self.cl, self.cd] = getclcd(self, val);
         end
 
+        function set.massflow(self, val)
+            self.massflow_ = val;
+        end
+
         function set.indVelAx(self, val)
             % Cache value
             self.indVelAx_ = val;
 
             % Calculate inflow ratios
-            self.inflowRat_ = (self.Op.speed + val) ./ (self.Op.omega * self.Rot.radius);
+            self.inflowRat_ = (self.upstreamVelAx + val) ./ (self.Op.omega * self.Rot.radius);
             self.indInflowRat_ = val ./ (self.Op.omega * self.Rot.radius);
+
+            % Calculate massflow rate
+            axialVel = self.upstreamVelAx + val;
+            self.massflow_ = 2 * pi * self.Op.Flow.rho * self.Rot.Bl.y * self.Rot.Bl.dy .* axialVel;
         end
 
         function set.indVelTg(self, val)
@@ -234,8 +249,12 @@ classdef ElemPerf < handle
             self.inflowRat_ = val;
 
             % Calculate induced velocity and ratio
-            self.indVelAx_ = val .* self.Op.omega * self.Rot.radius - self.Op.speed;
+            self.indVelAx_ = val .* self.Op.omega * self.Rot.radius - self.upstreamVelAx;
             self.indInflowRat_ = self.indVelAx ./ (self.Op.omega * self.Rot.radius);
+
+            % Calculate massflow rate
+            axialVel = self.upstreamVelAx + self.indVelAx_;
+            self.massflow_ = 2 * pi * self.Op.Flow.rho * self.Rot.Bl.y * self.Rot.Bl.dy .* axialVel;
         end
 
         function set.swirlRat(self, val)
@@ -252,7 +271,7 @@ classdef ElemPerf < handle
             self.indInflowRat_ = val;
 
             % Calculate inflow ratios
-            self.inflowRat_ = (self.Op.speed) ./ (self.Op.omega * self.Rot.radius) + val;
+            self.inflowRat_ = (self.upstreamVelAx) ./ (self.Op.omega * self.Rot.radius) + val;
             self.indVelAx_ = val .* (self.Op.omega * self.Rot.radius);
         end
 
@@ -271,6 +290,7 @@ classdef ElemPerf < handle
         calcforces(self) % Calculate forces, torque and power
         [cl, cd] = getclcd(self, aoaVect, reyVect, i) % Get values of cl and cd
         plotveltriangles(self, nTriangles, varargin)
+        updateupstreamvel(self, PrevOpRot, Mod)
     end
 
 end
diff --git a/src/classes/@ElemPerf/calcforces.m b/src/classes/@ElemPerf/calcforces.m
index f6700dc2c52f3b09dcccde00f86f1546c1291731..6d417116a17c9c45638371f31df84591e01844bb 100644
--- a/src/classes/@ElemPerf/calcforces.m
+++ b/src/classes/@ElemPerf/calcforces.m
@@ -39,7 +39,7 @@ function calcforces(self)
     if ~isempty(self.cl)
 
         axVel = self.upstreamVelAx + self.indVelAx;
-        tgVel = self.tgSpeed - self.indVelTg;
+        tgVel = self.tgSpeed - self.Rot.Bl.spinDir * (self.upstreamVelTg + self.indVelTg);
         relVel = sqrt(axVel.^2 + tgVel.^2);
 
         % Non-dimensionalization factor
diff --git a/src/classes/@ElemPerf/updateupstreamvel.m b/src/classes/@ElemPerf/updateupstreamvel.m
new file mode 100644
index 0000000000000000000000000000000000000000..00506a05bebcc424ef9a0535e781061c2e60f806
--- /dev/null
+++ b/src/classes/@ElemPerf/updateupstreamvel.m
@@ -0,0 +1,206 @@
+function updateupstreamvel(self, PrevOpRot, Mod)
+    % UPDATEUPSTREAMVEL Update upstream velocities to account for previous rotor downwash.
+    %   This function updates the axial and tangential upstream velocity of the current elements by
+    %   taking into account the downwash of the previous rotor.
+    %
+    % Notes:
+    %   At the moment, only the Simplified Multiple Stream Tube (SMST) model is implemented. It
+    %   assumes:
+    %     - no pre-contraction caused by the second rotor
+    % -----
+    %
+    % Syntax:
+    %   ElPerf.updateupstreamvel(PrevOpRot) updates upstream velocities in an ElemPerf object
+    %   according to the performance of the previous Operating Rotor `PrevOpRot`.
+    %
+    % Inputs:
+    %   prevOpRot : previous Operating Rotor object
+    %
+    % Outputs:
+    %   Updated values for upstreamVelAx and upstreamVelTg for the current ElemPerf object
+    %
+    % See also: ElemPerf.
+    %
+    % <a href="https:/gitlab.uliege.be/rotare/documentation">Complete documentation (online)</a>
+
+    % ----------------------------------------------------------------------------------------------
+    % (c) Copyright 2022-2023 University of Liege
+    % Author: Lyraie Rakotondratsimba
+    % Co-Author/Maintainer: Thomas Lambert <t.lambert@uliege.be>
+    % ULiege - Aeroelasticity and Experimental Aerodynamics
+    % MIT License
+    % Repo: https://gitlab.uliege.be/rotare/rotare
+    % Docs: https://gitlab.uliege.be/rotare/documentation
+    % Issues: https://gitlab.uliege.be/rotare/rotare/-/issues
+    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+    % Map the velocities according to the appropriate mode
+    switch Mod.coax.model
+        case 'none'
+            Mod.coax.contraType = 'none';
+            prevWake = calccontraction(PrevOpRot, Mod.coax);
+            mapvelocities(self, prevWake, length(self.Rot.Bl.y));
+        case 'sst'
+            sst(self, PrevOpRot, Mod.coax);
+        case 'mst'
+            mst(self, PrevOpRot, Mod.coax, false);
+        case 'smst'
+            mst(self, PrevOpRot, Mod.coax, true);
+    end
+
+end
+
+% ==================================== Helper functions ============================================
+
+function prevWake = calccontraction(PrevOpRot, ModCoax)
+    % CALCCONTRACTION Calculates the wake characteristics of the first rotor
+
+    % Velocity at the disk of previous rotor
+    prevRot.velAx = PrevOpRot.ElPerf.upstreamVelAx + PrevOpRot.ElPerf.indVelAx;
+    prevRot.velTg = PrevOpRot.ElPerf.indVelTg;
+    prevRot.upstreamVel = PrevOpRot.ElPerf.upstreamVelAx;
+    if strcmpi(ModCoax.model, 'sst')
+        prevRot.velAx = mean(prevRot.velAx);
+        prevRot.velTg = 0;
+        prevRot.upstreamVel = mean(PrevOpRot.ElPerf.upstreamVelAx);
+    end
+
+    % Velocities in the wake of previous rotor after contraction
+    switch ModCoax.contraType
+        case 'none'
+            prevWake.velAx = prevRot.velAx;
+            prevWake.velTg = prevRot.velTg;
+            prevWake.areaR = 1;
+        case 'farfield'
+            prevWake.velAx = 2 * prevRot.velAx - prevRot.upstreamVel;
+            prevWake.velTg = 2 * prevRot.velTg; % FIXME?
+            prevWake.areaR = prevRot.velAx ./ prevWake.velAx;
+            % prevWake.VelTg = PrevOpRot.ElPerf.dQ ./ (PrevOpRot.Rot.Bl.y .* ...
+            %                  PrevOpRot.ElPerf.massflow) .* (1 ./ areaRatio);
+        case 'value'
+            prevWake.areaR = Mod.coax.contraVal;
+            prevWake.velAx = prevRot.velAx ./ prevWake.areaR;
+            prevWake.velTg = prevRot.velTg ./ prevRot.velTg;
+    end
+
+    % This assumes annulus contraction, but not thickness contraction as well
+    % It must be recalculated in general MST model
+    prevWake.y = PrevOpRot.Rot.Bl.y .* sqrt(prevWake.areaR);
+    prevWake.dy = PrevOpRot.Rot.Bl.dy .* sqrt(prevWake.areaR);
+end
+
+function sst(self, PrevOpRot, ModCoax)
+    % SSTMODEL Implement the Simple Stream Tube model.
+    %   Only consider the mean axial velocity and the entire streamtube contraction
+
+    prevWake = calccontraction(PrevOpRot, ModCoax);
+    prevWake.spinDir = self.Rot.Bl.spinDir * PrevOpRot.Rot.Bl.spinDir;
+
+    % Resize properly and discard the swirl velocity
+    prevWake.velAx = ones(size(prevWake.y)) * prevWake.velAx;
+    prevWake.velTg = zeros(size(prevWake.y));
+
+    % Map velocities
+    vcRadIdx = calccradius(prevWake.y, self.Rot.Bl.y);
+    mapvelocities(self, prevWake, vcRadIdx);
+
+    % Check if massflows are conserved
+    massflowcheck(self, PrevOpRot, prevWake, vcRadIdx, ModCoax);
+
+end
+
+function mst(self, PrevOpRot, ModCoax, simple)
+    % MSTMODEL Implement the Multiple Stream Tube model.
+    %   Consider individual annulus contraction, as well as the effect on the swirl velocity
+
+    % Base calculation with only annulus contraction, no tube narrowing
+    prevWake = calccontraction(PrevOpRot, ModCoax);
+    prevWake.spinDir = self.Rot.Bl.spinDir * PrevOpRot.Rot.Bl.spinDir;
+
+    if ~simple
+        % Contract both annulus and tube thickness
+        yPrevExt = zeros(size(PrevOpRot.Rot.Bl.y)); % External radius of previous tube
+        yPrevExt(1) = PrevOpRot.Rot.Bl.y(1) - PrevOpRot.Rot.Bl.dy(1) / 2; % Hub
+
+        yw = zeros(size(PrevOpRot.Rot.Bl.y)); % Annulus center in wake
+        dyw = zeros(size(PrevOpRot.Rot.Bl.y)); % Annulus thickness in wake
+        for i = 1:numel(PrevOpRot.Rot.Bl.y)
+            if i > 1
+                yPrevExt(i) = yw(i - 1) + dyw(i - 1) / 2;
+            end
+
+            dyw(i) = -yPrevExt(i) + sqrt(yPrevExt(i)^2 + ...
+                                         2 * PrevOpRot.Rot.Bl.y(i) .* PrevOpRot.Rot.Bl.dy .* ...
+                                         prevWake.areaR(i));
+
+            yw(i) = yPrevExt(i) + dyw(i) / 2;
+        end
+        prevWake.y = yw;
+        prevWake.dy = dyw;
+
+    end
+
+    % Map velocities
+    vcRadIdx = calccradius(prevWake.y, self.Rot.Bl.y);
+    mapvelocities(self, prevWake, vcRadIdx);
+
+    % Check if massflows are conserved
+    massflowcheck(self, PrevOpRot, prevWake, vcRadIdx, ModCoax);
+end
+
+function vcIdxOnLower = calccradius(vcY, y)
+    % CALCVCRADIUS Calculate the radius of the vena contracta and its index on lower rotor
+
+    vcIdxOnLower = find(y <= vcY(end), 1, 'last');
+end
+
+function mapvelocities(self, prevWake, vcRadIdx)
+    % MAPVELOCITIES Map the velocities of the previous rotor wake onto the current rotor
+
+    self.upstreamVelAx(1:vcRadIdx) = interp1(prevWake.y, prevWake.velAx, ...
+                                             self.Rot.Bl.y(1:vcRadIdx), 'spline', 0);
+    self.upstreamVelTg(1:vcRadIdx) = interp1(prevWake.y, prevWake.spinDir * prevWake.velTg, ...
+                                             self.Rot.Bl.y(1:vcRadIdx), 'spline', 0);
+end
+
+function massflowcheck(self, PrevOpRot, prevWake, vcRadIdx, ModCoax)
+    % MASSFLOWCHECK Verify if the massflow is conserved after inteprolation in the vena contracta
+    %   The following three massflows should be roughly equal:
+    %     1. Mass flow accros entire previous rotor disk
+    %     2. Mass flow in wake after contraction of slipstream
+    %     3. Mass flow on lower rotor elements inside vena contracta after interpolation of wake
+
+    % 1. Entire previous rotor disk
+    if strcmpi(ModCoax.model, 'sst')
+        mf_prevRot = trapz(2 * pi * PrevOpRot.Rot.Bl.y .* PrevOpRot.Rot.Bl.dy .* ...
+                           PrevOpRot.Op.Flow.rho .* ...
+                           mean(PrevOpRot.ElPerf.upstreamVelAx + PrevOpRot.ElPerf.indVelAx));
+    else
+        mf_prevRot = PrevOpRot.totalmassflow;
+    end
+
+    % 2. Contracted wake
+    mf_prevWake = trapz(2 * pi * prevWake.y .* prevWake.dy .* PrevOpRot.Op.Flow.rho .* ...
+                        prevWake.velAx);
+    % 3. Lower rotor elements inside vena contracta
+    mf_curRot = trapz(2 * pi * self.Rot.Bl.y(1, 1:vcRadIdx) .* self.Rot.Bl.dy .* ...
+                      PrevOpRot.Op.Flow.rho .* self.upstreamVelAx(1:vcRadIdx));
+
+    % Error in mass flow conservation
+    err_prevRot = (mf_prevRot - mf_prevWake) / mf_prevRot;  % prevRot disk/wake
+    err_intrpCurr = (mf_prevWake - mf_curRot) / mf_prevWake; % prevRot wake/current vena contracta
+
+    %     close all;
+    %     figure
+    %     hold on
+    %     plot(PrevOpRot.Rot.Bl.y, PrevOpRot.ElPerf.upstreamVelAx + PrevOpRot.ElPerf.indVelAx)
+    %     plot(prevWake.y, prevWake.velAx)
+    %     plot(self.Rot.Bl.y, self.upstreamVelAx,'-o')
+    %     legend('upper', 'vc','lower')
+    %     pause
+
+    % Throw error if mass flow not conserved
+    assert(abs(err_prevRot) < 1e-12); % Should be 0 in theory
+    assert(abs(err_intrpCurr) < 2e-2); % Should be small
+
+end
diff --git a/src/classes/@OperRotor/OperRotor.m b/src/classes/@OperRotor/OperRotor.m
index 4c5842c1d8185585f47250747927356ebd3add71..948fccc8951cd3b9e6cd7d9d1074c3e9828ec97a 100644
--- a/src/classes/@OperRotor/OperRotor.m
+++ b/src/classes/@OperRotor/OperRotor.m
@@ -14,6 +14,8 @@ classdef OperRotor < handle
     %   ElPerf      - Performance of the elements (angles, velocities, forces, etc)
     %   tgTipSpeed  - Tangential tip speed, [m/s]
     %   relTipSpeed - Relative tip speed, [m/s]
+    %   totalmassflow - Mass flow through the rotor, [kg/s]
+
     %
     % OperRotor methods:
     %   calcperf  - Calculate operating rotor performance
@@ -64,6 +66,8 @@ classdef OperRotor < handle
         cQ (1, 1) double  % Rotor torque coefficient, [-]
         cP (1, 1) double  % Rotor power coefficient, [-]
 
+        totalmassflow (1, 1) double  % Mass flow through the rotor, [kg/s]
+
         nonDim (1, 2) char % Non-dimensionalization factor ('US', 'EU')
     end
 
@@ -119,6 +123,10 @@ classdef OperRotor < handle
             cP = calccoeff(self, 'power');
         end
 
+        function totalmassflow = get.totalmassflow(self)
+            totalmassflow = trapz(self.ElPerf.massflow);
+        end
+
         function nonDim = get.nonDim(self)
             nonDim = self.nonDim_cached;
         end
diff --git a/src/classes/@Result/Result.m b/src/classes/@Result/Result.m
index 5230a0ed9f4f4e13d9206e7c9aab9051bcacca10..0e71c09ab0971a88a42f19ce24807092f6c7bb54 100644
--- a/src/classes/@Result/Result.m
+++ b/src/classes/@Result/Result.m
@@ -5,7 +5,7 @@ classdef Result < handle
     %
     % Notes:
     %   - Almost all properties are private to prevent accidental overwriting.
-    %
+    %   - Most properties are structured as [nRotor, nOperPoint]
     % -----
     %
     % Result properties:
@@ -14,11 +14,16 @@ classdef Result < handle
     %   indvel   - Results obtained with the indVel solver
     %   stahlhut - Results obtained with the stalhlut solver
     %   operPts  - Table with the operating points for each individual result
-    %   ModExt   - Modelling enstension options (losses, etc)
-    %
+    %   ModExt   - Modelling extension options (losses, etc)
+    %   cT       - Table with all Thrust coefficients
+    %   cP       - Table with all Power coefficients
+    %   cQ       - Table with all Torque coefficients
     %
     % Result methods:
-    %   save - Save the Result object to file
+    %   save      - Save the Result object to file
+    %   summarize - Output a summary table with the operating points and the performance
+    %   filter    - Filter for a specific operating point
+    %   plotperf  - Plot and compare rotor performances
     %
     % Result constructor:
     %   Result = Result() creates an empty object.
@@ -43,10 +48,10 @@ classdef Result < handle
 
     % Base properties of the elements
     properties
-        leishman (1, :) OperRotor  % Results obtained with the leishman solver
-        indfact (1, :) OperRotor   % Results obtained with the indFact solver
-        indvel (1, :) OperRotor    % Results obtained with the indVel solver
-        stahlhut (1, :) OperRotor  % Results obtained with the stalhlut solver
+        leishman (:, :) OperRotor  % Results obtained with the leishman solver
+        indfact (:, :) OperRotor   % Results obtained with the indFact solver
+        indvel (:, :) OperRotor    % Results obtained with the indVel solver
+        stahlhut (:, :) OperRotor  % Results obtained with the stalhlut solver
         operPts table              % Table of operating points for each individual result
         ModExt (1, 1) struct       % Modelling enstension options (losses, etc)
     end
@@ -73,9 +78,16 @@ classdef Result < handle
         % ---------------------------------------------
         % Set methods
         function set.operPts(self, val)
-            % Note: A bug in Matlab prevents the user of variable names when  size checks are added
-            % in the property definition. https://stackoverflow.com/q/48423003
+            % Note: A bug in Matlab prevents the use of variable names when size checks are added
+            % in the property definition. See: https://stackoverflow.com/q/48423003
             self.operPts = val;
+
+            % Properly name and group operating points for multi rotors
+            % Must merge in that order to have correct indexes
+            nRots = (size(val, 2) - 2) / 2;
+            self.operPts = mergevars(self.operPts, 3 + nRots:size(val, 2));
+            self.operPts = mergevars(self.operPts, 3:3 + nRots - 1);
+
             self.operPts.Properties.VariableNames = {'altitude', 'speed', 'rpm', 'collective'};
 
         end
@@ -105,26 +117,21 @@ end
 function resTable = maketable(self, prop)
     % MAKETABLE Gather properties from all solvers and all operating points in a table
 
-    resTable = table('Size', [size(self.operPts, 1), 4], ...
-                     'VariableTypes', {'double', 'double', 'double', 'double'});
+    resTable = table('Size', [size(self.operPts, 1), 0]);
 
-    propName = cell(1, 4);
-    for i = 1:length(self.SOLVERS)
-        propName{i} = [prop, '_', self.SOLVERS{i}];
-    end
+    for iSolv = 1:length(self.SOLVERS)
+        thissolver = self.SOLVERS{iSolv};
+        tmpTable = [];
 
-    resTable.Properties.VariableNames = propName;
-
-    for iOp = 1:size(self.operPts, 1)
-        for iSolv = 1:length(self.SOLVERS)
-            solver = self.SOLVERS{iSolv};
-
-            if ~isempty(self.(solver))
-                resTable.(iSolv)(iOp) = self.(solver)(iOp).(prop);
-            else
-                resTable.(iSolv)(iOp) = NaN;
+        for iRotor = size(self.(thissolver), 2):-1:1
+            for iOp = size(self.operPts, 1):-1:1
+                tmpTable(iOp, iRotor) = self.(thissolver)(iOp, iRotor).(prop);
             end
         end
+
+        if ~isempty(tmpTable)
+            resTable = addvars(resTable, tmpTable, 'NewVariableNames', [prop, '_', thissolver]);
+        end
     end
 
 end
diff --git a/src/classes/@Result/filter.m b/src/classes/@Result/filter.m
index 436858f386967841f1254a20931c041a697af4a4..c563ed008028425536d681f92757a053fd866b15 100644
--- a/src/classes/@Result/filter.m
+++ b/src/classes/@Result/filter.m
@@ -6,7 +6,7 @@ function [filtered, idx] = filter(self, alt, speed, rpm, coll)
     % -----
     %
     % Syntax:
-    %   [filtered, idx] = Result.filter(alt, rpm, coll, speed) Return the Results of the four
+    %   [filtered, idx] = Result.filter(alt, speed, rpm, coll) Return the Results of the four
     %   solvers for the operating point defined by `alt`, `rpm`, `coll`, `speed`.
     %
     % Inputs:
@@ -27,6 +27,8 @@ function [filtered, idx] = filter(self, alt, speed, rpm, coll)
     %
     % <a href="https://gitlab.uliege.be/rotare/documentation">Complete documentation (online)</a>
 
+    % ----------------------------------------------------------------------------------------------
+    % FIXME: Needs adjustments for multiple rotors
     % ----------------------------------------------------------------------------------------------
     % (c) Copyright 2022-2023 University of Liege
     % Author: Thomas Lambert <t.lambert@uliege.be>
@@ -61,7 +63,7 @@ function [filtered, idx] = filter(self, alt, speed, rpm, coll)
     for iSolv = 1:length(DEF.ALLOWED_SOLVERS)
         thissolv = DEF.ALLOWED_SOLVERS{iSolv};
         if ~isempty(self.(thissolv))
-            filtered.(thissolv) = self.(thissolv)(1, idx);
+            filtered.(thissolv) = self.(thissolv)(idx, :);
         else
             filtered.(thissolv) = self.(thissolv);
         end
diff --git a/src/classes/@Result/plotperf.m b/src/classes/@Result/plotperf.m
index 0749dbee49d65704cdbe7b39c15d5183b84753b4..fc789ec1a009acb6fc52c97394a4da5a7e60060d 100644
--- a/src/classes/@Result/plotperf.m
+++ b/src/classes/@Result/plotperf.m
@@ -33,6 +33,8 @@ function plotperf(self, varargin)
     %
     % <a href="https://gitlab.uliege.be/rotare/documentation">Complete documentation (online)</a>
 
+    % ----------------------------------------------------------------------------------------------
+    % FIXME: Needs adjustments for multiple rotors
     % ----------------------------------------------------------------------------------------------
     % (c) Copyright 2022-2023 University of Liege
     % Author: Thomas Lambert <t.lambert@uliege.be>
@@ -72,7 +74,6 @@ function plotperf(self, varargin)
                 operVect = self.operPts(idx, :).(oper);
             end
             dataVect = vectorize(filtered.(thissolv), perf);
-
             plot(operVect, dataVect, lineSpec.(thissolv){:}, 'DisplayName', thissolv);
             hold on;
         end
diff --git a/src/configs/caradonna1981.m b/src/configs/caradonna1981.m
index e96cacec84a4321630bff298023a2b8a82540f25..bc082d9173ee18fb59120ec54773c90b22655502 100644
--- a/src/configs/caradonna1981.m
+++ b/src/configs/caradonna1981.m
@@ -53,6 +53,11 @@ Mod.solvers = {'all'};   % BEMT Solver ('leishman', 'indfact', 'indvel', 'stahlh
 % Extensions/corrections
 Mod.Ext.losses = 'all';  % Include losses using Prandtl formula ('none', 'hub', 'tip', 'both')
 
+% [COAXIAL ONLY] Contraction model
+Mod.coax.model = 'mst'; % Type of contraction ('none', 'sst', 'smst', 'mst')
+Mod.coax.contraType = 'farfield'; % (opt) Type of contraction ('farfield', 'value')
+Mod.coax.contraVal = 1 / 2; % (opt) Contraction ratio between upper disk and lower rotor, [-]
+
 % Numerical parameters
 Mod.Num.convCrit = 1e-4;  % Convergence criterion value,
 Mod.Num.maxIter  = 500;   % Maximum number of iterations for convergence calculations
@@ -115,6 +120,7 @@ Blade.nElem = 100;  % Number of blade elements, [-]
 
 % Rotor base position
 Blade.hubPos = [0, 0, 0]; % Rotor center position (used for coaxial rotors), [m]
+Blade.spinDir = 1;        % Rotor spin direction (used for coaxial rotors; 1 = cw, -1 = ccw)
 
 % ==================================================================================================
 % ======================================= Overwrites ===============================================
diff --git a/src/configs/knight1937.m b/src/configs/knight1937.m
index d28fce505d3331f63a8c25243a16c34d3fbc09f4..a328fa7c36bb43d6ec8bb5fab2295d5f1d05c59d 100644
--- a/src/configs/knight1937.m
+++ b/src/configs/knight1937.m
@@ -48,6 +48,11 @@ Mod.solvers = {'indvel'};   % BEMT Solver ('leishman', 'indfact', 'indvel', 'sta
 % Extensions/corrections
 Mod.Ext.losses = 'all';  % Include losses using Prandtl formula ('none', 'hub', 'tip', 'both')
 
+% [COAXIAL ONLY] Contraction model
+Mod.coax.model = 'mst'; % Type of contraction ('none', 'sst', 'smst', 'mst')
+Mod.coax.contraType = 'farfield'; % (opt) Type of contraction ('farfield', 'value')
+Mod.coax.contraVal = 1 / 2; % (opt) Contraction ratio between upper disk and lower rotor, [-]
+
 % Numerical parameters
 Mod.Num.convCrit = 1e-4;  % Convergence criterion value,
 Mod.Num.maxIter  = 500;   % Maximum number of iterations for convergence calculations
@@ -110,3 +115,4 @@ Blade.nElem = 100;  % Number of blade elements, [-]
 
 % Rotor base position
 Blade.hubPos = [0, 0, 0]; % Rotor center position (used for coaxial rotors), [m]
+Blade.spinDir = 1;        % Rotor spin direction (used for coaxial rotors; 1 = cw, -1 = ccw)
diff --git a/src/configs/template.m b/src/configs/template.m
index dbd803ec28c88c378ef3a1fc983671fbea5fcd71..6df35d0a42c169f7b51cafe69b8c65d1a95a7b91 100644
--- a/src/configs/template.m
+++ b/src/configs/template.m
@@ -63,7 +63,7 @@ Sim.Save.filename    = 'template';  % File name of the saved result
 
 % Outputs
 Sim.Out.showPlots = true;  % Show all plots (forces, angles, speed, ...)
-Sim.Out.show3D    = false;  % Show the 3D view of the whole rotor
+Sim.Out.show3D    = true;  % Show the 3D view of the whole rotor
 Sim.Out.hubType   = 'tangent_ogive';  % Hub (nose cone) type on the 3D view (see docs for list)
 Sim.Out.console   = true;  % Print the final results in console
 Sim.Out.verbosity = 'min'; % Verbosity level of the console output ('min', 'all')
@@ -85,6 +85,11 @@ Mod.solvers = 'all';  % BEMT Solver ('leishman', 'indfact', 'indvel', 'stahlhut'
 % Extensions/corrections
 Mod.Ext.losses = 'none';    % Include losses using Prandtl formula ('none', 'hub', 'tip', 'both')
 
+% [COAXIAL ONLY] Contraction model
+Mod.coax.model = 'mst'; % Type of contraction ('none', 'sst', 'smst', 'mst')
+Mod.coax.contraType = 'farfield'; % Type of contraction ('farfield', 'value')
+Mod.coax.contraVal = 1 / 2; % (opt) Contraction ratio between upper disk and lower rotor, [-]
+
 % Numerical parameters
 Mod.Num.convCrit = 1e-4;  % Convergence criterion value,
 Mod.Num.maxIter  = 500;   % Maximum number of iterations for convergence calculations
@@ -162,3 +167,4 @@ Blade.nElem = 100;  % Number of blade elements, [-]
 
 % Rotor base position
 Blade.hubPos = [0, 0, 0]; % Rotor center position (used for coaxial rotors), [m]
+Blade.spinDir = 1;        % Rotor spin direction (used for coaxial rotors; 1 = cw, -1 = ccw)
diff --git a/src/configs/templatecoax.m b/src/configs/templatecoax.m
index ccfa168eb3ad1cd95e8a94d51016e2b139e4b5de..aae4f71b77149ea2c5c54ae7de10eacb2d9ba678 100644
--- a/src/configs/templatecoax.m
+++ b/src/configs/templatecoax.m
@@ -1,5 +1,5 @@
 % TEMPLATECOAX Template configuration file for Coaxial rotors simulations with Rotare.
-%  This file extends the regular template configuration by adding a second rotor geometry.
+%  This file extends the regular template configuration by adding N-1 coaxial rotors.
 % -----
 %
 % See also: template, rotare, validconfig.
@@ -16,30 +16,42 @@
 % Issues: https://gitlab.uliege.be/rotare/rotare/-/issues
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
+N_ROTORS = 2; % Total number of rotors in coaxial configuration, [-]
+
 % ==================================================================================================
 % ================================== Baseline configuration ========================================
 % ==================================================================================================
-template; % Just load exisiting template
+% Just load exisiting template
+template;
+
+Sim.Out.show3D = false; % Disable 3D as it is not supported for coaxial (yet)
 
 % ==================================================================================================
-% ================================= Blade and rotor geometry =======================================
+% ====================================== COAXIAL SYSTEM  ===========================================
 % ==================================================================================================
-% Adds a second element to the blade structure to represent the second rotor
+% Adds a as many rotors as required, based on the initial blade geometry
 
-Blade(2) = Blade(1); % Make second rotor identical to first one
+% First rotor settings for collective and RPM
+init_coll = Op.collective;
+init_rpm = Op.rpm;
+Blade(N_ROTORS - 1) = Blade(1); % Pre-alloc
 
-if strcmp(Sim.Misc.appli, 'heli')
-    rotorShift = [0, 0, -1];
-else
-    rotorShift = [-1, 0, 0];
-end
-Blade(2).hubPos = Blade(1).hubPos + rotorShift; % Shift second rotor w.r.t the application type
+for i = 2:N_ROTORS
 
-% ==================================================================================================
-% ===================================== Operating points ===========================================
-% ==================================================================================================
+    % ================================ Blade and rotor geometry ====================================
+    Blade(i) = Blade(1); % Make second rotor identical to first one
+    Blade(i).spinDir = -1 * Blade(i - 1).spinDir; % Invert spin direction for each rotor
+    Blade(i).nElem = round(Blade(i - 1).nElem * 1);
+
+    % Shift second rotor w.r.t the application type
+    if strcmp(Sim.Misc.appli, 'heli')
+        rotorShift = [0, 0, -1] * Blade(i - 1).radius(end);
+    else
+        rotorShift = [-1, 0, 0] * Blade(i - 1).radius(end);
+    end
+    Blade(i).hubPos = Blade(i - 1).hubPos + rotorShift;
 
-% Operating points must be set for the two rotors
-% We simply extend the collective and rpm fields by adding values for the new rotor
-Op.collective = [Op.collective; 1, 4, 7];
-Op.rpm = [Op.rpm; 900, 1200];
+    % =================================== Operating points =========================================
+    Op.collective = [Op.collective; init_coll + 5];
+    Op.rpm = [Op.rpm; init_rpm * 1.5];
+end
diff --git a/src/rotare.m b/src/rotare.m
index 0a806711d1e0127109290091b4c88f6982a26c6a..7ef0451e2afc361db4579acf9f108a506fbf04f5 100644
--- a/src/rotare.m
+++ b/src/rotare.m
@@ -95,7 +95,7 @@ function [Results] = rotare(configFile)
 
     if Sim.Out.show3D
         % TODO: Allow plot for multiple rotors
-        Rot.plot('all', Sim.Out.hubType);
+        Rot(1).plot('all', Sim.Out.hubType);
     end
 
     % (Following comment is so miss-hit metric checker stays happy)
@@ -141,7 +141,8 @@ function [Results] = rotare(configFile)
 
                         % Table that identifies the operating point for future reuse
                         operPoints(iOperPoint, :) = [Uop.altitude(iAlt), Uop.speed(iSpeed), ...
-                                                     Uop.rpm(i, iRpm), Uop.collective(i, iColl)];
+                                                     [Uop.rpm(:, iRpm)'], ...
+                                                     [Uop.collective(:, iColl)']];
 
                         iOperPoint = iOperPoint + 1;
 
diff --git a/src/solvers/bemt.m b/src/solvers/bemt.m
index 1f08f66ef2da7b200eb007bb2c9d32c42ed20aeb..8924ea437b70ab862021e0b3b5f92050ea72e4c6 100644
--- a/src/solvers/bemt.m
+++ b/src/solvers/bemt.m
@@ -9,22 +9,21 @@ function bemt(OpRot, Mod)
     % -----
     %
     % Syntax:
-    %   Rot = BEMT(Rot, Mod, Flow)
+    %   BEMT(OpRot, Mod) Calculates the performances of each OpRot object with the Blade Element
+    %                    Momentum Theory, using the solvers and models defined in Mod
     %
     % Inputs:
-    %   Rot  : Rotor object fully defined
-    %   Flow : Flow variables
-    %   Mod  : Model and numerical parameters
+    %   OpRot : Operating rotor object(s) fully defined
+    %   Mod   : Model and numerical parameters
     %
     % Output:
-    %   Rot : Rotor object with calculated performances
+    %   OperRotor object(s) with calculated performances
     %
-    % See also: rotare, leishman, stahlhut, propsolv, turbsolv.
+    % See also: rotare, leishman, indfact, indvel, stahlhut.
     %
     % <a href="https://gitlab.uliege.be/rotare/documentation">Complete documentation (online)</a>
 
     % ----------------------------------------------------------------------------------------------
-    % TODO: Implement coaxial rotors
     % TODO: Implement oblique flows
     % ----------------------------------------------------------------------------------------------
     % (c) Copyright 2022-2023 University of Liege
@@ -36,15 +35,12 @@ function bemt(OpRot, Mod)
     % Issues: https://gitlab.uliege.be/rotare/rotare/-/issues
     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-    % For each rotor, calculate the external velocity and solve the BEMT equations
-
-    for i = 1:length(OpRot)
-
-        % TODO: Coaxial: Determine proper external velocity to pass to the rotor here
-
-        % Solves BEMT equations for the rotor using the true distribution of external velocity
-        bemtsinglerot(OpRot(i), Mod);
+    % First rotor always calculated as if isolated
+    bemtsinglerot(OpRot(1), Mod);
 
+    for i = 2:length(OpRot)
+        OpRot(i).ElPerf.updateupstreamvel(OpRot(i - 1), Mod); % Update upstream velocity
+        bemtsinglerot(OpRot(i), Mod); % Calculate BEMT as usual, with new upstream vel distribution
     end
 
 end
diff --git a/src/solvers/indfact.m b/src/solvers/indfact.m
index 11ac1eaebd9adcab4a0affc684f98c5daab04b9d..f635429b4788b97e0a7badf2f667ea57d0569c0b 100644
--- a/src/solvers/indfact.m
+++ b/src/solvers/indfact.m
@@ -54,12 +54,12 @@ function indfact(OpRot, Mod)
     INIT_VI = 1; % Initial guess for the axial induced velocity (for when Airspeed=0), [m/s]
 
     % Abbreviations
-    airspeed = OpRot.Op.speed;
+    airspeed = OpRot.ElPerf.upstreamVelAx;
 
     % Initial guesses
     if OpRot.Op.speed ~= 0
         a = INIT_AX_FACT * ones(1, OpRot.Rot.Bl.nElem);
-        v_ax = (1 + a) * airspeed;
+        v_ax = (1 + a) .* airspeed;
     else
         v_ax = INIT_VI * ones(1, OpRot.Rot.Bl.nElem);
     end
@@ -93,7 +93,7 @@ function indfact(OpRot, Mod)
         K_P = 1 - (1 - lossFact) .* sin(phi);
 
         % Analytical solution to the momentum and blade element equations.
-        if airspeed == 0
+        if OpRot.Op.speed == 0
             v_ax = sqrt(((relVel.^2 * OpRot.Rot.nBlades .* OpRot.Rot.Bl.chord) .* ...
                          (cl .* cos(phi) - cd .* sin(phi))) ./ ...
                         (8 * pi * OpRot.Rot.Bl.y .* K_T));
@@ -109,8 +109,11 @@ function indfact(OpRot, Mod)
             b = ((relVel.^2 * OpRot.Rot.nBlades .* OpRot.Rot.Bl.chord) .* ...
                  (cl .* sin(phi) + cd .* cos(phi))) ./ ...
                 (8 * pi * OpRot.Rot.Bl.y.^2 .* airspeed .* (1 + a) .* OpRot.Op.omega .* K_P);
-            v_ax = (1 + a) * airspeed;
+            v_ax = (1 + a) .* airspeed;
         end
+        % FIXME: We should pay attention for the case where individual element has a 0 airspeed due
+        % to upstreamvelocity (highly improbable). In such situation, this should be recomputed
+        % separately to prevent issues. -> Refactor the coefficient calculation into a function
 
         % Use relaxation to faciliate convergence of nonlinear system
         v_ax = v_ax_old * (1 - Mod.Num.relax) + v_ax * Mod.Num.relax;
diff --git a/src/solvers/indvel.m b/src/solvers/indvel.m
index 580da9a8d6ca8a02a4a90f8ebbbf4a296f1765bb..a0def66b266631dbafc87a74cb5a572b649e55ad 100644
--- a/src/solvers/indvel.m
+++ b/src/solvers/indvel.m
@@ -119,6 +119,7 @@ function indvel(OpRot, Mod)
     OpRot.ElPerf.alpha = alpha;
     OpRot.ElPerf.indVelAx = v - OpRot.ElPerf.upstreamVelAx;
     OpRot.ElPerf.indVelTg = uw / 2;
+
 end
 
 function force = coeff2force(coeff, OpRot, speed)
diff --git a/src/solvers/leishman.m b/src/solvers/leishman.m
index 54b69783b31fb333092d8d9bda57a48ce366613b..aa51f1eada3985c5213a10b75cb58f3c0ac9ac8a 100644
--- a/src/solvers/leishman.m
+++ b/src/solvers/leishman.m
@@ -69,7 +69,7 @@ function leishman(OpRot, Mod)
             lambda_old = lambda;
 
             % Update Reynolds and then clSlope w.r.t. the new inflow velocity
-            axialVel = OpRot.Op.speed + lambda * OpRot.tgTipSpeed;
+            axialVel = OpRot.ElPerf.upstreamVelAx + lambda * OpRot.tgTipSpeed;
             relVel = sqrt(axialVel.^2 + OpRot.ElPerf.tgSpeed.^2);
             reynolds = Flow.reynolds(relVel, ...
                                      OpRot.Rot.Bl.chord, OpRot.Op.Flow.mu, OpRot.Op.Flow.rho);
@@ -98,7 +98,7 @@ function leishman(OpRot, Mod)
     % Update values in ElemPerf
     OpRot.ElPerf.alpha = alpha;
     OpRot.ElPerf.inflAngle = phi;
-    OpRot.ElPerf.indVelAx = lambda * OpRot.tgTipSpeed - OpRot.Op.speed;
+    OpRot.ElPerf.indVelAx = lambda * OpRot.tgTipSpeed - OpRot.ElPerf.upstreamVelAx;
     OpRot.ElPerf.indVelTg = OpRot.ElPerf.tgSpeed - xi * OpRot.tgTipSpeed;
 end
 
@@ -113,7 +113,7 @@ function [lambda, xi, phi, alpha] = leishmaneq(OpRot, clSlope, lossFact)
     %   alpha  : Angle of attack, [rad]
 
     % Abbreviations
-    lambda_c = OpRot.Op.speed / OpRot.tgTipSpeed;
+    lambda_c = OpRot.ElPerf.upstreamVelAx / OpRot.tgTipSpeed;
     sol = OpRot.Rot.solidity; % [FIXME], normally it should be local solidity (see Carroll)
     r = OpRot.Rot.Bl.r;
     pitch = OpRot.ElPerf.truePitch;
diff --git a/src/solvers/stahlhut.m b/src/solvers/stahlhut.m
index 15b8dce0850024808cf6a2eca1201c854011d064..a6997be81d2780ea3bc606f5fda700249a401258 100644
--- a/src/solvers/stahlhut.m
+++ b/src/solvers/stahlhut.m
@@ -142,7 +142,7 @@ function [gphi, b1phi, b2phi] = stahlhuteq(i, phi, OpRot, lossType)
     reynolds = OpRot.ElPerf.reynolds(i); % Use approximate Reynolds by default
     tgSpeed = OpRot.ElPerf.tgSpeed(i);
     pitch = OpRot.ElPerf.truePitch(i);
-    airspeed = OpRot.Op.speed;
+    airspeed = OpRot.ElPerf.upstreamVelAx(i);
 
     % Recalculate Reynolds based on actual inflow velocity
     %   The first time we calculate it for a given element, we use the approximate value that
diff --git a/src/utils/preproc/validateconfig.m b/src/utils/preproc/validateconfig.m
index 48696340293827117629d22e62efbdcf681c5470..44712405c2996fe1a90884dd3f8e185640e49929 100644
--- a/src/utils/preproc/validateconfig.m
+++ b/src/utils/preproc/validateconfig.m
@@ -74,6 +74,8 @@ function [Sim, Mod, Flow, Op, Airfoil, Blade] = validateconfig(configFile)
     DEF.NONDIM = {'US', 'EU'};
     DEF.VERBOSITY = {'min', 'all'};
     DEF.SOLVERS = {'leishman', 'indfact', 'indvel', 'stahlhut', 'all'};
+    DEF.CONTRACTION_TYPE = {'farfield', 'value'};
+    DEF.CONTRACTION_MODEL = {'none', 'sst', 'smst', 'mst'};
     DEF.FLUID = {'air', 'seawater', 'freshwater'};
     DEF.LOSSES = {'none', 'hub', 'tip', 'both', 'all'};
     DEF.POLARS = {'polynomial', 'file'};
@@ -88,12 +90,12 @@ function [Sim, Mod, Flow, Op, Airfoil, Blade] = validateconfig(configFile)
     % Input checks
 
     Sim = checksim(Sim, configFile, DEF); % Simulation parameters
-    Mod = checkmod(Mod, configFile, DEF); % Models, solvers, etc.
     Flow.fluid = validatestring(Flow.fluid, DEF.FLUID, configFile, 'Flow.fluid'); % Fluid
     Airfoil = checkairfoil(Airfoil, configFile, DEF); % Airfoils data
 
     Blade = checkblade(Blade, configFile, length(Airfoil), DEF); % Blade
     Op = checkop(Op, configFile, DEF, numel(Blade), Flow.fluid); % Operating points
+    Mod = checkmod(Mod, configFile, numel(Blade), DEF); % Models, solvers, etc.
 
     % ===========================================
     % Extra warnings
@@ -169,7 +171,7 @@ end
 % ==================================================================================================
 % ==================================== Models and solvers ==========================================
 % ==================================================================================================
-function Mod = checkmod(Mod, configFile, DEF)
+function Mod = checkmod(Mod, configFile, nRotors, DEF)
 
     % Solvers
     Mod.solvers = cellstr(Mod.solvers);
@@ -195,6 +197,21 @@ function Mod = checkmod(Mod, configFile, DEF)
                        {'scalar', 'positive'}, ...
                        configFile, 'Mod.Num.maxIter');
 
+    if nRotors > 1
+        Mod.coax.model = validatestring(Mod.coax.model, DEF.CONTRACTION_MODEL, ...
+                                        configFile, 'Mod.coax.model');
+        if ~strcmpi(Mod.coax.model, 'none')
+            Mod.coax.contraType = validatestring(Mod.coax.contraType, DEF.CONTRACTION_TYPE, ...
+                                                 configFile, 'Mod.coax.contraType');
+        end
+        if strcmp(Mod.coax.contraType, 'value')
+            validateattributes(Mod.coax.contraVal, ...
+                               {'double'}, ...
+                               {'scalar', 'positive', '<=', 1}, ...
+                               configFile, 'Mod.Num.convCrit');
+        end
+    end
+
 end
 
 % ==================================================================================================
@@ -345,6 +362,16 @@ function Blade = checkblade(Blade, configFile, nAirfoils, DEF)
                             '<=', nAirfoils}, ...
                            configFile, 'Blade.iAirfoil');
 
+        validateattributes(Blade(i).hubPos, ...
+                           {'numeric'}, ...
+                           {'vector', 'finite', 'nonnan', 'numel', 3}, ...
+                           configFile, 'Blade.hubPos');
+        validateattributes(Blade(i).spinDir, ...
+                           {'numeric'}, ...
+                           {'scalar', 'integer', 'nonzero', ...
+                            '<=', 1, '>=', -1}, ...
+                           configFile, 'Blade.spinDir');
+
     end
 
     % Discretization