timeVaryingMappedFixedValueFvPatchField: Added support for reading point and field data directly from another case

Description
    This boundary conditions interpolates the values from a set of supplied
    points in space and time.

    By default the data files should be provide in
    constant/boundaryData/\<patch name\>/ directory:
      - points             : pointField of locations
      - \<time\>/\<field\> : field of values at time \<time\>

    Alternatively the names and locations of the points and field files may be
    specified explicitly via the optional dictionary entries:
      - dataDir \<optional top-level directory of the points and field data>;
      - points \<optional path including name of points file relative to
                 dataDir\>;
      - sample \<optional name of the sub-directory in the time directories
                containing the fields\>;
    This is particularly useful when mapping data from another case for which
    the \c sample \c functionObject is used to obtain the patch field data for
    mapping.

For example to specify that the point and field data should be mapped from
<source case name> the patch boundary condition would be written

    <patch name>
    {
        type            timeVaryingMappedFixedValue;
        dataDir         "../<source case name>/postProcessing/sample";
        points          "0/<sample name>/faceCentres";
        sample          <sample name>;
    }

In the above the source case directory is referred to relative to the current
case but the file and directory names are expanded so that environment variables
may be used.
This commit is contained in:
Henry Weller
2017-11-01 15:16:06 +00:00
parent 04b562cd7a
commit f6a31132ec
2 changed files with 319 additions and 228 deletions

View File

