View Issue Details

IDProjectCategoryView StatusLast Update
0003158OpenFOAMBugpublic2019-02-04 09:43
Reporterfede Assigned Towill  
PrioritynormalSeverityminorReproducibilityalways
Status resolvedResolutionfixed 
PlatformLinuxOSDebian OS VersionStretch
Product Versiondev 
Fixed in Versiondev 
Summary0003158: codeOptions entry no longer supported by #codeStream
DescriptionDear Henry,
after the recent updates to the implementation of dynamicCode, the codeOptions entry is no longer read in the codeStream functions. This feature was supported in the previous versions of the code (OpenFOAM-6 for instance). This causes that some coded functions working with previous (but still recent) versions of OpenFOAM.
 
The reason is that in the newest implementation of codeStream (available only in OpenFOAM-dev) some lines are missing. Everything works in OpenFOAM-6.
Please find attached the files with the patch to revert the missing functionality.

Wish this helps.
Have a nice week end,

/Federico

Steps To ReproduceTry to include a "codeOption" entry in a codedFunction and a file "fileInSystemFolder.H" in the path specified in the codeOptions. The code will not work (of course "ileInSystemFolder.H" must be located in a folder which is not included in the PATH, for instance $FOAM_CASE/system).

Example:

<fileEntry> ( #codeStream
{
    ....

    //! Optional:
    codeOptions
    #{
    -I${FOAM_CASE}/system
    #};
    
     code
    #{
          #include "fileInSystemFolder.H"
     #}
    ...
);
Additional InformationTesting was done in OpenFOAM-dev, commit 5afec3aae0a06adc5e1af0a23f7afad94e5d4b60.
TagsNo tags attached.

Activities

fede

2019-02-01 18:26

reporter  

codeStream.C (12,277 bytes)   
/*---------------------------------------------------------------------------*\
  =========                 |
  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
   \\    /   O peration     | Website:  https://openfoam.org
    \\  /    A nd           | Copyright (C) 2011-2019 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 "codeStream.H"
#include "addToMemberFunctionSelectionTable.H"
#include "IStringStream.H"
#include "OStringStream.H"
#include "dynamicCode.H"
#include "dynamicCodeContext.H"
#include "Time.H"

// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //

namespace Foam
{
namespace functionEntries
{
    defineTypeNameAndDebug(codeStream, 0);

    addToMemberFunctionSelectionTable
    (
        functionEntry,
        codeStream,
        execute,
        dictionaryIstream
    );

    addToMemberFunctionSelectionTable
    (
        functionEntry,
        codeStream,
        execute,
        primitiveEntryIstream
    );

}
}


const Foam::word Foam::functionEntries::codeStream::codeTemplateC =
    "codeStreamTemplate.C";


// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //

Foam::dlLibraryTable& Foam::functionEntries::codeStream::libs
(
    const dictionary& dict
)
{
    const baseIOdictionary& d = static_cast<const baseIOdictionary&>
    (
        dict.topDict()
    );
    return const_cast<Time&>(d.time()).libs();
}


bool Foam::functionEntries::codeStream::doingMasterOnlyReading
(
    const dictionary& dict
)
{
    const dictionary& topDict = dict.topDict();

    if (isA<baseIOdictionary>(topDict))
    {
        const baseIOdictionary& d = static_cast<const baseIOdictionary&>
        (
            topDict
        );

        if (debug)
        {
            Pout<< "codeStream : baseIOdictionary:" << dict.name()
                << " master-only-reading:" << d.globalObject()
                << endl;
        }

        return d.globalObject();
    }
    else
    {
        if (debug)
        {
            Pout<< "codeStream : not a baseIOdictionary:" << dict.name()
                << " master-only-reading:" << regIOobject::masterOnlyReading
                << endl;
        }

        // Fall back to regIOobject::masterOnlyReading
        return regIOobject::masterOnlyReading;
    }
}


Foam::functionEntries::codeStream::streamingFunctionType
Foam::functionEntries::codeStream::getFunction
(
    const dictionary& parentDict,
    const dictionary& codeDict
)
{
    // get code, codeInclude, ...
    dynamicCodeContext context(codeDict, {"code", "codeInclude", "codeOptions", "localCode"});

    // codeName: codeStream + _<sha1>
    // codeDir : _<sha1>
    std::string sha1Str(context.sha1().str(true));
    dynamicCode dynCode("codeStream" + sha1Str, sha1Str);

    // Load library if not already loaded
    // Version information is encoded in the libPath (encoded with the SHA1)
    const fileName libPath = dynCode.libPath();

    // see if library is loaded
    void* lib = nullptr;

    const dictionary& topDict = parentDict.topDict();
    if (isA<baseIOdictionary>(topDict))
    {
        lib = libs(parentDict).findLibrary(libPath);
    }

    if (!lib)
    {
        Info<< "Using #codeStream with " << libPath << endl;
    }


    // nothing loaded
    // avoid compilation if possible by loading an existing library
    if (!lib)
    {
        if (isA<baseIOdictionary>(topDict))
        {
            // Cached access to dl libs. Guarantees clean up upon destruction
            // of Time.
            dlLibraryTable& dlLibs = libs(parentDict);
            if (dlLibs.open(libPath, false))
            {
                lib = dlLibs.findLibrary(libPath);
            }
        }
        else
        {
            // Uncached opening of libPath. Do not complain if cannot be loaded
            lib = dlOpen(libPath, false);
        }
    }


    // create library if required
    if (!lib)
    {
        bool create =
            Pstream::master()
         || (regIOobject::fileModificationSkew <= 0);   // not NFS

        if (create)
        {
            if (!dynCode.upToDate(context))
            {
                // filter with this context
                dynCode.reset(context);

                // compile filtered C template
                dynCode.addCompileFile(codeTemplateC);

                // define Make/options
                dynCode.setMakeOptions
                (
                    "EXE_INC = -g \\\n"
                  + context.options()
                  + "\n\nLIB_LIBS = \\\n"
                  + "    -lOpenFOAM \\\n"
                  + context.libs()
                );

                if (!dynCode.copyOrCreateFiles(true))
                {
                    FatalIOErrorInFunction
                    (
                        parentDict
                    )   << "Failed writing files for" << nl
                        << dynCode.libRelPath() << nl
                        << exit(FatalIOError);
                }
            }

            if (!dynCode.wmakeLibso())
            {
                FatalIOErrorInFunction
                (
                    parentDict
                )   << "Failed wmake " << dynCode.libRelPath() << nl
                    << exit(FatalIOError);
            }
        }

        //- Only block if we're not doing master-only reading. (flag set by
        //  regIOobject::read, baseIOdictionary constructor)
        if
        (
           !doingMasterOnlyReading(topDict)
         && regIOobject::fileModificationSkew > 0
        )
        {
            //- Since the library has only been compiled on the master the
            //  other nodes need to pick this library up through NFS
            //  We do this by just polling a few times using the
            //  fileModificationSkew.

            off_t mySize = Foam::fileSize(libPath);
            off_t masterSize = mySize;
            Pstream::scatter(masterSize);

            if (debug)
            {
                Pout<< endl<< "on processor " << Pstream::myProcNo()
                    << " have masterSize:" << masterSize
                    << " and localSize:" << mySize
                    << endl;
            }


            if (mySize < masterSize)
            {
                if (debug)
                {
                    Pout<< "Local file " << libPath
                        << " not of same size (" << mySize
                        << ") as master ("
                        << masterSize << "). Waiting for "
                        << regIOobject::fileModificationSkew
                        << " seconds." << endl;
                }
                Foam::sleep(regIOobject::fileModificationSkew);

                // Recheck local size
                mySize = Foam::fileSize(libPath);

                if (mySize < masterSize)
                {
                    FatalIOErrorInFunction
                    (
                        parentDict
                    )   << "Cannot read (NFS mounted) library " << nl
                        << libPath << nl
                        << "on processor " << Pstream::myProcNo()
                        << " detected size " << mySize
                        << " whereas master size is " << masterSize
                        << " bytes." << nl
                        << "If your case is not NFS mounted"
                        << " (so distributed) set fileModificationSkew"
                        << " to 0"
                        << exit(FatalIOError);
                }
            }

            if (debug)
            {
                Pout<< endl<< "on processor " << Pstream::myProcNo()
                    << " after waiting: have masterSize:" << masterSize
                    << " and localSize:" << mySize
                    << endl;
            }
        }

        if (isA<baseIOdictionary>(topDict))
        {
            // Cached access to dl libs. Guarantees clean up upon destruction
            // of Time.
            dlLibraryTable& dlLibs = libs(parentDict);

            if (debug)
            {
                Pout<< "Opening cached dictionary:" << libPath << endl;
            }

            if (!dlLibs.open(libPath, false))
            {
                FatalIOErrorInFunction
                (
                    parentDict
                )   << "Failed loading library " << libPath << nl
                    << "Did you add all libraries to the 'libs' entry"
                    << " in system/controlDict?"
                    << exit(FatalIOError);
            }

            lib = dlLibs.findLibrary(libPath);
        }
        else
        {
            // Uncached opening of libPath
            if (debug)
            {
                Pout<< "Opening uncached dictionary:" << libPath << endl;
            }
            lib = dlOpen(libPath, true);
        }
    }

    bool haveLib = lib;
    if (!doingMasterOnlyReading(topDict))
    {
        reduce(haveLib, andOp<bool>());
    }

    if (!haveLib)
    {
        FatalIOErrorInFunction
        (
            parentDict
        )   << "Failed loading library " << libPath
            << " on some processors."
            << exit(FatalIOError);
    }


    // Find the function handle in the library
    streamingFunctionType function =
        reinterpret_cast<streamingFunctionType>
        (
            dlSym(lib, dynCode.codeName())
        );


    if (!function)
    {
        FatalIOErrorInFunction
        (
            parentDict
        )   << "Failed looking up symbol " << dynCode.codeName()
            << " in library " << lib << exit(FatalIOError);
    }

    return function;
}


// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //

bool Foam::functionEntries::codeStream::execute
(
    const dictionary& parentDict,
    primitiveEntry& entry,
    Istream& is
)
{
    Info<< "Using #codeStream at line " << is.lineNumber()
        << " in file " <<  parentDict.name() << endl;

    dynamicCode::checkSecurity
    (
        "functionEntries::codeStream::execute(..)",
        parentDict
    );

    // get code dictionary
    // must reference parent for stringOps::expand to work nicely
    dictionary codeDict("#codeStream", parentDict, is);

    streamingFunctionType function = getFunction(parentDict, codeDict);

    // use function to write stream
    OStringStream os(is.format());
    (*function)(os, parentDict);

    // get the entry from this stream
    IStringStream resultStream(os.str());
    entry.read(parentDict, resultStream);

    return true;
}


bool Foam::functionEntries::codeStream::execute
(
    dictionary& parentDict,
    Istream& is
)
{
    Info<< "Using #codeStream at line " << is.lineNumber()
        << " in file " <<  parentDict.name() << endl;

    dynamicCode::checkSecurity
    (
        "functionEntries::codeStream::execute(..)",
        parentDict
    );

    // get code dictionary
    // must reference parent for stringOps::expand to work nicely
    dictionary codeDict("#codeStream", parentDict, is);

    streamingFunctionType function = getFunction(parentDict, codeDict);

    // use function to write stream
    OStringStream os(is.format());
    (*function)(os, parentDict);

    // get the entry from this stream
    IStringStream resultStream(os.str());
    parentDict.read(resultStream);

    return true;
}


// ************************************************************************* //
codeStream.C (12,277 bytes)   
dynamicCodeContext.C (3,390 bytes)   
/*---------------------------------------------------------------------------*\
  =========                 |
  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
   \\    /   O peration     | Website:  https://openfoam.org
    \\  /    A nd           | Copyright (C) 2011-2019 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 "dynamicCodeContext.H"
#include "stringOps.H"
#include "OSHA1stream.H"

// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //

void Foam::dynamicCodeContext::addLineDirective
(
    string& code,
    const label lineNum,
    const fileName& name
)
{
    code = "#line " + Foam::name(lineNum + 1) + " \"" + name + "\"\n" + code;
}


// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //

Foam::dynamicCodeContext::dynamicCodeContext
(
    const dictionary& dict,
    const wordList& codeKeys
)
:
    dict_(dict),
    code_(),
    options_(),
    libs_()
{
    // Expand all dictionary entries. Note that this removes any leading or
    // trailing whitespace, which is necessary for compilation options, and
    // doesn't hurt for everything else
    List<const entry*> codePtrs(codeKeys.size(), nullptr);
    forAll(codeKeys, i)
    {
        const word& key = codeKeys[i];
        codePtrs[i] = dict.lookupEntryPtr(key, false, false);
        if (codePtrs[i])
        {
            code_.insert
            (
                key,
                stringOps::expand
                (
                    stringOps::trim(codePtrs[i]->stream()),
                    dict
                )
            );
        }
        else
        {
            code_.insert(key, "");
        }
    }

    // Calculate SHA1 digest from all entries
    OSHA1stream os;
    forAllConstIter(HashTable<string>, code_, iter)
    {
        os << iter();
    }
    sha1_ = os.digest();

    // Add line directive after calculating SHA1 since this includes
    // "processor..." in the path which differs between cores
    forAll(codeKeys, i)
    {
        if (codePtrs[i])
        {
            const word& key = codeKeys[i];
            if (key != "codeOptions")
            {
                addLineDirective
                (
                    code_[key],
                    codePtrs[i]->startLineNumber(),
                    dict.name()
                );

            }
            else
            {
                options_ = code_[key];
            }
        }
    }
}


// ************************************************************************* //
dynamicCodeContext.C (3,390 bytes)   

fede

2019-02-01 18:28

reporter   ~0010277

Apologies with my English.
"This causes that some coded functions working with previous (but still recent) versions of OpenFOAM."

means

"This causes that some coded functions working with previous (but still recent) versions of OpenFOAM do not work with the latest commits of OpenFOAM-dev."

will

2019-02-04 09:43

manager   ~0010278

Thanks you for the report.

I haven't applied your changes. They do not resolve the problem in the way that the design intends. "codeOptions" was not intended to be part of the list of code keys, as it is not actually C++ code and therefore has to be handled a little differently. Similarly, "codeLibs" was also missing, but should not be one of the code keys for the same reason.

What I forgot to add in my changes last week was just the reading and setting of the options and libs data in the dynamicCodeContext constructor. I have now done this as part of commit 528dccc0 in dev. It should now work as it did before.

You did not provide a test case, so I cannot be 100% sure that this fixes your problem. I actually did my testing with codedFunctionObject. If your problem persists, please open another report and attach an example which reproduces the failure.

Issue History

Date Modified Username Field Change
2019-02-01 18:26 fede New Issue
2019-02-01 18:26 fede File Added: codeStream.C
2019-02-01 18:26 fede File Added: dynamicCodeContext.C
2019-02-01 18:28 fede Note Added: 0010277
2019-02-04 09:31 will View Status private => public
2019-02-04 09:32 will Note View State: 0010277: public
2019-02-04 09:43 will Assigned To => will
2019-02-04 09:43 will Status new => resolved
2019-02-04 09:43 will Resolution open => fixed
2019-02-04 09:43 will Fixed in Version => dev
2019-02-04 09:43 will Note Added: 0010278