/*---------------------------------------------------------------------------*\
  =========                 |
  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
   \\    /   O peration     |
    \\  /    A nd           | Copyright (C) 1991-2007 OpenCFD Ltd.
     \\/     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/>.

Application
    applyWallFunctionBounaryConditions

Description
    Updates OpenFOAM RAS cases to use the new (v1.6) wall function framework

    Attempts to determine whether case is compressible or incompressible, or
    can be supplied with -compressible command line argument

Usage
    - applyWallFunctionBoundaryConditions [OPTION]

    @param -compressible
    Operate in compressible mode.

    @param -noZero \n
    Ignore timestep 0.

    @param -constant \n
    Include the constant directory.

    @param -time \<time\>\n
    Apply only to specific time.

    @param -latestTime \n
    Only apply to latest time step.

    @param -case \<dir\> \n
    Specify the case directory

    @param -parallel \n
    Run the case in parallel

    @param -help \n
    Display short usage message

    @param -doc \n
    Display Doxygen documentation page

    @param -srcDoc \n
    Display source code

\*---------------------------------------------------------------------------*/

#include <OpenFOAM/argList.H>
#include <finiteVolume/fvMesh.H>
#include <OpenFOAM/Time.H>
#include <finiteVolume/volFields.H>
#include <finiteVolume/surfaceFields.H>

#include <OpenFOAM/wallPolyPatch.H>

#include <incompressibleRASModels/epsilonWallFunctionFvPatchScalarField.H>
#include <incompressibleRASModels/kqRWallFunctionFvPatchField.H>
#include <incompressibleRASModels/nutWallFunctionFvPatchScalarField.H>
#include <incompressibleRASModels/omegaWallFunctionFvPatchScalarField.H>

#include <compressibleRASModels/epsilonWallFunctionFvPatchScalarField.H>
#include <compressibleRASModels/kqRWallFunctionFvPatchField.H>
#include <compressibleRASModels/mutWallFunctionFvPatchScalarField.H>
#include <compressibleRASModels/omegaWallFunctionFvPatchScalarField.H>

using namespace Foam;

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

bool caseIsCompressible(const fvMesh& mesh)
{
    // Attempt flux field
    IOobject phiHeader
    (
        "phi",
        mesh.time().timeName(),
        mesh,
        IOobject::MUST_READ,
        IOobject::NO_WRITE
    );

    if (phiHeader.headerOk())
    {
        surfaceScalarField phi(phiHeader, mesh);
        if (phi.dimensions() == dimDensity*dimVelocity*dimArea)
        {
            return true;
        }
    }

    // Attempt density field
    IOobject rhoHeader
    (
        "rho",
        mesh.time().timeName(),
        mesh,
        IOobject::MUST_READ,
        IOobject::NO_WRITE
    );

    if (rhoHeader.headerOk())
    {
        volScalarField rho(rhoHeader, mesh);
        if (rho.dimensions() == dimDensity)
        {
            return true;
        }
    }

    // Attempt pressure field
    IOobject pHeader
    (
        "p",
        mesh.time().timeName(),
        mesh,
        IOobject::MUST_READ,
        IOobject::NO_WRITE
    );

    if (pHeader.headerOk())
    {
        volScalarField p(pHeader, mesh);
        if (p.dimensions() == dimMass/sqr(dimTime)/dimLength)
        {
            return true;
        }
    }

    // If none of the above are true, assume that the case is incompressible
    return false;
}


void createVolScalarField
(
    const fvMesh& mesh,
    const word& fieldName,
    const dimensionSet& dims
)
{
    IOobject fieldHeader
    (
        fieldName,
        mesh.time().timeName(),
        mesh,
        IOobject::MUST_READ,
        IOobject::NO_WRITE
    );

    if (!fieldHeader.headerOk())
    {
        Info<< "Creating field " << fieldName << nl << endl;

        volScalarField field
        (
            IOobject
            (
                fieldName,
                mesh.time().timeName(),
                mesh,
                IOobject::NO_READ,
                IOobject::NO_WRITE
            ),
            mesh,
            dimensionedScalar("zero", dims, 0.0)
        );

        field.write();
    }
}