@ -28,6 +28,238 @@ License
#include "AverageField.H"
#include "IFstream.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
template<class Type>
Foam::fileName
Foam::timeVaryingMappedFixedValueFvPatchField<Type>::findFieldFile
(
const word& timeName
) const
{
const fileName fieldFileName
(
dataDir_/timeName/sampleName_/fieldTableName_
);
const fileName typeFieldFileName
(
dataDir_/timeName/sampleName_
/pTraits<Type>::typeName + Field<Type>::typeName
/fieldTableName_
);
if (exists(fieldFileName))
{
return fieldFileName;
}
else if (exists(typeFieldFileName))
{
return typeFieldFileName;
}
else
{
FatalErrorInFunction
<< "Cannot find field file "
<< fieldFileName << " " << typeFieldFileName
<< exit(FatalError);
return fileName::null;
}
}
template<class Type>
void Foam::timeVaryingMappedFixedValueFvPatchField<Type>::checkTable()
{
// Initialise
if (mapperPtr_.empty())
{
// Reread values and interpolate
const fileName samplePointsFile(dataDir_/pointsName_);
pointField samplePoints((IFstream(samplePointsFile)()));
if (debug)
{
Info<< "timeVaryingMappedFixedValueFvPatchField :"
<< " Read " << samplePoints.size() << " sample points from "
<< samplePointsFile << endl;
}
// tbd: run-time selection
bool nearestOnly
(
!mapMethod_.empty()
&& mapMethod_ != "planarInterpolation"
);
// Allocate the interpolator
mapperPtr_.reset
(
new pointToPointPlanarInterpolation
(
samplePoints,
this->patch().patch().faceCentres(),
perturb_,
nearestOnly
)
);
// Read the times for which data is available
sampleTimes_ = Time::findTimes(dataDir_);
if (debug)
{
Info<< "timeVaryingMappedFixedValueFvPatchField : In directory "
<< dataDir_ << " found times "
<< pointToPointPlanarInterpolation::timeNames(sampleTimes_)
<< endl;
}
}
// Find current time in sampleTimes
label lo = -1;
label hi = -1;
bool foundTime = mapperPtr_().findTime
(
sampleTimes_,
startSampleTime_,
this->db().time().value(),
lo,
hi
);
if (!foundTime)
{
FatalErrorInFunction
<< "Cannot find starting sampling values for current time "
<< this->db().time().value() << nl
<< "Have sampling values for times "
<< pointToPointPlanarInterpolation::timeNames(sampleTimes_) << nl
<< "In directory " << dataDir_ << " of field " << fieldTableName_
<< exit(FatalError);
}
// Update sampled data fields.
if (lo != startSampleTime_)
{
startSampleTime_ = lo;
if (startSampleTime_ == endSampleTime_)
{
// No need to reread since are end values
if (debug)
{
Pout<< "checkTable : Setting startValues to (already read) "
<< dataDir_/sampleTimes_[startSampleTime_].name()
<< endl;
}
startSampledValues_ = endSampledValues_;
startAverage_ = endAverage_;
}
else
{
if (debug)
{
Pout<< "checkTable : Reading startValues from "
<< dataDir_/sampleTimes_[lo].name()
<< endl;
}
// Reread values and interpolate
const fileName valsFile
(
findFieldFile(sampleTimes_[startSampleTime_].name())
);
Field<Type> vals;
if (setAverage_)
{
AverageField<Type> avals((IFstream(valsFile)()));
vals = avals;
startAverage_ = avals.average();
}
else
{
IFstream(valsFile)() >> vals;
}
if (vals.size() != mapperPtr_().sourceSize())
{
FatalErrorInFunction
<< "Number of values (" << vals.size()
<< ") differs from the number of points ("
<< mapperPtr_().sourceSize()
<< ") in file " << valsFile << exit(FatalError);
}
startSampledValues_ = mapperPtr_().interpolate(vals);
}
}
if (hi != endSampleTime_)
{
endSampleTime_ = hi;
if (endSampleTime_ == -1)
{
// endTime no longer valid. Might as well clear endValues.
if (debug)
{
Pout<< "checkTable : Clearing endValues" << endl;
}
endSampledValues_.clear();
}
else
{
if (debug)
{
Pout<< "checkTable : Reading endValues from "
<< dataDir_/sampleTimes_[endSampleTime_].name()
<< endl;
}
// Reread values and interpolate
const fileName valsFile
(
findFieldFile(sampleTimes_[endSampleTime_].name())
);
Field<Type> vals;
if (setAverage_)
{
AverageField<Type> avals((IFstream(valsFile)()));
vals = avals;
endAverage_ = avals.average();
}
else
{
IFstream(valsFile)() >> vals;
}
if (vals.size() != mapperPtr_().sourceSize())
{
FatalErrorInFunction
<< "Number of values (" << vals.size()
<< ") differs from the number of points ("
<< mapperPtr_().sourceSize()
<< ") in file " << valsFile << exit(FatalError);
}
endSampledValues_ = mapperPtr_().interpolate(vals);
}
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template<class Type>
@ -40,6 +272,9 @@ timeVaryingMappedFixedValueFvPatchField
:
fixedValueFvPatchField<Type>(p, iF),
fieldTableName_(iF.name()),
dataDir_(this->db().time().constant()/"boundaryData"/this->patch().name()),
pointsName_("points"),
sampleName_(word::null),
setAverage_(false),
perturb_(0),
mapperPtr_(nullptr),
@ -64,7 +299,17 @@ timeVaryingMappedFixedValueFvPatchField
)
:
fixedValueFvPatchField<Type>(p, iF, dict, false),
fieldTableName_(iF.name()),
fieldTableName_(dict.lookupOrDefault("fieldTable", iF.name())),
dataDir_
(
dict.lookupOrDefault
(
"dataDir",
this->db().time().constant()/"boundaryData"/this->patch().name()
)
),
pointsName_(dict.lookupOrDefault<fileName>("points", "points")),
sampleName_(dict.lookupOrDefault("sample", word::null)),
setAverage_(dict.lookupOrDefault("setAverage", false)),
perturb_(dict.lookupOrDefault("perturb", 1e-5)),
mapMethod_
@ -85,6 +330,10 @@ timeVaryingMappedFixedValueFvPatchField
endAverage_(Zero),
offset_()
{
dataDir_.expand();
pointsName_.expand();
sampleName_.expand();
if (dict.found("offset"))
{
offset_ = Function1<Type>::New("offset", dict);
@ -103,8 +352,6 @@ timeVaryingMappedFixedValueFvPatchField
<< ", 'nearest'" << exit(FatalIOError);
}
dict.readIfPresent("fieldTable", fieldTableName_);
if (dict.found("value"))
{
fvPatchField<Type>::operator==(Field<Type>("value", dict, p.size()));
@ -132,6 +379,9 @@ timeVaryingMappedFixedValueFvPatchField
:
fixedValueFvPatchField<Type>(ptf, p, iF, mapper),
fieldTableName_(ptf.fieldTableName_),
dataDir_(ptf.dataDir_),
pointsName_(ptf.pointsName_),
sampleName_(ptf.sampleName_),
setAverage_(ptf.setAverage_),
perturb_(ptf.perturb_),
mapMethod_(ptf.mapMethod_),
@ -156,6 +406,9 @@ timeVaryingMappedFixedValueFvPatchField
:
fixedValueFvPatchField<Type>(ptf),
fieldTableName_(ptf.fieldTableName_),
dataDir_(ptf.dataDir_),
pointsName_(ptf.pointsName_),
sampleName_(ptf.sampleName_),
setAverage_(ptf.setAverage_),
perturb_(ptf.perturb_),
mapMethod_(ptf.mapMethod_),
@ -181,6 +434,9 @@ timeVaryingMappedFixedValueFvPatchField
:
fixedValueFvPatchField<Type>(ptf, iF),
fieldTableName_(ptf.fieldTableName_),
dataDir_(ptf.dataDir_),
pointsName_(ptf.pointsName_),
sampleName_(ptf.sampleName_),
setAverage_(ptf.setAverage_),
perturb_(ptf.perturb_),
mapMethod_(ptf.mapMethod_),
@ -239,222 +495,6 @@ void Foam::timeVaryingMappedFixedValueFvPatchField<Type>::rmap
}
template<class Type>
void Foam::timeVaryingMappedFixedValueFvPatchField<Type>::checkTable()
{
// Initialise
if (mapperPtr_.empty())
{
// Reread values and interpolate
fileName samplePointsFile
(
this->db().time().constant()
/"boundaryData"
/this->patch().name()
/"points"
);
pointField samplePoints((IFstream(samplePointsFile)()));
if (debug)
{
Info<< "timeVaryingMappedFixedValueFvPatchField :"
<< " Read " << samplePoints.size() << " sample points from "
<< samplePointsFile << endl;
}
// tbd: run-time selection
bool nearestOnly =
(
!mapMethod_.empty()
&& mapMethod_ != "planarInterpolation"
);
// Allocate the interpolator
mapperPtr_.reset
(
new pointToPointPlanarInterpolation
(
samplePoints,
this->patch().patch().faceCentres(),
perturb_,
nearestOnly
)
);
// Read the times for which data is available
const fileName samplePointsDir = samplePointsFile.path();
sampleTimes_ = Time::findTimes(samplePointsDir);
if (debug)
{
Info<< "timeVaryingMappedFixedValueFvPatchField : In directory "
<< samplePointsDir << " found times "
<< pointToPointPlanarInterpolation::timeNames(sampleTimes_)
<< endl;
}
}
// Find current time in sampleTimes
label lo = -1;
label hi = -1;
bool foundTime = mapperPtr_().findTime
(
sampleTimes_,
startSampleTime_,
this->db().time().value(),
lo,
hi
);
if (!foundTime)
{
FatalErrorInFunction
<< "Cannot find starting sampling values for current time "
<< this->db().time().value() << nl
<< "Have sampling values for times "
<< pointToPointPlanarInterpolation::timeNames(sampleTimes_) << nl
<< "In directory "
<< this->db().time().constant()/"boundaryData"/this->patch().name()
<< "\n on patch " << this->patch().name()
<< " of field " << fieldTableName_
<< exit(FatalError);
}
// Update sampled data fields.
if (lo != startSampleTime_)
{
startSampleTime_ = lo;
if (startSampleTime_ == endSampleTime_)
{
// No need to reread since are end values
if (debug)
{
Pout<< "checkTable : Setting startValues to (already read) "
<< "boundaryData"
/this->patch().name()
/sampleTimes_[startSampleTime_].name()
<< endl;
}
startSampledValues_ = endSampledValues_;
startAverage_ = endAverage_;
}
else
{
if (debug)
{
Pout<< "checkTable : Reading startValues from "
<< "boundaryData"
/this->patch().name()
/sampleTimes_[lo].name()
<< endl;
}
// Reread values and interpolate
fileName valsFile
(
this->db().time().constant()
/"boundaryData"
/this->patch().name()
/sampleTimes_[startSampleTime_].name()
/fieldTableName_
);
Field<Type> vals;
if (setAverage_)
{
AverageField<Type> avals((IFstream(valsFile)()));
vals = avals;
startAverage_ = avals.average();
}
else
{
IFstream(valsFile)() >> vals;
}
if (vals.size() != mapperPtr_().sourceSize())
{
FatalErrorInFunction
<< "Number of values (" << vals.size()
<< ") differs from the number of points ("
<< mapperPtr_().sourceSize()
<< ") in file " << valsFile << exit(FatalError);
}
startSampledValues_ = mapperPtr_().interpolate(vals);
}
}
if (hi != endSampleTime_)
{
endSampleTime_ = hi;
if (endSampleTime_ == -1)
{
// endTime no longer valid. Might as well clear endValues.
if (debug)
{
Pout<< "checkTable : Clearing endValues" << endl;
}
endSampledValues_.clear();
}
else
{
if (debug)
{
Pout<< "checkTable : Reading endValues from "
<< "boundaryData"
/this->patch().name()
/sampleTimes_[endSampleTime_].name()
<< endl;
}
// Reread values and interpolate
fileName valsFile
(
this->db().time().constant()
/"boundaryData"
/this->patch().name()
/sampleTimes_[endSampleTime_].name()
/fieldTableName_
);
Field<Type> vals;
if (setAverage_)
{
AverageField<Type> avals((IFstream(valsFile)()));
vals = avals;
endAverage_ = avals.average();
}
else
{
IFstream(valsFile)() >> vals;
}
if (vals.size() != mapperPtr_().sourceSize())
{
FatalErrorInFunction
<< "Number of values (" << vals.size()
<< ") differs from the number of points ("
<< mapperPtr_().sourceSize()
<< ") in file " << valsFile << exit(FatalError);
}
endSampledValues_ = mapperPtr_().interpolate(vals);
}
}
}
template<class Type>
void Foam::timeVaryingMappedFixedValueFvPatchField<Type>::updateCoeffs()
{
@ -571,6 +611,18 @@ void Foam::timeVaryingMappedFixedValueFvPatchField<Type>::write
{
fvPatchField<Type>::write(os);
this->writeEntryIfDifferent
(
os,
"dataDir",
this->db().time().constant()/"boundaryData"/this->patch().name(),
dataDir_
);
this->writeEntryIfDifferent(os, "points", fileName("points"), pointsName_);
this->writeEntryIfDifferent(os, "sample", fileName::null, sampleName_);
this->writeEntryIfDifferent(os, "setAverage", Switch(false), setAverage_);
this->writeEntryIfDifferent(os, "perturb", scalar(1e-5), perturb_);

View File

@ -2,7 +2,7 @@
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\ / A nd | Copyright (C) 2011-2017 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
License
@ -31,10 +31,22 @@ Description
This boundary conditions interpolates the values from a set of supplied
points in space and time.
Supplied data should be specified in constant/boundaryData/\<patchname\>/
By default the data files should be provide in
constant/boundaryData/\<patch name\>/ directory:
- points : pointField of locations
- \<time\>/\<field\> : field of values at time \<time\>
Alternatively the names and locations of the points and field files may be
specified explicitly via the optional dictionary entries:
- dataDir \<optional top-level directory of the points and field data>;
- points \<optional path including name of points file relative to
dataDir\>;
- sample \<optional name of the sub-directory in the time directories
containing the fields\>;
This is particularly useful when mapping data from another case for which
the \c sample \c functionObject is used to obtain the patch field data for
mapping.
The default mode of operation (mapMethod planarInterpolation) is to project
the points onto a plane (constructed from the first threee points) and
construct a 2D triangulation and finds for the face centres the triangle it
@ -53,15 +65,31 @@ Usage
fieldTableName | Alternative field name to sample | no| this field name
mapMethod | Type of mapping | no | planarInterpolation
offset | Offset to mapped values | no | Zero
dataDir | Top-level directory of the points and field data \\
| no | constant/boundaryData/\<patch name\>
points | Path including name of points file relative to dataDir \\
| no | points
sample | Name of the sub-directory in the time directories \\
containing the fields | no | ""
\endtable
\verbatim
<patchName>
<patch name>
{
type timeVaryingMappedFixedValue;
}
\endverbatim
\verbatim
<patch name>
{
type timeVaryingMappedFixedValue;
dataDir "../<source case name>/postProcessing/sample";
points "0/<sample name>/faceCentres";
sample <sample name>;
}
\endverbatim
See also
Foam::fixedValueFvPatchField
Foam::Function1Types
@ -99,6 +127,17 @@ class timeVaryingMappedFixedValueFvPatchField
//- Name of the field data table, defaults to the name of the field
word fieldTableName_;
//- Optional name of the data directory,
// defaults to "constant/boundaryData/<patch name>"
fileName dataDir_;
//- Optional name of the points file, defaults to "points"
fileName pointsName_;
//- Optional name of the sample sub-directory from which the field are
// read, defaults to ""
fileName sampleName_;
//- If true adjust the mapped field to maintain average value
Switch setAverage_;
@ -135,6 +174,12 @@ class timeVaryingMappedFixedValueFvPatchField
//- Time varying offset values to interpolated data
autoPtr<Function1<Type>> offset_;
//- Helper function to find the field files
fileName findFieldFile(const word& timeName) const;
//- Find boundary data inbetween current time and interpolate
void checkTable();
public:
@ -231,12 +276,6 @@ public:
);
// Utility functions
//- Find boundary data inbetween current time and interpolate
void checkTable();
// Evaluation functions
//- Update the coefficients associated with the patch field