From dd506c3f14a130bcb4ce28bfb9318487889e46d7 Mon Sep 17 00:00:00 2001
From: andy <andy>
Date: Thu, 20 Oct 2011 16:40:04 +0100
Subject: [PATCH] ENH: Added new rotorDiskSource momentum source model for
 rotor blades/airfoils

---
 src/finiteVolume/Make/files                   |   6 +
 .../rotorDiskSource/bladeModel/bladeModel.C   | 195 +++++++
 .../rotorDiskSource/bladeModel/bladeModel.H   | 151 ++++++
 .../profileModel/lookup/lookupProfile.C       | 148 ++++++
 .../profileModel/lookup/lookupProfile.H       | 124 +++++
 .../profileModel/profileModel.C               |  99 ++++
 .../profileModel/profileModel.H               | 136 +++++
 .../profileModel/profileModelList.C           | 121 +++++
 .../profileModel/profileModelList.H           |  91 ++++
 .../profileModel/series/seriesProfile.C       | 116 +++++
 .../profileModel/series/seriesProfile.H       | 116 +++++
 .../rotorDiskSource/rotorDiskSource.C         | 492 ++++++++++++++++++
 .../rotorDiskSource/rotorDiskSource.H         | 249 +++++++++
 .../rotorDiskSourceTemplates.C                | 205 ++++++++
 14 files changed, 2249 insertions(+)
 create mode 100644 src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/bladeModel/bladeModel.C
 create mode 100644 src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/bladeModel/bladeModel.H
 create mode 100644 src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/lookup/lookupProfile.C
 create mode 100644 src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/lookup/lookupProfile.H
 create mode 100644 src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/profileModel.C
 create mode 100644 src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/profileModel.H
 create mode 100644 src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/profileModelList.C
 create mode 100644 src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/profileModelList.H
 create mode 100644 src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/series/seriesProfile.C
 create mode 100644 src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/series/seriesProfile.H
 create mode 100644 src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/rotorDiskSource.C
 create mode 100644 src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/rotorDiskSource.H
 create mode 100644 src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/rotorDiskSourceTemplates.C

diff --git a/src/finiteVolume/Make/files b/src/finiteVolume/Make/files
index 3bcd7b80d55..cc2f3e4225a 100644
--- a/src/finiteVolume/Make/files
+++ b/src/finiteVolume/Make/files
@@ -390,6 +390,12 @@ $(basicSource)/actuationDiskSource/actuationDiskSource.C
 $(basicSource)/radialActuationDiskSource/radialActuationDiskSource.C
 $(basicSource)/explicitSource/explicitSource.C
 $(basicSource)/explicitSetValue/explicitSetValue.C
+$(basicSource)/rotorDiskSource/rotorDiskSource.C
+$(basicSource)/rotorDiskSource/bladeModel/bladeModel.C
+$(basicSource)/rotorDiskSource/profileModel/profileModel.C
+$(basicSource)/rotorDiskSource/profileModel/profileModelList.C
+$(basicSource)/rotorDiskSource/profileModel/lookup/lookupProfile.C
+$(basicSource)/rotorDiskSource/profileModel/series/seriesProfile.C
 
 
 LIB = $(FOAM_LIBBIN)/libfiniteVolume