void replaceBoundaryType
(
    const fvMesh& mesh,
    const word& fieldName,
    const word& boundaryType,
    const string& boundaryValue
)
{
    IOobject header
    (
        fieldName,
        mesh.time().timeName(),
        mesh,
        IOobject::MUST_READ,
        IOobject::NO_WRITE
    );

    if (!header.headerOk())
    {
        return;
    }

    Info<< "Updating boundary types for field " << header.name() << endl;

    const word oldTypeName = IOdictionary::typeName;
    const_cast<word&>(IOdictionary::typeName) = word::null;

    IOdictionary dict(header);

    const_cast<word&>(IOdictionary::typeName) = oldTypeName;
    const_cast<word&>(dict.type()) = dict.headerClassName();

    // Make a backup of the old field
    word backupName(dict.name() + ".old");
    Info<< "    copying " << dict.name() << " to "
        << backupName << endl;
    IOdictionary dictOld = dict;
    dictOld.rename(backupName);
    dictOld.regIOobject::write();

    // Loop through boundary patches and update
    const polyBoundaryMesh& bMesh = mesh.boundaryMesh();
    dictionary& boundaryDict = dict.subDict("boundaryField");
    forAll(bMesh, patchI)
    {
        if (isA<wallPolyPatch>(bMesh[patchI]))
        {
            word patchName = bMesh[patchI].name();
            dictionary& oldPatch = boundaryDict.subDict(patchName);

            dictionary newPatch(dictionary::null);
            newPatch.add("type", boundaryType);
            newPatch.add("value", ("uniform " + boundaryValue).c_str());

            oldPatch = newPatch;
        }
    }

    Info<< "    writing updated " << dict.name() << nl << endl;
    dict.regIOobject::write();
}


void updateCompressibleCase(const fvMesh& mesh)
{
    Info<< "Case treated as compressible" << nl << endl;
    createVolScalarField
    (
        mesh,
        "mut",
        dimArea/dimTime*dimDensity
    );
    replaceBoundaryType
    (
        mesh,
        "mut",
        compressible::RASModels::mutWallFunctionFvPatchScalarField::typeName,
        "0"
    );
    replaceBoundaryType
    (
        mesh,
        "epsilon",
        compressible::RASModels::epsilonWallFunctionFvPatchScalarField::
            typeName,
        "0"
    );
    replaceBoundaryType
    (
        mesh,
        "omega",
        compressible::RASModels::omegaWallFunctionFvPatchScalarField::typeName,
        "0"
    );
    replaceBoundaryType
    (
        mesh,
        "k",
        compressible::RASModels::kqRWallFunctionFvPatchField<scalar>::typeName,
        "0"
    );
    replaceBoundaryType
    (
        mesh,
        "q",
        compressible::RASModels::kqRWallFunctionFvPatchField<scalar>::typeName,
        "0"
    );
    replaceBoundaryType
    (
        mesh,
        "R",
        compressible::RASModels::kqRWallFunctionFvPatchField<symmTensor>::
            typeName,
        "(0 0 0 0 0 0)"
    );
}


void updateIncompressibleCase(const fvMesh& mesh)
{
    Info<< "Case treated as incompressible" << nl << endl;
    createVolScalarField(mesh, "nut", dimArea/dimTime);

    replaceBoundaryType
    (
        mesh,
        "nut",
        incompressible::RASModels::nutWallFunctionFvPatchScalarField::typeName,
        "0"
    );
    replaceBoundaryType
    (
        mesh,
        "epsilon",
        incompressible::RASModels::epsilonWallFunctionFvPatchScalarField::
            typeName,
        "0"
    );
    replaceBoundaryType
    (
        mesh,
        "omega",
        incompressible::RASModels::omegaWallFunctionFvPatchScalarField::
            typeName,
        "0"
    );
    replaceBoundaryType
    (
        mesh,
        "k",
        incompressible::RASModels::kqRWallFunctionFvPatchField<scalar>::
            typeName,
        "0"
    );
    replaceBoundaryType
    (
        mesh,
        "q",
        incompressible::RASModels::kqRWallFunctionFvPatchField<scalar>::
            typeName,
        "0"
    );
    replaceBoundaryType
    (
        mesh,
        "R",
        incompressible::RASModels::kqRWallFunctionFvPatchField<symmTensor>::
            typeName,
        "(0 0 0 0 0 0)"
    );
}


int main(int argc, char *argv[])
{
    #include <OpenFOAM/addTimeOptions.H>
    argList::validOptions.insert("compressible", "");

    #include <OpenFOAM/setRootCase.H>
    #include <OpenFOAM/createTime.H>
    #include <OpenFOAM/createMesh.H>

    bool compressible = args.optionFound("compressible");

    Info<< "Updating turbulence fields to operate using new run time "
        << "selectable" << nl << "wall functions"
        << nl << endl;

    if (compressible || caseIsCompressible(mesh))
    {
        updateCompressibleCase(mesh);
    }
    else
    {
        updateIncompressibleCase(mesh);
    }

    Info<< "End\n" << endl;

    return 0;
}


// ************************ vim: set sw=4 sts=4 et: ************************ //