diff --git a/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/bladeModel/bladeModel.C b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/bladeModel/bladeModel.C
new file mode 100644
index 00000000000..a1f76d3e993
--- /dev/null
+++ b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/bladeModel/bladeModel.C
@@ -0,0 +1,195 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "bladeModel.H"
+#include "unitConversion.H"
+#include "Tuple2.H"
+#include "vector.H"
+#include "IFstream.H"
+
+
+// * * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * //
+
+bool Foam::bladeModel::readFromFile() const
+{
+    return fName_ != fileName::null;
+}
+
+
+void Foam::bladeModel::interpolateWeights
+(
+    const scalar& xIn,
+    const List<scalar>& values,
+    label& i1,
+    label& i2,
+    scalar& ddx
+) const
+{
+    scalar x = -GREAT;
+    label nElem = values.size();
+
+    i2 = 0;
+    while ((x < xIn) && (i2 < nElem))
+    {
+        x = values[i2];
+        i2++;
+    }
+
+    if (i2 == 0)
+    {
+        i1 = i2;
+        ddx = 0.0;
+        return;
+    }
+    else if (i2 == values.size())
+    {
+        i2 = values.size() - 1;
+        i1 = i2;
+        ddx = 0.0;
+        return;
+    }
+    else
+    {
+        i1 = i2 - 1;
+        ddx = (xIn - values[i1])/(values[i2] - values[i1]);
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::bladeModel::bladeModel(const dictionary& dict)
+:
+    profileName_(),
+    profileID_(),
+    radius_(),
+    twist_(),
+    chord_(),
+    fName_(fileName::null)
+{
+    List<Tuple2<word, vector> > data;
+    if (readFromFile())
+    {
+        IFstream is(fName_);
+        is  >> data;
+    }
+    else
+    {
+        dict.lookup("data") >> data;
+    }
+
+
+    if (data.size() > 0)
+    {
+        profileName_.setSize(data.size());
+        profileID_.setSize(data.size());
+        radius_.setSize(data.size());
+        twist_.setSize(data.size());
+        chord_.setSize(data.size());
+
+        forAll(data, i)
+        {
+            profileName_[i] = data[i].first();
+            profileID_[i] = -1;
+            radius_[i] = data[i].second()[0];
+            twist_[i] = degToRad(data[i].second()[1]);
+            chord_[i] = data[i].second()[2];
+        }
+    }
+    else
+    {
+        FatalErrorIn
+        (
+            "Foam::bladeModel::bladeModel"
+            "("
+                "const dictionary&, "
+                "const word&"
+            ")"
+        )   << "No blade data specified" << exit(FatalError);
+    }
+}
+
+// * * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * //
+
+Foam::bladeModel::~bladeModel()
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+const Foam::List<Foam::word>& Foam::bladeModel::profileName() const
+{
+    return profileName_;
+}
+
+
+const Foam::List<Foam::label>& Foam::bladeModel::profileID() const
+{
+    return profileID_;
+}
+
+
+const Foam::List<Foam::scalar>& Foam::bladeModel::radius() const
+{
+    return radius_;
+}
+
+
+const Foam::List<Foam::scalar>& Foam::bladeModel::twist() const
+{
+    return twist_;
+}
+
+
+const Foam::List<Foam::scalar>& Foam::bladeModel::chord() const
+{
+    return chord_;
+}
+
+
+Foam::List<Foam::label>& Foam::bladeModel::profileID()
+{
+    return profileID_;
+}
+
+
+void Foam::bladeModel::interpolate
+(
+    const scalar radius,
+    scalar& twist,
+    scalar& chord,
+    label& i1,
+    label& i2,
+    scalar& invDr
+) const
+{
+    interpolateWeights(radius, radius_, i1, i2, invDr);
+
+    twist = invDr*(twist_[i2] - twist_[i1]) + twist_[i1];
+    chord = invDr*(chord_[i2] - chord_[i1]) + chord_[i1];
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/bladeModel/bladeModel.H b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/bladeModel/bladeModel.H
new file mode 100644
index 00000000000..df26e7bd401
--- /dev/null
+++ b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/bladeModel/bladeModel.H
@@ -0,0 +1,151 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::bladeModel
+
+Description
+    Blade model class
+
+SourceFiles
+    bladeModel.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef bladeModel_H
+#define bladeModel_H
+
+#include "List.H"
+#include "dictionary.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                         Class bladeModel Declaration
+\*---------------------------------------------------------------------------*/
+
+class bladeModel
+{
+
+protected:
+
+    // Protected data
+
+        //- Corresponding profile name per section
+        List<word> profileName_;
+
+        //- Corresponding profile ID per section
+        List<label> profileID_;
+
+        //- Radius [m]
+        List<scalar> radius_;
+
+        //- Twist [deg] on input, converted to [rad]
+        List<scalar> twist_;
+
+        //- Chord [m]
+        List<scalar> chord_;
+
+        //- File name (optional)
+        fileName fName_;
+
+
+    // Protected Member Functions
+
+        //- Return ture if file name is set
+        bool readFromFile() const;
+
+        //- Return the interpolation indices and gradient
+        void interpolateWeights
+        (
+            const scalar& xIn,
+            const List<scalar>& values,
+            label& i1,
+            label& i2,
+            scalar& ddx
+        ) const;
+
+
+public:
+
+    //- Constructor
+    bladeModel(const dictionary& dict);
+
+
+    //- Destructor
+    virtual ~bladeModel();
+
+
+    // Member functions
+
+        // Access
+
+            //- Return const access to the profile name list
+            const List<word>& profileName() const;
+
+            //- Return const access to the profile ID list
+            const List<label>& profileID() const;
+
+            //- Return const access to the radius list
+            const List<scalar>& radius() const;
+
+            //- Return const access to the twist list
+            const List<scalar>& twist() const;
+
+            //- Return const access to the chord list
+            const List<scalar>& chord() const;
+
+
+        // Edit
+
+            //- Return non-const access to the profile ID list
+            List<label>& profileID();
+
+
+        // Evaluation
+
+            //- Return the twist and chord for a given radius
+            virtual void interpolate
+            (
+                const scalar radius,
+                scalar& twist,
+                scalar& chord,
+                label& i1,
+                label& i2,
+                scalar& invDr
+            ) const;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/lookup/lookupProfile.C b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/lookup/lookupProfile.C
new file mode 100644
index 00000000000..ae80d03995d
--- /dev/null
+++ b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/lookup/lookupProfile.C
@@ -0,0 +1,148 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "lookupProfile.H"
+#include "addToRunTimeSelectionTable.H"
+#include "vector.H"
+#include "unitConversion.H"
+#include "IFstream.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    defineTypeNameAndDebug(lookupProfile, 0);
+    addToRunTimeSelectionTable(profileModel, lookupProfile, dictionary);
+}
+
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+void Foam::lookupProfile::interpolateWeights
+(
+    const scalar& xIn,
+    const List<scalar>& values,
+    label& i1,
+    label& i2,
+    scalar& ddx
+) const
+{
+    scalar x = -GREAT;
+    label nElem = values.size();
+
+    i2 = 0;
+    while ((x < xIn) && (i2 < nElem))
+    {
+        x = values[i2];
+        i2++;
+    }
+
+    if (i2 == 0)
+    {
+        i1 = i2;
+        ddx = 0.0;
+        return;
+    }
+    else if (i2 == values.size())
+    {
+        i2 = values.size() - 1;
+        i1 = i2;
+        ddx = 0.0;
+        return;
+    }
+    else
+    {
+        i1 = i2 - 1;
+        ddx = (xIn - values[i1])/(values[i2] - values[i1]);
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::lookupProfile::lookupProfile
+(
+    const dictionary& dict,
+    const word& modelName
+)
+:
+    profileModel(dict, modelName),
+    AOA_(),
+    Cd_(),
+    Cl_()
+{
+    List<vector> data;
+    if (readFromFile())
+    {
+        IFstream is(fName_);
+        is  >> data;
+    }
+    else
+    {
+        dict.lookup("data") >> data;
+    }
+
+    if (data.size() > 0)
+    {
+        AOA_.setSize(data.size());
+        Cd_.setSize(data.size());
+        Cl_.setSize(data.size());
+
+        forAll(data, i)
+        {
+            AOA_[i] = degToRad(data[i][0]);
+            Cd_[i] = data[i][1];
+            Cl_[i] = data[i][2];
+        }
+    }
+    else
+    {
+        FatalErrorIn
+        (
+            "Foam::lookupProfile::lookupProfile"
+            "("
+                "const dictionary&, "
+                "const word&"
+            ")"
+        )   << "No profile data specified" << exit(FatalError);
+    }
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+void Foam::lookupProfile::Cdl(const scalar alpha, scalar& Cd, scalar& Cl) const
+{
+    label i1 = -1;
+    label i2 = -1;
+    scalar invAlpha = -1.0;
+    interpolateWeights(alpha, AOA_, i1, i2, invAlpha);
+
+    Cd = invAlpha*(Cd_[i2] - Cd_[i1]) + Cd_[i1];
+    Cl = invAlpha*(Cl_[i2] - Cl_[i1]) + Cl_[i1];
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/lookup/lookupProfile.H b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/lookup/lookupProfile.H
new file mode 100644
index 00000000000..deb5c1f725a
--- /dev/null
+++ b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/lookup/lookupProfile.H
@@ -0,0 +1,124 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::lookupProfile
+
+Description
+    Look-up based profile data - drag and lift coefficients are lineraly
+    interpolated based on the supplied angle of attack
+
+    Input in list format:
+
+        data
+        (
+            (AOA1 Cd1 Cl2)
+            (AOA2 Cd2 Cl2)
+            ...
+            (AOAN CdN CdN)
+        );
+
+    where:
+        AOA = angle of attack [deg] converted to [rad] internally
+        Cd  = drag coefficient
+        Cl  = lift coefficient
+
+SourceFiles
+    lookupProfile.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef lookupProfile_H
+#define lookupProfile_H
+
+#include "profileModel.H"
+#include "List.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                        Class lookupProfile Declaration
+\*---------------------------------------------------------------------------*/
+
+class lookupProfile
+:
+    public profileModel
+{
+
+protected:
+
+    // Protected data
+
+        //- List of angle-of-attack values [deg] on input, converted to [rad]
+        List<scalar> AOA_;
+
+        //- List of drag coefficient values
+        List<scalar> Cd_;
+
+        //- List of lift coefficient values
+        List<scalar> Cl_;
+
+
+    // Protected Member Functions
+
+        //- Return the interpolation indices and gradient
+        void interpolateWeights
+        (
+            const scalar& xIn,
+            const List<scalar>& values,
+            label& i1,
+            label& i2,
+            scalar& ddx
+        ) const;
+
+
+public:
+
+    //- Runtime type information
+    TypeName("lookup");
+
+    //- Constructor
+    lookupProfile(const dictionary& dict, const word& modelName);
+
+
+    // Member functions
+
+        // Evaluation
+
+            //- Return the Cd and Cl for a given angle-of-attack
+            virtual void Cdl(const scalar alpha, scalar& Cd, scalar& Cl) const;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/profileModel.C b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/profileModel.C
new file mode 100644
index 00000000000..c9008d276c6
--- /dev/null
+++ b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/profileModel.C
@@ -0,0 +1,99 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "profileModel.H"
+#include "addToRunTimeSelectionTable.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    defineTypeNameAndDebug(profileModel, 0);
+    defineRunTimeSelectionTable(profileModel, dictionary);
+}
+
+
+// * * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * //
+
+bool Foam::profileModel::readFromFile() const
+{
+    return fName_ != fileName::null;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::profileModel::profileModel(const dictionary& dict, const word& name)
+:
+    dict_(dict),
+    name_(name),
+    fName_(fileName::null)
+{
+    dict.readIfPresent("fileName", fName_);
+}
+
+// * * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * //
+
+Foam::profileModel::~profileModel()
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+const Foam::word& Foam::profileModel::name() const
+{
+    return name_;
+}
+
+
+Foam::autoPtr<Foam::profileModel> Foam::profileModel::New
+(
+    const dictionary& dict
+)
+{
+    const word& modelName(dict.dictName());
+
+    const word modelType(dict.lookup("type"));
+
+    Info<< "    - creating " << modelType << " profile " << modelName << endl;
+
+    dictionaryConstructorTable::iterator cstrIter =
+        dictionaryConstructorTablePtr_->find(modelType);
+
+    if (cstrIter == dictionaryConstructorTablePtr_->end())
+    {
+        FatalErrorIn("profileModel::New(const dictionary&)")
+            << "Unknown profile model type " << modelType
+            << nl << nl
+            << "Valid model types are :" << nl
+            << dictionaryConstructorTablePtr_->sortedToc()
+            << exit(FatalError);
+    }
+
+    return autoPtr<profileModel>(cstrIter()(dict, modelName));
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/profileModel.H b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/profileModel.H
new file mode 100644
index 00000000000..5c850ec1bb3
--- /dev/null
+++ b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/profileModel.H
@@ -0,0 +1,136 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::profileModel
+
+Description
+    Base class for profile models
+
+SourceFiles
+    profileModel.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef profileModel_H
+#define profileModel_H
+
+#include "autoPtr.H"
+#include "runTimeSelectionTables.H"
+#include "dictionary.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                        Class profileModel Declaration
+\*---------------------------------------------------------------------------*/
+
+class profileModel
+{
+
+protected:
+
+    // Protected data
+
+        //- Coefficients dictionary
+        const dictionary dict_;
+
+        //- Name of profile model
+        const word name_;
+
+        //- File name (optional)
+        fileName fName_;
+
+
+    // Protected Member Functions
+
+        //- Return ture if file name is set
+        bool readFromFile() const;
+
+
+public:
+
+    //- Runtime type information
+    TypeName("profileModel");
+
+
+        // Declare run-time constructor selection table
+        declareRunTimeSelectionTable
+        (
+            autoPtr,
+            profileModel,
+            dictionary,
+            (
+                const dictionary& dict,
+                const word& modelName
+            ),
+            (dict, modelName)
+        );
+
+
+    // Selectors
+
+        //- Return a reference to the selected basicSource model
+        static autoPtr<profileModel> New(const dictionary& dict);
+
+
+    //- Constructor
+    profileModel(const dictionary& dict, const word& modelName);
+
+
+    //- Destructor
+    virtual ~profileModel();
+
+
+    // Member functions
+
+        // Access
+
+            //- Return const access to the source name
+            const word& name() const;
+
+
+        // Evaluation
+
+            //- Return the Cd and Cl for a given angle-of-attack
+            virtual void Cdl
+            (
+                const scalar alpha,
+                scalar& Cd,
+                scalar& Cl
+            ) const = 0;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/profileModelList.C b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/profileModelList.C
new file mode 100644
index 00000000000..904b01372b5
--- /dev/null
+++ b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/profileModelList.C
@@ -0,0 +1,121 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "profileModelList.H"
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::profileModelList::profileModelList
+(
+    const dictionary& dict,
+    const bool readFields
+)
+:
+    PtrList<profileModel>(),
+    dict_(dict)
+{
+    if (readFields)
+    {
+        wordList modelNames(dict.toc());
+
+        Info<< "    Constructing blade profiles:" << endl;
+
+        if (modelNames.size() > 0)
+        {
+            this->setSize(modelNames.size());
+
+            forAll(modelNames, i)
+            {
+                const word& modelName = modelNames[i];
+
+                this->set
+                (
+                    i,
+                    profileModel::New(dict.subDict(modelName))
+                );
+            }
+        }
+        else
+        {
+            Info<< "        none" << endl;
+        }
+    }
+}
+
+
+// * * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * //
+
+Foam::profileModelList::~profileModelList()
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+void Foam::profileModelList::connectBlades
+(
+    const List<word>& names,
+    List<label>& addr
+) const
+{
+    // construct the addressing between blade sections and profiles
+    forAll(names, bI)
+    {
+        label index = -1;
+        const word& profileName = names[bI];
+
+        forAll(*this, pI)
+        {
+            const profileModel& pm = this->operator[](pI);
+
+            if (pm.name() == profileName)
+            {
+                index = pI;
+                break;
+            }
+        }
+
+        if (index == -1)
+        {
+            List<word> profileNames(size());
+            forAll(*this, i)
+            {
+                const profileModel& pm = this->operator[](i);
+                profileNames[i] = pm.name();
+            }
+
+            FatalErrorIn("void Foam::connectBlades(List<word>& names) const")
+                << "Profile " << profileName << " could not be found "
+                << "in profile list.  Available profiles are"
+                << profileNames << exit(FatalError);
+        }
+        else
+        {
+            addr[bI] = index;
+        }
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/profileModelList.H b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/profileModelList.H
new file mode 100644
index 00000000000..74326dced4b
--- /dev/null
+++ b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/profileModelList.H
@@ -0,0 +1,91 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::profileModelList
+
+Description
+    Base class for profile models
+
+SourceFiles
+    profileModel.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef profileModelList_H
+#define profileModelList_H
+
+#include "PtrList.H"
+#include "profileModel.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                    Class profileModelList Declaration
+\*---------------------------------------------------------------------------*/
+
+class profileModelList
+:
+    public PtrList<profileModel>
+{
+
+protected:
+
+    // Protected data
+
+        //- Dictionary
+        const dictionary dict_;
+
+
+public:
+
+    //- Constructor
+    profileModelList(const dictionary& dict, const bool readFields = true);
+
+    //- Destructor
+    ~profileModelList();
+
+
+    // Member Functions
+
+        //- Set blade->profile addressing
+        void connectBlades
+        (
+            const List<word>& names,
+            List<label>& addr
+        ) const;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/series/seriesProfile.C b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/series/seriesProfile.C
new file mode 100644
index 00000000000..865ed3ab62e
--- /dev/null
+++ b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/series/seriesProfile.C
@@ -0,0 +1,116 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "seriesProfile.H"
+#include "addToRunTimeSelectionTable.H"
+#include "IFstream.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+    defineTypeNameAndDebug(seriesProfile, 0);
+    addToRunTimeSelectionTable(profileModel, seriesProfile, dictionary);
+}
+
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+Foam::scalar Foam::seriesProfile::evaluate
+(
+    const scalar& xIn,
+    const List<scalar>& values
+) const
+{
+    scalar result = 0.0;
+
+    forAll(values, i)
+    {
+        result += values[i]*cos((i+1)*xIn);
+    }
+
+    return result;
+}
+
+
+// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
+
+Foam::seriesProfile::seriesProfile
+(
+    const dictionary& dict,
+    const word& modelName
+)
+:
+    profileModel(dict, modelName),
+    CdCoeffs_(),
+    ClCoeffs_()
+{
+    if (readFromFile())
+    {
+        IFstream is(fName_);
+        is  >> CdCoeffs_ >> ClCoeffs_;
+    }
+    else
+    {
+        dict.lookup("CdCoeffs") >> CdCoeffs_;
+        dict.lookup("ClCoeffs") >> ClCoeffs_;
+    }
+
+
+    if (CdCoeffs_.empty())
+    {
+        FatalErrorIn
+        (
+            "Foam::seriesProfile::seriesProfile"
+            "("
+                "const dictionary&, "
+                "const word&"
+            ")"
+        )   << "CdCoeffs must be specified" << exit(FatalError);
+    }
+    if (ClCoeffs_.empty())
+    {
+        FatalErrorIn
+        (
+            "Foam::seriesProfile::seriesProfile"
+            "("
+                "const dictionary&, "
+                "const word&"
+            ")"
+        )   << "ClCoeffs must be specified" << exit(FatalError);
+    }
+}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+void Foam::seriesProfile::Cdl(const scalar alpha, scalar& Cd, scalar& Cl) const
+{
+    Cd = evaluate(alpha, CdCoeffs_);
+    Cl = evaluate(alpha, ClCoeffs_);
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/series/seriesProfile.H b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/series/seriesProfile.H
new file mode 100644
index 00000000000..73b474aae1f
--- /dev/null
+++ b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/profileModel/series/seriesProfile.H
@@ -0,0 +1,116 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::seriesProfile
+
+Description
+    Series-up based profile data - drag and lift coefficients computed as
+    sum of cosine series
+
+        Cd = sum_i(CdCoeff)*cos(i*AOA)
+        Cl = sum_i(ClCoeff)*cos(i*AOA)
+
+    where:
+        AOA = angle of attack [deg] converted to [rad] internally
+        Cd = drag coefficent
+        Cl = lift coefficent
+
+    Input in two (arbitrary length) lists:
+
+        CdCoeffs (coeff1 coeff2 ... coeffN);
+        ClCoeffs (coeff1 coeff2 ... coeffN);
+
+SourceFiles
+    seriesProfile.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef seriesProfile_H
+#define seriesProfile_H
+
+#include "profileModel.H"
+#include "List.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                        Class seriesProfile Declaration
+\*---------------------------------------------------------------------------*/
+
+class seriesProfile
+:
+    public profileModel
+{
+
+protected:
+
+    // Protected data
+
+        //- List of drag coefficient values
+        List<scalar> CdCoeffs_;
+
+        //- List of lift coefficient values
+        List<scalar> ClCoeffs_;
+
+
+    // Protected Member Functions
+
+        //- Evaluate
+        scalar evaluate
+        (
+            const scalar& xIn,
+            const List<scalar>& values
+        ) const;
+
+
+public:
+
+    //- Runtime type information
+    TypeName("series");
+
+    //- Constructor
+    seriesProfile(const dictionary& dict, const word& modelName);
+
+
+    // Member functions
+
+        // Evaluation
+
+            //- Return the Cd and Cl for a given angle-of-attack
+            virtual void Cdl(const scalar alpha, scalar& Cd, scalar& Cl) const;
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/rotorDiskSource.C b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/rotorDiskSource.C
new file mode 100644
index 00000000000..03eb60364b9
--- /dev/null
+++ b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/rotorDiskSource.C
@@ -0,0 +1,492 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "rotorDiskSource.H"
+#include "addToRunTimeSelectionTable.H"
+#include "mathematicalConstants.H"
+#include "unitConversion.H"
+#include "geometricOneField.H"
+
+using namespace Foam::constant;
+
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+namespace Foam
+{
+    defineTypeNameAndDebug(rotorDiskSource, 0);
+    addToRunTimeSelectionTable(basicSource, rotorDiskSource, dictionary);
+
+    template<> const char* NamedEnum<rotorDiskSource::geometryModeType, 2>::
+        names[] =
+    {
+        "auto",
+        "specified"
+    };
+
+    const NamedEnum<rotorDiskSource::geometryModeType, 2>
+        rotorDiskSource::geometryModeTypeNames_;
+
+    template<> const char* NamedEnum<rotorDiskSource::inletFlowType, 3>::
+        names[] =
+    {
+        "fixed",
+        "surfaceNormal",
+        "local"
+    };
+
+    const NamedEnum<rotorDiskSource::inletFlowType, 3>
+        rotorDiskSource::inletFlowTypeNames_;
+}
+
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+void Foam::rotorDiskSource::checkData()
+{
+    switch (selectionMode())
+    {
+        case smCellSet:
+        case smCellZone:
+        case smAll:
+        {
+            // set the profile ID for each blade section
+            profiles_.connectBlades(blade_.profileName(), blade_.profileID());
+            switch (inletFlow_)
+            {
+                case ifFixed:
+                {
+                    coeffs_.lookup("inletVelocity") >> inletVelocity_;
+                    break;
+                }
+                case ifSurfaceNormal:
+                {
+                    scalar UIn(readScalar(coeffs_.lookup("inletNormalVelocity")));
+                    inletVelocity_ = -coordSys_.e3()*UIn;
+                    break;
+                }
+                case ifLocal:
+                {
+                    // do nothing
+                    break;
+                }
+                default:
+                {
+                    FatalErrorIn("void rotorDiskSource::checkData()")
+                        << "Unknown inlet velocity type" << abort(FatalError);
+                }
+            }
+
+
+            break;
+        }
+        default:
+        {
+            FatalErrorIn("void rotorDiskSource::checkData()")
+                << "Source cannot be used with '"
+                << selectionModeTypeNames_[selectionMode()]
+                << "' mode.  Please use one of: " << nl
+                << selectionModeTypeNames_[smCellSet] << nl
+                << selectionModeTypeNames_[smCellZone] << nl
+                << selectionModeTypeNames_[smAll]
+                << exit(FatalError);
+        }
+    }
+}
+
+
+void Foam::rotorDiskSource::setFaceArea(vector& axis, const bool correct)
+{
+    // calculate rotor face areas
+    const vectorField& Sf = mesh_.Sf();
+    const scalarField& magSf = mesh_.magSf();
+
+    boolList selectedCells(mesh_.nCells(), false);
+    boolList processedFaces(mesh_.nFaces(), false);
+    UIndirectList<bool>(selectedCells, cells_) = boolList(cells_.size(), true);
+
+    vector n = vector::zero;
+
+    label nFace = 0;
+    area_ = 0.0;
+    forAll(cells_, i)
+    {
+        const label cellI = cells_[i];
+        const cell& cFaces = mesh_.cells()[cellI];
+        forAll(cFaces, j)
+        {
+            const label faceI = cFaces[j];
+            label own = mesh_.owner()[faceI];
+            label nbr = mesh_.neighbour()[faceI];
+            if
+            (
+                !processedFaces[faceI]
+             && (selectedCells[own] != selectedCells[nbr])
+            )
+            {
+                if (selectedCells[own])
+                {
+                    if (((Sf[faceI]/magSf[faceI]) & axis) > 0.8)
+                    {
+                        area_[i] += magSf[faceI];
+                        n += Sf[faceI];
+                        nFace++;
+                    }
+                }
+                else if (selectedCells[nbr])
+                {
+                    if (((-Sf[faceI]/magSf[faceI]) & axis) > 0.8)
+                    {
+                        area_[i] += magSf[faceI];
+                        n -= Sf[faceI];
+                        nFace++;
+                    }
+                }
+                processedFaces[faceI] = true;
+            }
+        }
+    }
+
+    if (correct && (nFace > 0))
+    {
+        axis = n/mag(n);
+    }
+}
+
+
+void Foam::rotorDiskSource::createCoordinateSystem()
+{
+    // construct the local rotor co-prdinate system
+    vector origin(vector::zero);
+    vector axis(vector::zero);
+    vector refDir(vector::zero);
+
+    geometryModeType gm =
+        geometryModeTypeNames_.read(coeffs_.lookup("geometryMode"));
+
+    switch (gm)
+    {
+        case gmAuto:
+        {
+            // determine rotation origin
+            scalar sumV = 0.0;
+            const scalarField& V = mesh_.V();
+            const vectorField& C = mesh_.C();
+            forAll(cells_, i)
+            {
+                const label cellI = cells_[i];
+                sumV += V[cellI];
+                origin += V[cellI]*C[cellI];
+            }
+            origin /= sumV;
+
+            // determine first radial vector
+            vector dx1(vector::zero);
+            scalar magR = -GREAT;
+            forAll(cells_, i)
+            {
+                const label cellI = cells_[i];
+                vector test = C[cellI] - origin;
+                if (mag(test) > magR)
+                {
+                    dx1 = test;
+                    magR = mag(test);
+                }
+            }
+
+            // determine second radial vector and cross to determine axis
+            forAll(cells_, i)
+            {
+                const label cellI = cells_[i];
+                vector dx2 = C[cellI] - origin;
+                if (mag(dx2) > 0.5*magR)
+                {
+                    axis = dx1 ^ dx2;
+                    if (mag(axis) > SMALL)
+                    {
+                        break;
+                    }
+                }
+            }
+            axis /= mag(axis);
+
+            // axis direction is somewhat arbitrary - check if user needs
+            // needs to reverse
+            bool reverse(readBool(coeffs_.lookup("reverseAxis")));
+            if (reverse)
+            {
+                axis *= -1.0;
+            }
+
+            coeffs_.lookup("refDirection") >> refDir;
+
+            // set the face areas and apply correction to calculated axis
+            // e.g. if cellZone is more than a single layer in thickness
+            setFaceArea(axis, true);
+
+            break;
+        }
+        case gmSpecified:
+        {
+            coeffs_.lookup("origin") >> origin;
+            coeffs_.lookup("axis") >> axis;
+            coeffs_.lookup("refDirection") >> refDir;
+
+            setFaceArea(axis, false);
+
+            break;
+        }
+        default:
+        {
+            FatalErrorIn
+            (
+                "rotorDiskSource::createCoordinateSystem(const geometryMode&);"
+            )   << "Unknown geometryMode " << geometryModeTypeNames_[gm]
+                << ". Available geometry modes include " << geometryModeTypeNames_
+                << exit(FatalError);
+        }
+    }
+
+    coordSys_ = cylindricalCS("rotorCoordSys", origin, axis, refDir, false);
+
+    const scalar sumArea = gSum(area_);
+    const scalar diameter = Foam::sqrt(4.0*sumArea/mathematical::pi);
+    Info<< "    Rotor gometry:" << nl
+        << "    - disk diameter = " << diameter << nl
+        << "    - disk area     = " << sumArea << nl
+        << "    - origin        = " << coordSys_.origin() << nl
+        << "    - r-axis        = " << coordSys_.e1() << nl
+        << "    - psi-axis      = " << coordSys_.e2() << nl
+        << "    - z-axis        = " << coordSys_.e3() << endl;
+}
+
+
+void Foam::rotorDiskSource::constructGeometry()
+{
+    const vectorField& C = mesh_.C();
+
+    const vector rDir = coordSys_.e1();
+    const vector zDir = coordSys_.e3();
+
+    forAll(cells_, i)
+    {
+        const label cellI = cells_[i];
+
+        // position in rotor co-ordinate system
+        x_[i] = coordSys_.localPosition(C[cellI]);
+
+        // cache max radius
+        rMax_ = max(rMax_, x_[i].x());
+
+        // determine swept angle relative to rDir axis
+        scalar psi = x_[i].y() - rDir.y();
+
+        // blade flap angle
+        scalar beta = flap_.beta0 - flap_.beta1*cos(psi) - flap_.beta2*sin(psi);
+
+        // determine rotation tensor to convert into the rotor cone plane
+        scalar c = cos(-beta);
+        scalar s = sin(-beta);
+        R_[i] = tensor(1, 0, 0, 0, c, s, 0, -s, c);
+
+        // geometric angle of attack - not including twist
+        alphag_[i] = trim_.alphaC - trim_.A*cos(psi) - trim_.B*sin(psi);
+    }
+}
+
+
+Foam::tmp<Foam::vectorField> Foam::rotorDiskSource::inflowVelocity
+(
+    const volVectorField& U
+) const
+{
+    switch (inletFlow_)
+    {
+        case ifFixed:
+        case ifSurfaceNormal:
+        {
+            return tmp<vectorField>
+            (
+                new vectorField(mesh_.nCells(), inletVelocity_)
+            );
+
+            break;
+        }
+        case ifLocal:
+        {
+            return U.internalField();
+
+            break;
+        }
+        default:
+        {
+            FatalErrorIn
+            (
+                "Foam::tmp<Foam::vectorField> "
+                "Foam::rotorDiskSource::inflowVelocity"
+                "(const volVectorField&) const"
+            )   << "Unknown inlet flow specification" << abort(FatalError);
+        }
+    }
+
+    return tmp<vectorField>(new vectorField(mesh_.nCells(), vector::zero));
+}
+
+
+// * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * * //
+
+Foam::rotorDiskSource::rotorDiskSource
+(
+    const word& name,
+    const word& modelType,
+    const dictionary& dict,
+    const fvMesh& mesh
+
+)
+:
+    basicSource(name, modelType, dict, mesh),
+    coeffs_(dict_.subDict(type() + "Coeffs")),
+    rhoName_("none"),
+    omega_(0.0),
+    nBlades_(0),
+    inletFlow_(ifLocal),
+    inletVelocity_(vector::zero),
+    tipEffect_(1.0),
+    flap_(),
+    trim_(),
+    blade_(coeffs_.subDict("blade")),
+    profiles_(coeffs_.subDict("profiles")),
+    x_(cells_.size(), vector::zero),
+    R_(cells_.size(), I),
+    alphag_(cells_.size(), 0.0),
+    area_(cells_.size(), 0.0),
+    coordSys_(false),
+    rMax_(0.0)
+{
+    read(dict);
+}
+
+
+// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //
+
+Foam::rotorDiskSource::~rotorDiskSource()
+{}
+
+
+// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
+
+void Foam::rotorDiskSource::addSu(fvMatrix<vector>& UEqn)
+{
+    // add source to lhs of eqn
+
+    const volVectorField& U = UEqn.psi();
+
+    if (UEqn.dimensions() == dimForce)
+    {
+        coeffs_.lookup("rhoName") >> rhoName_;
+
+        const volScalarField& rho =
+            mesh_.lookupObject<volScalarField>(rhoName_);
+
+        UEqn += calculateForces
+            (
+                rho.internalField(),
+                inflowVelocity(U),
+                dimForce/dimVolume
+            );
+    }
+    else
+    {
+        UEqn += calculateForces
+            (
+                oneField(),
+                inflowVelocity(U),
+                dimForce/dimVolume/dimDensity
+            );
+    }
+}
+
+
+void Foam::rotorDiskSource::addSu(fvMatrix<scalar>& UEqn)
+{
+    // do nothing
+}
+
+
+void Foam::rotorDiskSource::writeData(Ostream& os) const
+{
+    os  << indent << name_ << endl;
+    dict_.write(os);
+}
+
+
+bool Foam::rotorDiskSource::read(const dictionary& dict)
+{
+    if (basicSource::read(dict))
+    {
+        coeffs_ = dict.subDict(type() + "Coeffs");
+
+        scalar rpm(readScalar(coeffs_.lookup("rpm")));
+        omega_ = rpm/60.0*mathematical::twoPi;
+
+        coeffs_.lookup("nBlades") >> nBlades_;
+
+        inletFlow_ = inletFlowTypeNames_.read(coeffs_.lookup("inletFlowType"));
+
+        coeffs_.lookup("tipEffect") >> tipEffect_;
+
+        const dictionary& flapCoeffs(coeffs_.subDict("flapCoeffs"));
+        flapCoeffs.lookup("beta0") >> flap_.beta0;
+        flapCoeffs.lookup("beta1") >> flap_.beta1;
+        flapCoeffs.lookup("beta2") >> flap_.beta2;
+        flap_.beta0 = degToRad(flap_.beta0);
+
+        const dictionary& trimCoeffs(coeffs_.subDict("trimCoeffs"));
+        trimCoeffs.lookup("alphaC") >> trim_.alphaC;
+        trimCoeffs.lookup("A") >> trim_.A;
+        trimCoeffs.lookup("B") >> trim_.B;
+        trim_.alphaC = degToRad(trim_.alphaC);
+
+        checkData();
+
+        createCoordinateSystem();
+
+        constructGeometry();
+
+        if (debug)
+        {
+            writeField("alphag", alphag_, true);
+            writeField("faceArea", area_, true);
+        }
+
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+
+// ************************************************************************* //
diff --git a/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/rotorDiskSource.H b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/rotorDiskSource.H
new file mode 100644
index 00000000000..0d646b98996
--- /dev/null
+++ b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/rotorDiskSource.H
@@ -0,0 +1,249 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+Class
+    Foam::rotorDiskSource
+
+Description
+    Cell-zone based momemtum source
+
+    Source approximates the mean effects of rotor forces on a cylindrical
+    region within the domain
+
+SourceFiles
+    rotorDiskSource.C
+    rotorDiskSourceTemplates.C
+
+\*---------------------------------------------------------------------------*/
+
+#ifndef rotorDiskSource_H
+#define rotorDiskSource_H
+
+#include "basicSource.H"
+#include "cylindricalCS.H"
+#include "NamedEnum.H"
+#include "bladeModel.H"
+#include "profileModelList.H"
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+/*---------------------------------------------------------------------------*\
+                      Class rotorDiskSource Declaration
+\*---------------------------------------------------------------------------*/
+
+class rotorDiskSource
+:
+    public basicSource
+{
+public:
+
+    enum geometryModeType
+    {
+        gmAuto,
+        gmSpecified
+    };
+    static const NamedEnum<geometryModeType, 2> geometryModeTypeNames_;
+
+    enum inletFlowType
+    {
+        ifFixed,
+        ifSurfaceNormal,
+        ifLocal
+    };
+    static const NamedEnum<inletFlowType, 3> inletFlowTypeNames_;
+
+
+protected:
+
+    // Helper structures to encapsulate flap and trim data
+
+        struct flapData
+        {
+            scalar beta0;   // coning angle [deg]
+            scalar beta1;   // lateral flapping coeff
+            scalar beta2;   // longitudinal flapping coeff
+        };
+
+        struct trimData
+        {
+            scalar alphaC;  // collective pitch angle [deg]
+            scalar A;       // lateral cyclic coeff
+            scalar B;       // longitudinal cyclic coeff
+        };
+
+
+    // Protected data
+
+        //- Coefficients dictionary
+        dictionary coeffs_;
+
+        //- Name of density field
+        word rhoName_;
+
+        //- Rotational speed [rad/s]
+        scalar omega_;
+
+        //- Number of blades
+        label nBlades_;
+
+        //- Inlet flow type
+        inletFlowType inletFlow_;
+
+        //- Inlet velocity for specified iinflow
+        vector inletVelocity_;
+
+        //- Tip effect [0-1]
+        //  Ratio of blade radius beyond which lift=0
+        scalar tipEffect_;
+
+        //- Blade flap coefficients [rad/s]
+        flapData flap_;
+
+        //- Blad trim coefficients
+        trimData trim_;
+
+        //- Blade data
+        bladeModel blade_;
+
+        //- Profile data
+        profileModelList profiles_;
+
+        //- Cell centre positions in local rotor frame (Cartesian x, y, z)
+        List<point> x_;
+
+        //- Rotation tensor for flap angle
+        List<tensor> R_;
+
+        //- Geometric angle of attack [deg]
+        List<scalar> alphag_;
+
+        //- Area [m2]
+        List<scalar> area_;
+
+        //- Rotor co-ordinate system (r, theta, z)
+        cylindricalCS coordSys_;
+
+        //- Maximum radius
+        scalar rMax_;
+
+        
+    // Protected Member Functions
+
+        //- Check data
+        void checkData();
+
+        //- Set the face areas per cell, and optionally correct the rotor axis
+        void setFaceArea(vector& axis, const bool correct);
+
+        //- Create the co-ordinate system
+        void createCoordinateSystem();
+
+        //- Construct geometry
+        void constructGeometry();
+
+        //- Return the inlet flow field
+        tmp<vectorField> inflowVelocity(const volVectorField& U) const;
+
+        //- Calculate forces
+        template<class RhoType>
+        tmp<volVectorField> calculateForces
+        (
+            const RhoType& rho,
+            const vectorField& U,
+            const dimensionSet& dims
+        );
+
+        //- Helper function to write rotor values
+        template<class Type>
+        void writeField
+        (
+            const word& name,
+            const List<Type>& values,
+            const bool writeNow = false
+        ) const;
+
+
+public:
+
+    //- Runtime type information
+    TypeName("rotorDisk");
+
+
+    // Constructors
+
+        
+        //- Construct from components
+        rotorDiskSource
+        (
+            const word& name,
+            const word& modelType,
+            const dictionary& dict,
+            const fvMesh& mesh
+        );
+
+
+    //- Destructor
+    virtual ~rotorDiskSource();
+
+
+    // Member Functions
+
+
+        // Source term addition
+
+            //- Source term to fvMatrix<vector>
+            virtual void addSu(fvMatrix<vector>& UEqn);
+
+            //- Source term to fvMatrix<scalar>
+            virtual void addSu(fvMatrix<scalar>& UEqn);
+
+
+        // I-O
+
+            //- Write the source properties
+            virtual void writeData(Ostream&) const;
+
+            //- Read source dictionary
+            virtual bool read(const dictionary& dict);
+};
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+} // End namespace Foam
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#ifdef NoRepository
+    #include "rotorDiskSourceTemplates.C"
+#endif
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+#endif
+
+// ************************************************************************* //
+
diff --git a/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/rotorDiskSourceTemplates.C b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/rotorDiskSourceTemplates.C
new file mode 100644
index 00000000000..5bc97c703f9
--- /dev/null
+++ b/src/finiteVolume/cfdTools/general/fieldSources/basicSource/rotorDiskSource/rotorDiskSourceTemplates.C
@@ -0,0 +1,205 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
+   \\    /   O peration     |
+    \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
+     \\/     M anipulation  |
+-------------------------------------------------------------------------------
+License
+    This file is part of OpenFOAM.
+
+    OpenFOAM is free software: you can redistribute it and/or modify it
+    under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+    for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
+
+\*---------------------------------------------------------------------------*/
+
+#include "rotorDiskSource.H"
+#include "addToRunTimeSelectionTable.H"
+#include "mathematicalConstants.H"
+#include "unitConversion.H"
+
+using namespace Foam::constant;
+
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+template<class Type>
+void Foam::rotorDiskSource::writeField
+(
+    const word& name,
+    const List<Type>& values,
+    const bool writeNow
+) const
+{
+    typedef GeometricField<Type, fvPatchField, volMesh> fieldType;
+
+    if (mesh_.time().outputTime() || writeNow)
+    {
+        tmp<fieldType> tfld
+        (
+            new fieldType
+            (
+                IOobject
+                (
+                    name,
+                    mesh_.time().timeName(),
+                    mesh_,
+                    IOobject::NO_READ,
+                    IOobject::NO_WRITE
+                ),
+                mesh_,
+                dimensioned<Type>("zero", dimless, pTraits<Type>::zero)
+            )
+        );
+
+        Field<Type>& fld = tfld().internalField();
+
+        if (cells_.size() != values.size())
+        {
+            FatalErrorIn("") << "cells_.size() != values_.size()"
+                << abort(FatalError);
+        }
+
+        forAll(cells_, i)
+        {
+            const label cellI = cells_[i];
+            fld[cellI] = values[i];
+        }
+
+        tfld().write();
+    }
+}
+
+
+template<class RhoType>
+Foam::tmp<Foam::volVectorField> Foam::rotorDiskSource::calculateForces
+(
+    const RhoType& rho,
+    const vectorField& U,
+    const dimensionSet& dims
+)
+{
+    tmp<volVectorField> tForce
+    (
+        new volVectorField
+        (
+            IOobject
+            (
+                "rotorForce",
+                mesh_.time().timeName(),
+                mesh_,
+                IOobject::NO_READ,
+                IOobject::NO_WRITE
+            ),
+            mesh_,
+            dimensionedVector("zero", dims, vector::zero)
+        )
+    );
+
+    vectorField& force = tForce().internalField();
+    const scalarField& V = mesh_.V();
+
+
+    // logging info
+    scalar dragEff = 0.0;
+    scalar liftEff = 0.0;
+    scalar AOAmin = GREAT;
+    scalar AOAmax = -GREAT;
+
+    forAll(cells_, i)
+    {
+        if (area_[i] > ROOTVSMALL)
+        {
+            const label cellI = cells_[i];
+
+            const scalar radius = x_[i].x();
+
+            // apply correction due to flap in cartesian frame
+            vector Uc = R_[i] & U[cellI];
+
+            // velocity in local reference frame
+            Uc = coordSys_.localVector(Uc);
+
+            // set radial component of velocity to zero
+            Uc.x() = 0.0;
+
+            // remove blade linear velocity from blade normal component
+            Uc.y() -= radius*omega_;
+
+            // velocity magnitude
+            scalar magUc = mag(Uc);
+
+            // determine blade data for this radius
+            // i1 = index of upper bound data point in blade list
+            scalar twist = 0.0;
+            scalar chord = 0.0;
+            label i1 = -1;
+            label i2 = -1;
+            scalar invDr = 0.0;
+            blade_.interpolate(radius, twist, chord, i1, i2, invDr);
+
+            // effective angle of attack
+            scalar alphaEff = alphag_[i] + twist - atan(Uc.z()/Uc.y());
+            AOAmin = min(AOAmin, alphaEff);
+            AOAmax = max(AOAmax, alphaEff);
+
+            // determine profile data for this radius and angle of attack
+            const label profile1 = blade_.profileID()[i1];
+            const label profile2 = blade_.profileID()[i2];
+
+            scalar Cd1 = 0.0;
+            scalar Cl1 = 0.0;
+            profiles_[profile1].Cdl(alphaEff, Cd1, Cl1);
+
+            scalar Cd2 = 0.0;
+            scalar Cl2 = 0.0;
+            profiles_[profile2].Cdl(alphaEff, Cd2, Cl2);
+
+            scalar Cd = invDr*(Cd2 - Cd1) + Cd1;
+            scalar Cl = invDr*(Cl2 - Cl1) + Cl1;
+
+            // apply tip effect for blade lift
+            scalar tipFactor = 1.0;
+            if (radius/rMax_ > tipEffect_)
+            {
+                tipFactor = 0.0;
+            }
+
+            // calculate forces
+            scalar pDyn = 0.5*rho[cellI]*sqr(magUc);
+            scalar f = pDyn*chord*nBlades_*area_[i]/(mathematical::twoPi);
+            vector localForce = vector(0.0, f*Cd, tipFactor*f*Cl);
+
+            // accumulate forces
+            dragEff += localForce.y();
+            liftEff += localForce.z();
+
+            // convert force to global cartesian co-ordinate system
+            force[cellI] = coordSys_.globalVector(localForce);
+
+            force[cellI] /= V[cellI];
+        }
+    }
+
+
+    Info<< type() << " output:" << nl
+        << "    min/max(AOA)   = " << radToDeg(AOAmin) << ", "
+        << radToDeg(AOAmax) << nl
+        << "    Effective drag = " << dragEff << nl
+        << "    Effective lift = " << liftEff << endl;
+
+
+    return tForce;
+}
+
+
+// ************************************************************************* //
-- 
GitLab